如何使用 Typhoon 为集成测试注入假的、存根的或模拟的依赖项
How to inject fake, stubbed or mock dependencies for Integration tests using Typhoon
我正在尝试使用 KIF 编写集成测试。我的问题是:
How to inject stubbed, mock or fake dependency for particular view controller?
每个使用数据模型、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;
现在在集成测试目标中我有 class 喜欢:
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 class 覆盖了 UserModel class,增加了对下一个调用请求存根响应的可能性。
该解决方案无效。
我应该如何以及在何处传递 FakeUserModel?
1) 我想访问注入的实例
2) 注入的实例必须是 FakeUserModel 类型,它只在集成测试目标中。
3) 我不想修改集成测试的生产代码。
单元测试
如果您希望用测试替身替换给定 class 的所有依赖项,从而在与合作者隔离的情况下测试 class,这将是一个单元测试。只需实例化一个实例进行测试,将您的测试替身(模拟、存根等)作为协作者传递。
集成测试
如果您希望使用测试替身修补程序集中的一个或多个实例,将系统置于集成测试所需的状态,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]
有关此方法的更多信息,请参阅用户指南的 Integration Testing 部分。
模块化
或者,您可以模块化您的程序集,并使用子 class 或替代实现激活,它提供某些 classes 的另一种实现,例如:
UIAssembly *uiAssembly = [[UIAssembly new]
activateWithCollaboratingAssemblies:@[
[TestNetworkComponents new], //<--- Patched for testing
[PersistenceComponents new]];
SignUpViewController* viewController = [uiAssembly signUpViewController];
有关此方法的更多信息,请参阅用户指南的 modularization section。
如果你想修补故事板使用的程序集并使用 Plist 集成初始化,那么你可以通过调用以下方法使该程序集成为默认程序集:
[yourAssembly makeDefault];
您可以通过调用以下方式在您的测试用例中获取此程序集:
[yourAssembly defaultAssembly];
之后,您可以轻松修补一些定义。在测试开始之前让你的程序集默认是很重要的,所以也许应用程序委托将是一个很好的地方。这可能不是最佳解决方案,但看起来您想实现对程序集的一些全局访问。
我正在尝试使用 KIF 编写集成测试。我的问题是:
How to inject stubbed, mock or fake dependency for particular view controller?
每个使用数据模型、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;
现在在集成测试目标中我有 class 喜欢:
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 class 覆盖了 UserModel class,增加了对下一个调用请求存根响应的可能性。
该解决方案无效。
我应该如何以及在何处传递 FakeUserModel?
1) 我想访问注入的实例
2) 注入的实例必须是 FakeUserModel 类型,它只在集成测试目标中。
3) 我不想修改集成测试的生产代码。
单元测试
如果您希望用测试替身替换给定 class 的所有依赖项,从而在与合作者隔离的情况下测试 class,这将是一个单元测试。只需实例化一个实例进行测试,将您的测试替身(模拟、存根等)作为协作者传递。
集成测试
如果您希望使用测试替身修补程序集中的一个或多个实例,将系统置于集成测试所需的状态,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]
有关此方法的更多信息,请参阅用户指南的 Integration Testing 部分。
模块化
或者,您可以模块化您的程序集,并使用子 class 或替代实现激活,它提供某些 classes 的另一种实现,例如:
UIAssembly *uiAssembly = [[UIAssembly new]
activateWithCollaboratingAssemblies:@[
[TestNetworkComponents new], //<--- Patched for testing
[PersistenceComponents new]];
SignUpViewController* viewController = [uiAssembly signUpViewController];
有关此方法的更多信息,请参阅用户指南的 modularization section。
如果你想修补故事板使用的程序集并使用 Plist 集成初始化,那么你可以通过调用以下方法使该程序集成为默认程序集:
[yourAssembly makeDefault];
您可以通过调用以下方式在您的测试用例中获取此程序集:
[yourAssembly defaultAssembly];
之后,您可以轻松修补一些定义。在测试开始之前让你的程序集默认是很重要的,所以也许应用程序委托将是一个很好的地方。这可能不是最佳解决方案,但看起来您想实现对程序集的一些全局访问。