Reputation: 5453
I'm finally imposing some TDD on a project I'm working on, and running into the edges... I know the code I want but not how to test for it :)
The implementation I'm looking for is:
- (void) doSomething
{
FooBuilder *foo = [[FooBuilder alloc] init];
[foo doSomethingElseWithCompletionBlock:^{
[self somethingDone];
}];
}
So I want my test to verify that a) the method under test allocates a new FooBuilder
and b) that method then calls a method on the new object.
How do I go about this? I started down the path of trying to mock the alloc
class method but quickly determined that down that path lies madness.
Note I'm not testing FooBuilder itself with this test, just that the collaboration is there.
Upvotes: 4
Views: 630
Reputation: 5453
I ended up addressing this by adding a new class method to FooBuilder
which takes a completion block as an argument. So I've effectively moved the instantiation and method call out of my object-under-test into the collaborator object. Now I can mock that single class method call.
I think this ends up being slightly better design than what I started with; the detail that there needs to be a new FooBuilder
instantiated is hidden from users of the class now. It's also pretty simple.
It does have the property that it maintains the strong coupling between my object-under-test and the FooBuilder
class. Maybe that'll bite me down the road - but I'm making the YAGNI bet that it won't.
Upvotes: 0
Reputation: 20980
Normally, dependency injection is used to provide a fully-formed object, saying "instead of asking for this object, here you go, use this." But in this case, we want the ability to instantiate a new object. So instead of injecting an object, all we have to do is inject the class. "Instead of creating a specific class, here you go, instantiate one of these."
There are two main forms of dependency injection: "constructor injection" (I'll stick with the term "constructor" even though Objective-C separates this into allocation and initialization) and "property injection".
For constructor injection, specify the class in the initializer:
- (instancetype)initWithFooBuilderClass:(Class)fooBuilderClass;
For property injection, specify the class in a property:
@property (nonatomic, strong) Class fooBuilderClass;
Constructor injection is clearer, because it makes the dependency obvious. But you may prefer property injection. Sometimes I start one way and refactor toward the other, changing my mind.
Either way, you can have a default initializer that either calls -initWithFooBuilderClass:
, or sets the property, to [FooBuilderClass class]
.
Then doSomething
would start like this:
- (void)doSomething
{
id foo = [[self.fooBuilderClass alloc] init];
...
Upvotes: 1