如何使用 OCMock 部分模拟遗留代码中的对象?
How to partially mock an object inside legacy code with OCMock?
我想完成here, i.e create mocks inside legacy code. However I require partial instead of nice or strict模拟所描述的内容。
例如,考虑行为与 GKLeaderbaord
完全相同的排行榜,除了实施 loadScoresWithCompletionHandler:
.
的存根版本
我已经在 XCTestCase
中尝试过此代码,但它目前在指定行的运行时失败:OCMInvocationMatcher
引发 EXC_BAD_ACCESS
错误。也许有一些无限递归正在进行。
id leaderboardMock = OCMClassMock(GKLeaderboard.class);
OCMStub([leaderboardMock alloc])
.andReturn(OCMPartialMock([GKLeaderboard alloc]));
OCMStub([leaderboardMock loadScoresWithCompletionHandler: [OCMArg any]])
.andDo(^(NSInvocation *invocation) { /* ... */ });
// these parts normally nested inside legacy code
GKLeaderboard *leaderboard = /* raises EXC_BAD_ACCESS */
[[GKLeaderboard alloc] initWithPlayers: @[ GKLocalPlayer.localPlayer ]];
leaderboard.identifier = @"Test";
[leaderboard loadScoresWithCompletionHandler: nil /* ... */ ];
我做错了什么,这甚至可以用于部分模型吗?
UPDATE 我现在可以看到指示的行可能(很明显)如何导致无限递归,但还不知道如何避免(或中断)它。
UPDATE 我也没有成功尝试引入专用 class 和 OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc])
(也没有 OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])
).也许 OCMock
在 init
(documentation 说:"it is not possible to stub the init
method, because that is implemented by the mock itself")的水平上发挥了它的魔力,因此这种尝试达到了 alloc
(或 [=22)的水平=]) 无法达到预期的效果。
不确定我是否遵循了您的意图。这似乎是一个误会。以下内容对您不起作用吗?
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
您可以无限制地使用普通对象。您可以使用为对象创建的部分模拟来操作 leaderboard
中的实际实例。这就是部分模拟的美妙之处。
更新:如果对象创建不在你的控制之下,你可以尝试以下方法:
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboardMock alloc]).andReturn(leaderboardMock);
OCMStub([leaderboardMock initWithPlayers:[OCMArg any]).andReturn(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
我现在得出结论,method swizzling 是一个可能的选择。
替换方法可以是,例如从遗留代码的上下文中生成部分模型,因此在该上下文中引入部分模型,而无需更改遗留 API。
你不应该使用下面的行,它会模拟你的整个 class 和真实对象的 none 将被调用。
OCMClassMock(GKLeaderboard.class)
我想完成here, i.e create mocks inside legacy code. However I require partial instead of nice or strict模拟所描述的内容。
例如,考虑行为与 GKLeaderbaord
完全相同的排行榜,除了实施 loadScoresWithCompletionHandler:
.
我已经在 XCTestCase
中尝试过此代码,但它目前在指定行的运行时失败:OCMInvocationMatcher
引发 EXC_BAD_ACCESS
错误。也许有一些无限递归正在进行。
id leaderboardMock = OCMClassMock(GKLeaderboard.class);
OCMStub([leaderboardMock alloc])
.andReturn(OCMPartialMock([GKLeaderboard alloc]));
OCMStub([leaderboardMock loadScoresWithCompletionHandler: [OCMArg any]])
.andDo(^(NSInvocation *invocation) { /* ... */ });
// these parts normally nested inside legacy code
GKLeaderboard *leaderboard = /* raises EXC_BAD_ACCESS */
[[GKLeaderboard alloc] initWithPlayers: @[ GKLocalPlayer.localPlayer ]];
leaderboard.identifier = @"Test";
[leaderboard loadScoresWithCompletionHandler: nil /* ... */ ];
我做错了什么,这甚至可以用于部分模型吗?
UPDATE 我现在可以看到指示的行可能(很明显)如何导致无限递归,但还不知道如何避免(或中断)它。
UPDATE 我也没有成功尝试引入专用 class 和 OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc])
(也没有 OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])
).也许 OCMock
在 init
(documentation 说:"it is not possible to stub the init
method, because that is implemented by the mock itself")的水平上发挥了它的魔力,因此这种尝试达到了 alloc
(或 [=22)的水平=]) 无法达到预期的效果。
不确定我是否遵循了您的意图。这似乎是一个误会。以下内容对您不起作用吗?
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
您可以无限制地使用普通对象。您可以使用为对象创建的部分模拟来操作 leaderboard
中的实际实例。这就是部分模拟的美妙之处。
更新:如果对象创建不在你的控制之下,你可以尝试以下方法:
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboardMock alloc]).andReturn(leaderboardMock);
OCMStub([leaderboardMock initWithPlayers:[OCMArg any]).andReturn(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
我现在得出结论,method swizzling 是一个可能的选择。
替换方法可以是,例如从遗留代码的上下文中生成部分模型,因此在该上下文中引入部分模型,而无需更改遗留 API。
你不应该使用下面的行,它会模拟你的整个 class 和真实对象的 none 将被调用。
OCMClassMock(GKLeaderboard.class)