How to inject fake, stubbed or mock dependencies for Integration tests using Typhoon

别来无恙 提交于 2019-12-07 13:54:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!