OCMock 测试分配一个对象并在其上调用一个方法

Posted

技术标签:

【中文标题】OCMock 测试分配一个对象并在其上调用一个方法【英文标题】:OCMock test an object is allocated and a method is called on it 【发布时间】:2013-07-29 04:29:56 【问题描述】:

我终于在我正在处理的项目中实施了一些 TDD,并且遇到了边缘......我知道我想要的代码,但不知道如何测试它:)

我正在寻找的实现是:

- (void) doSomething

    FooBuilder *foo = [[FooBuilder alloc] init];
    [foo doSomethingElseWithCompletionBlock:^
        [self somethingDone];
    ];

所以我希望我的测试验证 a) 被测方法分配了一个新的 FooBuilder 和 b) 然后该方法调用了新对象上的方法。

我该怎么做?我开始尝试模拟 alloc 类方法,但很快确定这条路是疯狂的。

请注意,我不是用这个测试来测试 FooBuilder 本身,只是协作存在。

【问题讨论】:

【参考方案1】:

通常情况下,依赖注入用于提供一个完整的对象,表示“不是要求这个对象,而是使用这个。”但在这种情况下,我们希望能够实例化一个新对象。因此,与其注入对象,不如注入类。 “与其创建一个特定的类,不如在这里实例化其中一个。”

依赖注入有两种主要形式:“构造函数注入”(我将坚持使用术语“构造函数”,尽管 Objective-C 将其分为分配和初始化)和“属性注入”。

对于构造函数注入,在初始化器中指定类:

- (instancetype)initWithFooBuilderClass:(Class)fooBuilderClass;

对于属性注入,在属性中指定类:

@property (nonatomic, strong) Class fooBuilderClass;

构造函数注入更清晰,因为它使依赖关系明显。但是你可能更喜欢属性注入。有时我从一种方式开始,然后向另一种方式重构,改变了我的想法。

无论哪种方式,您都可以有一个默认初始化程序,它调用 -initWithFooBuilderClass: 或将属性设置为 [FooBuilderClass class]

然后doSomething 会这样开始:

- (void)doSomething

    id foo = [[self.fooBuilderClass alloc] init];
    ...

【讨论】:

乔恩,感谢您的关注和详细的回复 - 但我想我在这里遗漏了一些东西。您将被测类与FooBuilder 类之间的耦合诊断为问题。这样做,我觉得你让一些简单的事情变得更加复杂。我有一个显示 Foos 的对象 Bar;为此,它需要使用 FooBuilder 构建一个 Foo。我觉得你是在要求我介绍一般性,我不需要仅仅为了使测试更容易。 我今天早上醒来意识到问题实际上只是一个类的依赖注入。所以我完全重写了我的答案;试穿尺寸。 …如果仍然感觉过于复杂,请告诉我,我会尝试进一步解释。 我仍然认为您没有得到我的要求。我的问题实际上是关于如何驱动 OCMock API 以确保我的被测对象进行正确的出站调用。您仍在尝试将通用性注入不需要它的类中。 OCMock 不是问题。事实上,在您熟悉这些模式之前,我建议不要使用 any 模拟框架(改为手动制作您自己的测试替身)。这里的模式是依赖注入;这是提供双倍的机制。如果没有 DI,您的 TDD 将仅限于状态验证(被测类的状态是什么)。通过添加 DI 和测试替身,您可以进行行为验证(被测类如何与其他类交互)。 @JonReid 是正确的。如果你想这样写方法,你不能用OCMock来验证你对FooBuilder的使用。另一种选择是更改您的方法以采用FooBuilder 参数:-(void)doSomethingWithFooBuilder:(FooBuilder *)fooBuilder。然后您的测试可以通过 FooBuilder 的模拟实例。【参考方案2】:

我最终通过向FooBuilder 添加一个新的类方法来解决这个问题,该方法将完成块作为参数。所以我已经有效地将实例化和方法调用从我的被测对象移到了合作者对象中。现在我可以模拟单个类方法调用了。

我认为这最终的设计比我开始的设计要好一些;现在对类的用户隐藏了需要实例化新的FooBuilder 的细节。这也很简单。

它确实具有保持我的被测对象和FooBuilder 类之间的强耦合的特性。也许那会在路上咬我 - 但我打赌 YAGNI 不会。

【讨论】:

我很高兴你找到了一些有用的东西。但是您将在其他地方面临同样的问题,并且需要利用依赖注入。我在这里再举一个 DI 例子:***.com/questions/13711911/… 我相信我会的,这是一个很好的技巧。但是,在这种情况下,确实感觉添加通用性会使代码更难理解。感谢您的帮助 - 我已经在我的应用程序中使用了来自您网站的一些提示 :)

以上是关于OCMock 测试分配一个对象并在其上调用一个方法的主要内容,如果未能解决你的问题,请参考以下文章

React 中的 React.component 除了创建我们组件的实例并在其上设置 props 对象之外还有啥作用?

使用从测试方法调用的方法的类的 OCMock

自动释放对象递减的保留计数何时减少?

OCMock: OCMPartialMock 对象调用实际方法而不是存根

UITableViewController 并在其上推送 DetailViewController

如何创建一个空垫并在其上画线?