【问题标题】:Passing primitives to an OCMock's stub将原语传递给 OCMock 的存根
【发布时间】:2011-06-12 23:02:11
【问题描述】:

我正在学习如何使用 OCMock 来测试我的 iPhone 的项目,我有这样的场景:一个带有 getHeightAtX:andY: 方法的 HeightMap 类,以及一个使用 HeightMap 的 Render 类。我正在尝试使用一些 HeightMap 模拟对渲染进行单元测试。这有效:

id mock = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[mock stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:0 andY:0];

当然,仅适用于 x=0y=0。我想使用“平面”高度图进行测试。这意味着我需要做这样的事情:

id chunk = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[chunk stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:[OCMArg any] andY:[OCMArg any]];

但这会引发两个编译警告:

警告:传递 'getHeightAtX:andY:' 的参数 1 从指针生成整数而不进行强制转换

还有一个运行时错误:

调用了意外的方法:'getHeightAtX:0 andY:0 stubbed: getHeightAtX:15545040 andY:15545024'

我错过了什么?我发现无法将 anyValue 传递给这个模拟。

【问题讨论】:

标签: xcode primitive ocmock


【解决方案1】:

这个问题被问到已经有一段时间了,但我自己也遇到了这个问题,在任何地方都找不到解决方案。 OCMock 现在支持ignoringNonObjectArgs,因此expect 的示例是

[[[mockObject expect] ignoringNonObjectArgs] someMethodWithPrimitiveArgument:5];

5 实际上并没有做任何事情,只是一个填充值

【讨论】:

  • 还有一个警告,如果你有一个方法你正在存根并使用ignoringNonObjectArgs,如果你在原始参数之后有一个块并且你用[OCMarg checkWithBlock:]存根它,那个块不会被评估(调用)
  • 这是对 OP 问题的正确答案。感谢您指出这一点!我没有看到它被添加到 OCMock 中。
  • +1 鉴于它有奇怪的副作用,我认为这个警告是一个很好的选择。部分解决方案!
  • 是否也有类似OCMExpect的宏语法来支持这个?
【解决方案2】:

OCMock 当前不支持原始参数的松散匹配。有关于支持此on the OCMock forums 的潜在更改的讨论,尽管它似乎已经停滞不前。

我发现的唯一解决方案是以我知道将要传入的原始值的方式构建我的测试,尽管它远非理想。

【讨论】:

  • 自从您发布有关此主题的帖子后有什么变化吗?
  • 我相信您现在可以使用 [OCMArg setToValue:OCMOCK_VALUE(some_primitive)] 执行此操作。它基本上用 NSValue 包装原语。至少,“功能”页面将其作为示例显示:ocmock.org/features
  • @stuckj 不能解决 OP 的问题。 setToValue 允许您存根一个接受指针参数的方法。通常,该方法会为该指针分配一些值。 setToValue 允许您在存根拦截调用时分配一个值。 OP 正在尝试对原始参数进行松散匹配。 @Andrew_Park 有(现在)正确答案,可以使用 ignoringNonObjectArgs
【解决方案3】:

请改用OCMockito

它支持原始参数匹配。

例如,在您的情况下:

id chunk = mock([Chunk class]);
[[given([chunk getHeightAtX:0]) withMatcher:anything() forArgument:0] willReturnInt:0];

【讨论】:

  • 啊,我喜欢 OCMockito,但这一切都是你没有部分的嘲笑!?
【解决方案4】:

除了 Andrew Park 回答之外,您还可以让它更通用、更美观:

#define OCMStubIgnoringNonObjectArgs(invocation) \
({ \
    _OCMSilenceWarnings( \
        [OCMMacroState beginStubMacro]; \
        [[[OCMMacroState globalState] recorder] ignoringNonObjectArgs]; \
        invocation; \
        [OCMMacroState endStubMacro]; \
    ); \
})

你可以这样使用它:

OCMStubIgnoringNonObjectArgs(someMethodParam:0 param2:0).andDo(someBlock)

你可以对期待做同样的事情。这种情况用于作为主题启动请求的存根。已使用 OCMock 3.1.1 进行了测试。

【讨论】:

  • 嗯,someMethodParam:0 param2:0 正在调用什么对象?
  • 对象,你实际上是在嘲笑它
【解决方案5】:

你可以这样做: id chunk = OCMClassMock([Chunk class]) OCMStub([chunk ignoringNonObjectArgs] getHeightAtX:0 andY:0]])

阅读更多:http://ocmock.org/reference/#argument-constraints

【讨论】:

【解决方案6】:

尽管相当老套,但使用期望存储传递的块以便稍后在测试代码中调用的方法对我有用:

- (void)testVerifyPrimitiveBlockArgument
{
    // mock object that would call the block in production
    id mockOtherObject = OCMClassMock([OtherObject class]);

    // pass the block calling object to the test object
    Object *objectUnderTest = [[Object new] initWithOtherObject:mockOtherObject];

    // store the block when the method is called to use later
    __block void (^completionBlock)(NSUInteger value) = nil;
    OCMExpect([mockOtherObject doSomethingWithCompletion:[OCMArg checkWithBlock:^BOOL(id value) { completionBlock = value; return YES; }]]);

    // call the method that's being tested
    [objectUnderTest doThingThatCallsBlockOnOtherObject];

    // once the expected method has been called from `doThingThatCallsBlockOnOtherObject`, continue
    OCMVerifyAllWithDelay(mockOtherObject, 0.5);
    // simulate callback from mockOtherObject with primitive value, can be done on the main or background queue
    completionBlock(45);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-16
    • 1970-01-01
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多