使用OCMock测试嵌套错误处理

时间:2009-07-11 22:00:39

标签: objective-c cocoa unit-testing mocking

我真的很感激使用OCMock测试错误的一些建议。

测试方法

它使用AppScriptController类的实例抓取进程。

@implementation ProcessGrabber

  -(void)grabProcess {
      NSError *error = nil;

      NSString *processName = [appScriptController processName:ProcessRef
                                                         error:&error];
      if(error == nil) {
         NSNumber *processID = [appScriptController processID:ProcessRef
                                                        error:&error];

          if(error == nil) {
          ... More operations...
          }
      }

      if(error) {
          [NSApp raiseError:error];
      }
  }

@end

模拟方法

AppScriptController类与系统交互,所以我想嘲笑它。

@implementation AppScriptController

-(NSString *)processName:(SERef *)theProcessRef error:(NSError **)theError {
    return [[theProcessRef name] error:error];
}

-(NSNumber *)processID:(SERef *)theProcessRef error:(NSError **)theError {
    return [[theProcessRef name] error:error];
}

测试

-(void)testGrabProcess {
  NSError *error = nil;        

  OCMockObject *mock = [OCMockObject mockForClass:[AppScriptController class]];
  [[[mock expect] andReturn:@"Process Name"] processName:nil error:&error];

  // ... Somehow inject an error here...

  [[[mock expect] andReturn:34] processID:nil error:&error];

}

问题

我想检查嵌套的错误处理代码是否正常工作。所以我希望能够在方法的特定点注入特定的错误代码。

实施例

模拟进程名称获取中的错误。我测试过程ID抓取没有被调用。

仅在进程ID抓取中模拟错误。我测试过程名称抓取是否正在运行,但是在执行进程ID之后没有任何操作。

无论错误发生在何处,请测试是否引发了正确的错误。

我尝试了什么

要尝试的显而易见的事情是设置错误,我把“......不知何故在这里注入错误...”。我没想到这会起作用,但事实并非如此。

我在Google上搜索了答案,我想过尝试以某种方式将错误包装在一个单独的类中,但我看不出这会有什么帮助。

我已经考虑了好几个小时,但我仍然没有接近解决方案。

有人可以帮忙吗?

5 个答案:

答案 0 :(得分:2)

我很想看到一个真正的测试代码的实际测试方法,因为我觉得在问题的简化中迷失了一些东西。也就是说,也许OCMock不是这项工作的最佳工具,在这里?我不太确定它能很好地处理NSError **参数(我从未尝试过)。也许你可以通过使用自定义test stub而不是通用的模拟对象来获得更好的服务?

答案 1 :(得分:2)

如果其他人仍然在寻找这个,实际上有一个相当简单的方法来OCMock

- (void)testGrabProcess {

    OCMockObject *mock = [OCMockObject mockForClass:[AppScriptController class]];
    [[[[mock expect] andDo:^(NSInvocation *invocation) {
        NSError * __autoreleasing *anError = nil;
        [invocation getArgument:&anError atIndex:3];
        *anError = [NSError errorWithDomain:@"MYDomain" code:2323 userInfo:nil];
    }] andReturn:@"Process Name"] processName:nil error:&error];

    [[[mock expect] andReturnValue:@34] processID:nil error:(BECloudPlayerDevice *__autoreleasing *)[OCMArg anyPointer]];
}

答案 2 :(得分:0)

你可以为返回nil的情况创建另一个测试;

-(void)testGrabProcessError {
  NSError *error = //create the error code you want to test here.;        

  OCMockObject *mock = [OCMockObject mockForClass:[AppScriptController class]];
  [[[mock expect] andReturn:nil] processName:nil error:&error];
  ...
}

答案 3 :(得分:0)

我解决了这个问题如下:

在MyClass中有一个像这样的方法:

- (BOOL) myMethod:(id)someParameter error:(NSError **)error;

在测试中我这样做:

id myMock = [OCMockObject mockForClass:[MyClass class]];
[[myMock expect] myMethod:someParameter error:(NSError *__autoreleasing *)[OCMArg anyPointer]];

这样我就避免创建MyClass的自定义模拟子类。

答案 4 :(得分:0)

对于正在寻找 OCMock 3 语法的用户:

    AppScriptController *controller = OCMClassMock([AppScriptController class]);
    OCMStub([controller processName:[OCMArg any] error:[OCMArg setTo:nil]]).andDo(^(NSInvocation *invocation) {
        NSError * __autoreleasing *err = nil;
        [invocation getArgument:&err atIndex:3];
        *err = [NSError errorWithDomain:@"MyDomain" code:123 userInfo:nil];
    });