我终于把一些TDD强加给了我正在进行的项目,并陷入了边缘。。。我知道我想要的代码,但不知道如何测试:)
我正在寻找的实现是:
- (void) doSomething
{
FooBuilder *foo = [[FooBuilder alloc] init];
[foo doSomethingElseWithCompletionBlock:^{
[self somethingDone];
}];
}
因此,我希望我的测试验证a)被测试的方法分配了一个新的FooBuilder
,b)该方法然后在新对象上调用一个方法。
我该怎么做?我开始尝试模拟alloc
类方法,但很快就确定这条路是疯狂的。
注意,我并没有用这个测试来测试FooBuilder本身,只是协作是存在的。
通常,依赖注入用于提供一个完整的对象,表示"与其要求这个对象,不如使用这个。"但在这种情况下,我们希望能够实例化一个新对象。因此,我们所要做的不是注入对象,而是注入类。"与其创建一个特定的类,不如实例化其中一个。"
依赖注入有两种主要形式:"构造函数注入"(尽管Objective-C将其分为分配和初始化,但我仍然使用"构造函数"一词)和"属性注入"。
对于构造函数注入,请在初始值设定项中指定类:
- (instancetype)initWithFooBuilderClass:(Class)fooBuilderClass;
对于属性注入,请指定属性中的类:
@property (nonatomic, strong) Class fooBuilderClass;
构造函数注入更清晰,因为它使依赖关系变得明显。但你可能更喜欢房地产注入。有时我会从一种方式开始,然后向另一种方式重构,改变我的想法。
无论哪种方式,都可以有一个默认的初始值设定项,它可以调用-initWithFooBuilderClass:
,也可以将属性设置为[FooBuilderClass class]
。
然后doSomething
会这样启动:
- (void)doSomething
{
id foo = [[self.fooBuilderClass alloc] init];
...
我最终通过向FooBuilder
添加一个新的类方法来解决这个问题,该方法将一个完成块作为参数。因此,我已经有效地将实例化和方法调用从测试中的对象转移到了collaborator对象中。现在我可以模拟那个单类方法调用了。
我认为这最终会比我刚开始的设计稍微好一点;需要实例化新的CCD_ 7的细节现在对该类的用户隐藏。它也很简单。
它确实具有这样的特性,即它保持了我的测试对象和FooBuilder
类之间的强耦合。也许这会咬我一口——但我打赌它不会。