6

我想知道如何进行测试。我有一个采用参数的方法,并根据该参数的某些属性创建另一个对象并对其进行操作。代码看起来像这样:

- (void) navigate:(NavContext *)context {
  Destination * dest = [[Destination alloc] initWithContext:context];
  if (context.isValid) {
    [dest doSomething];
  } else {
    // something else
  }
  [dest release];
}

我要验证的是,如果 context.isValid 为真,则在 dest 上调用 doSomething,但我不知道如何使用 OCMock 或任何其他传统测试方法来测试它(或者是否可能),因为该对象是完全在方法的范围内创建。我会以错误的方式解决这个问题吗?

4

4 回答 4

5

您可以使用 OCMock,但您必须修改代码以获取Destination对象或使用可以先用模拟对象替换的单例对象。

最干净的方法可能是实施

-(void) navigate:(NavContext *)context destination:(Destination *)dest;

方法。将实现更改为-(void) navigate:(NavContext *)context以下内容:

- (void) navigate:(NavContext *)context {
    Destination * dest = [[Destination alloc] initWithContext:context];
    [self navigate:context destination:dest];
    [dest release];
}

这将允许您的测试直接调用带有额外参数的方法。(在其他语言中,您可以简单地通过为目标参数提供默认值来实现这一点,但 Objective-C 不支持默认参数。)

于 2009-07-06T21:22:58.527 回答
1

我要验证的是,如果 context.isValid 为真,则在 dest 上调用 doSomething

我想你可能在这里测试了错误的东西。您可以放心地假设(我希望)布尔语句在 ObjC 中正常工作。您不想测试 Context 对象吗?如果 context.isValid 则保证 [dest doSomething] 分支被执行。

于 2009-07-06T21:27:47.787 回答
0

这是完全可能的,使用方法调配这样有趣的技术,但它可能会以错误的方式进行。如果绝对没有办法观察从单元测试中调用的效果,那么它不是调用实现细节doSomething的事实吗?doSomething

(如果您要进行此测试,实现目标的一种方法是将doSomething方法替换为Destination通知您的单元测试然后将调用传递给的方法doSomething。)

于 2009-07-06T21:04:22.857 回答
0

我喜欢在这种情况下使用工厂方法。

@interface Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context;
@end

@implementation Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context
{
    return [[Destination alloc] initWithContext:context];
}
@end

然后我制作了一个 FakeClass:

#import "Destination+Factory.h"

@interface FakeDestination : Destination
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
// Note! Instance method!
- (Destination *)destinationWithContext:(NavContext *)context;
@end

@implementation FakeDestination
+ (id)sharedInstance
{
    static id _sharedInstance = nil;
    if (!_sharedInstance)
    {
        _sharedInstance = [[FakeDestination alloc] init];
    }
    return _sharedInstance;
}
+ (void)setSharedInstance:(id)sharedInstance
{
    _sharedInstance = sharedInstance;
}
// Overrides
+ (Destination *)destinationWithContext:(NavContext *)context { [FakeDestination.sharedInstance destinationWithContext:context]; }
// Instance
- (Destination *)destinationWithContext:(NavContext *)context { return nil; }
@end

一旦你设置好了,你只需要调整类方法+ (Destination *)destinationWithContext:(NavContext *)context;

现在您设置为:

id destinationMock = [OCMock mockForClass:FakeDestination.class];
// do the swizzle
[FakeDestination setSharedInstance:destinationMock];
[[destinationMock expect] doSomething];
// Call your method
[destinationMock verify];

这是相当多的预先编码,但它非常可重用。

于 2013-03-31T04:26:43.480 回答