在单元测试中,将模拟值设置为参数(按引用传递)

时间:2016-08-25 18:11:17

标签: ios unit-testing ocmock

我有一个方法:

-(void)startTaskForResult:(long long*)result {
  ...
}

我要单元测试的函数调用上面的函数:

-(void)doWork {
 long long result = 0;
 [self startTaskForResult:&result];
}

我正在使用OCMock库进行单元测试。在我的测试用例中,我想将result参数设置为模拟值,例如100不关心-(void)startTaskForResult:(long long*)result的实际实施。

我尝试了以下方式:

-(void)testDoWork{
// try to set 100 to argument 'result'
OCMStub([classToTest startTaskForResult:[OCMArg setToValue:OCMOCK_VALUE((long long){100})]]);
// run the function, but it doesn't use mocked value 100 for argument 'result'
[classToTest doWork];
...
}

但是,当我运行测试时,它不会将参数result的模拟值100使用。在我的情况下,将模拟值设置为参数的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

很少有人回答你的问题:

问题代码:

- (void)testDoWork 
{
    id mock = OCMPartialMock(classToTest)
    OCMStub([mock startTaskForResult:[OCMArg setToValue:OCMOCK_VALUE((long long){100})]]).andForwardToRealObject;
    // set your expectation here
    [classToTest doWork];
}
  1. 解决您的特定问题:

    • 你的对象应该是部分模拟
    • 你的方法应该是存根(你做到了)
    • 您的存根应转发给真实对象(我假设您需要调用方法startTaskForResult:
  2. 但是,您遇到问题是因为您使用错误的方法进行测试;

  3. 编写单元测试有三种最常见的策略:

    1. Arrange-Act-Assert用于测试方法
    2. Given-When-Then用于测试功能
    3. Setup-Record-Verify用于测试副作用。这通常需要嘲笑。
    4. 所以:

      • 如果你想测试startTaskForResult:返回特定值 - 你应该只调用它并期望返回值(不是你的情况,方法返回类型是无效的)
      • 如果方法改变了对象的状态 - 你应该期望状态改变,比如属性值
      • 如果调用doWork会产生调用startTaskForResult:的副作用,那么你应该将其存根并期待它的调用,就像我用上面的代码编写的那样。然而(!!!),但你不应该期待这样的事情。这不是一种有很多测试意义的行为,因为它是内部类的实现细节。一种可能的情况是,当两种方法都是公开的并且在类合同中明确说明时,一种方法应该通过一些初步设置来调用另一种方法。在这种情况下,您希望使用某些状态/参数进行方法调用。

      要使您的应用程序代码可测试,您需要不断重构代码。有些代码是不可测试的,采用应用程序代码可能更好,而不是尝试用测试覆盖它。你失去了最初的测试目标 - 重构安全性和降低成本。