我想完成here所描述的内容,即在遗留代码中创建模拟。但是,我需要partial而不是漂亮的 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 我现在可以看到指示的行如何(非常明显地)导致无限递归,但还不知道如何避免(或破坏)它。
更新我也没有成功尝试使用OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc])
(也不是OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])
)引入专用课程。也许OCMock
在init
的水平上发挥其魔力(documentation说:“不可能存根init
方法,因为这是由模拟本身实现的“)这样的尝试alloc
(或initWithPlayers:
)的水平不能达到预期的效果。
答案 0 :(得分:1)
不确定我是否遵循您的目标。这似乎是一种误解。以下不适合您吗?
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: ...]);
答案 1 :(得分:0)
我现在已经得出结论,method swizzling将是一种可能的选择。
替代方法可以是从遗留代码的上下文中生成部分模型,因此在该上下文中引入部分模拟,而无需更改遗留API。
答案 2 :(得分:0)
你不应该使用以下行,它将模拟你的整个类,并且不会调用任何真实对象。
OCMClassMock(GKLeaderboard.class)