问题
I'm trying to write integration tests using KIF. My question is:
How to inject stubbed, mock or fake dependency for particular view controller?
Each view controller using dependencies like a data model, http client, store manager etc. comes from ModelAssembly, ApplicationAssembly, ManagerAssmebly.
On storyboard, for login view i have a key path, containing value "loginViewController".
Creating view controllers:
ViewControllersAssembly.h
@interface ViewControllersAssembly : TyphoonAssembly
@property (nonatomic, strong) ModelAssembly *modelAssembly;
- (id)loginViewController;
@end
ViewControllersAssembly.m
@implementation ViewControllersAssembly
- (UIViewController *)loginViewController {
return [TyphoonDefinition withClass:[LoginViewController class] configuration:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(userModel) with:[self.modelAssembly userModel]];
}];
}
UserModel have method to login
- (RACSingnal*)loginWithEmail:(NSString*)email password:(NSString*)password;
Now in integration tests target i have class like:
LoginTests.h
@interface LoginTests : KIFTestCase
@property (nonatomic, strong) UserModel *fakeUserModel;
@end
LoginTests.m
@implementation LoginTests
- (void)beforeAll {
self.fakeDataModel = [self mockDataModel];
}
- (void)testLogin {
[self.fakeDataModel mockNextResponse:[RACSignalHelper getGeneralErrorSignalWithError:[[NSError alloc] initWithDomain:@"http://some.com" code:452 userInfo:nil]]];
[tester waitForViewWithAccessibilityLabel:@"loginScreen"];
[tester enterText:@"user@gmail.com" intoViewWithAccessibilityLabel:@"emailAdress"];
[tester enterText:@"asd123" intoViewWithAccessibilityLabel:@"password"];
[tester tapViewWithAccessibilityLabel:@"loginButton"];
[tester tapViewWithAccessibilityLabel:@"OK"];
// for example error code 542 we should display alert with message "User Banned"
// now somehow check that UIAlertView localizedDescription was "User Banned"
}
- (FakeUserModel *)mockUserModel {
ModelAssembly *modelAssembly = [[ModelAssembly assembly] activate];
TyphoonPatcher *patcher = [[TyphoonPatcher alloc] init];
[patcher patchDefinitionWithSelector:@selector(userModel) withObject:^id{
return [FakeUserModel new];
}];
[modelAssembly attachDefinitionPostProcessor:patcher];
return [modelAssembly userModel];
}
FakeUserModel is class that override UserModel class, adding possibility to stub response for next called request.
that solution not is not working.
How and where i should pass FakeUserModel?
1) i'd like to have access to injected instance
2) injected instance must be of type FakeUserModel, which is only in integration tests target.
3) i don't want to modify production code for integration tests.
回答1:
Unit Testing
If you wish to replace all dependencies for a given class with a test double, and thus test a class in isolation from its collaborators, this would be a unit test. Simply instantiate an instance for testing, passing in your test doubles (mock, stub, etc) as collaborators.
Integration Testing
If you wish to patch-out one or more instances in an assembly with a test double, to put the system into the required state for an integration test, Typhoon provides several approaches.
You can patch out a component as follows:
MiddleAgesAssembly* assembly = [[MiddleAgesAssembly assembly] activate];
TyphoonPatcher* patcher = [[TyphoonPatcher alloc] init];
[patcher patchDefinitionWithSelector:@selector(knight) withObject:^id{
Knight* mockKnight = mock([Knight class]);
[given([mockKnight favoriteDamsels]) willReturn:@[
@"Mary",
@"Janezzz"
]];
return mockKnight;
}];
[assembly attachPostProcessor:patcher];
Knight* knight = [(MiddleAgesAssembly*) factory knight]
More information on this approach can be found in the Integration Testing section of the user guide.
Modularization
Alternatively you could modularize your assembly, and activate with a sub-class or alternative implementation, that provides another implementation of certain classes, example:
UIAssembly *uiAssembly = [[UIAssembly new]
activateWithCollaboratingAssemblies:@[
[TestNetworkComponents new], //<--- Patched for testing
[PersistenceComponents new]];
SignUpViewController* viewController = [uiAssembly signUpViewController];
More information on this approach can be found in the modularization section of the user guide.
回答2:
If you want to patch-out the assembly that is used by the storyboard and initialized using Plist integration, then you can make that assembly default by calling:
[yourAssembly makeDefault];
and you can get this assembly in your test case by calling:
[yourAssembly defaultAssembly];
and after that, you can easily patch some definitions. It's important to make your assembly default before test starts, so maybe app delegate will be a good place for that. This is probalby not the best solution, but it looks like you want to achieve some global access to assembly.
来源:https://stackoverflow.com/questions/34004010/how-to-inject-fake-stubbed-or-mock-dependencies-for-integration-tests-using-typ