Как внедрить поддельные, заглушенные или фиктивные зависимости для интеграционных тестов с использованием тайфуна

Я пытаюсь написать интеграционные тесты с использованием KIF. Мой вопрос:

Как добавить заглушку, макет или фальшивую зависимость для конкретного контроллера представления?

Каждый контроллер представления, использующий зависимости, такие как модель данных, клиент http, менеджер магазина и т. Д., Поступает из ModelAssembly, ApplicationAssembly, ManagerAssmebly.

На раскадровке, для входа в систему у меня есть ключевой путь, содержащий значение "loginViewController".

Создание контроллеров представления:

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 есть метод для входа

- (RACSingnal*)loginWithEmail:(NSString*)email password:(NSString*)password;

Теперь в целевых тестах интеграции у меня есть класс, как:

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 - это класс, который переопределяет класс UserModel, добавляя возможность заглушки в ответ на следующий вызванный запрос.

это решение не работает.

Как и где я должен пройти FakeUserModel?

1) я хотел бы иметь доступ к внедренному экземпляру

2) внедренный экземпляр должен иметь тип FakeUserModel, который присутствует только в целевых тестах интеграции.

3) я не хочу модифицировать производственный код для интеграционных тестов.

2 ответа

Решение

Модульное тестирование

Если вы хотите заменить все зависимости для данного класса тестовым двойником и, таким образом, протестировать класс изолированно от его соавторов, это будет модульный тест. Просто создайте экземпляр для тестирования, передав парные тесты (макет, заглушка и т. Д.) В качестве соавторов.

Интеграционное тестирование

Если вы хотите исправить один или несколько экземпляров в сборке с двойным тестом, чтобы перевести систему в необходимое состояние для интеграционного теста, Typhoon предлагает несколько подходов.

Вы можете исправить компонент следующим образом:

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]

Более подробную информацию об этом подходе можно найти в разделе " Интеграционное тестирование " в руководстве пользователя.

Модульность

В качестве альтернативы вы можете модульно собрать свою сборку и активировать ее с помощью подкласса или альтернативной реализации, которая предоставляет другую реализацию определенных классов, например:

UIAssembly *uiAssembly = [[UIAssembly new] 
    activateWithCollaboratingAssemblies:@[
        [TestNetworkComponents new], //<--- Patched for testing
        [PersistenceComponents new]];

SignUpViewController* viewController = [uiAssembly signUpViewController];

Более подробную информацию об этом подходе можно найти в разделе модуляризации руководства пользователя.

Если вы хотите пропатчить сборку, которая используется раскадровкой и инициализирована с использованием интеграции Plist, вы можете сделать эту сборку по умолчанию, вызвав:

[yourAssembly makeDefault];

и вы можете получить эту сборку в своем тестовом примере, позвонив:

[yourAssembly defaultAssembly];

и после этого вы можете легко исправить некоторые определения. Важно, чтобы ваша сборка была установлена ​​по умолчанию перед началом теста, поэтому, возможно, делегат приложения подойдет для этого. Это, вероятно, не лучшее решение, но, похоже, вы хотите получить глобальный доступ к сборке.

Другие вопросы по тегам