如何用OCMock部分模拟遗留代码中的对象?

时间:2015-01-30 16:59:10

标签: objective-c mocking ocmock

我想完成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]))引入专用课程。也许OCMockinit的水平上发挥其魔力(documentation说:“不可能存根init方法,因为这是由模拟本身实现的“)这样的尝试alloc(或initWithPlayers:)的水平不能达到预期的效果。

3 个答案:

答案 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)