I have Assembly
:
@interface MDUIAssembly : TyphoonAssembly
@property (nonatomic, strong, readonly) MDServiceAssembly *services;
@property (nonatomic, strong, readonly) MDModelAssembly *models;
- (id)choiceController;
@end
@implementation MDUIAssembly
- (void)resolveCollaboratingAssemblies
{
_services = [TyphoonCollaboratingAssemblyProxy proxy];
_models = [TyphoonCollaboratingAssemblyProxy proxy];
}
- (id)choiceController
{
return [TyphoonDefinition withClass:[MDChoiceViewController class]
configuration: ^(TyphoonDefinition *definition) {
[definition useInitializer:@selector(initWithAnalytics:diary:)
parameters: ^(TyphoonMethod *initializer) {
[initializer injectParameterWith:[_services analytics]];
[initializer injectParameterWith:[_models diary]];
}];
}];
}
@end
Here what I'm trying to do in tests:
- (void)setUp
{
patcher = [TyphoonPatcher new];
MDUIAssembly *ui = (id) [TyphoonComponentFactory defaultFactory];
[patcher patchDefinition:[ui choiceController] withObject:^id{
return mock([MDChoiceViewController class]);
}];
[[TyphoonComponentFactory defaultFactory] attachPostProcessor:patcher];
}
- (void) tearDown
{
[super tearDown];
[patcher rollback];
}
Unfortunately my setUp
fails with next message:
-[MDChoiceViewController key]: unrecognized selector sent to instance 0xbb8aaf0
What I'm doing wrong?
Here's some extra advice to go along with the main answer . . .
Unit Tests vs Integration Tests:
In Typhoon we adhere to the traditional terms:
Unit Tests : Testing your class in isolation from collaborators. This is where you inject test doubles like mocks or stubs in place of all of the real dependencies.
Integration Tests: Testing your class using real collaborators. Although you may patch our a component in order to put the system in the required state for that test.
So any test that uses
TyphoonPatcher
would probably be an integration test.More info here: Typhoon Integration Testing
Resolve Collaborating Assemblies:
This was required in earlier version of Typhoon, but is not longer needed. Any properties that are are sub-class of TyphoonAssembly will be treated as collaborating assemblies. Remove the following:
Tests instantiate their own assembly:
We recommend that tests instantiate and tear down their on TyphoonComponentFactory. The advantages are:
[TyphoonComponentFactory defaultFactory]
is a global and has some drawbacks.You've encountered a poor design choice on Typhoon's part, but there's an easy work-around.
You're using this method:
. . which is expecting a
TyphoonDefinition
as argument. When bootstrapping Typhoon:TyphoonAssembly
subclasses, which Typhoon instruments to obtain recipes for building components. TheseTyphoonAssembly
sub-clases are then discarded.TyphoonComponentFactory
that will allow any of yourTyphoonAssembly
interfaces to pose in front of it. (This is so you can have multiple configs of the same class, while still avoiding magic strings, allows auto-completion in your IDE, etc).When the
TyphoonPatcher
was written it was designed for the case where you obtain a newTyphoonComponentFactory
for your tests (recommended), like this:What happened:
So the problem is that the
TyphoonPatcher
is expectingTyphoonDefinition
from theTyphoonAssembly
and instead it is getting an actual component from aTyphoonComponentFactory
.Very confusing, and that way of obtaining a patcher should be deprecated.
Solution:
Use the following instead: