[英]TyphoonPatcher for mocking in unit tests
我有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
这是我要在测试中尝试做的事情:
- (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];
}
不幸的是我setUp
失败,下一条消息:
-[MDChoiceViewController key]: unrecognized selector sent to instance 0xbb8aaf0
我做错了什么?
您在台风方面遇到了一个糟糕的设计选择,但是有一个简单的解决方法。
您正在使用此方法:
[patcher patchDefinition:[ui choiceController] withObject:^id{
return mock([MDChoiceViewController class]);
}];
。 。 这需要TyphoonDefinition
作为参数。 引导台风时:
TyphoonAssembly
子类开始,Typhoon通过TyphoonAssembly
子类来获取用于构建组件的配方。 然后将这些TyphoonAssembly
子分类丢弃。 TyphoonComponentFactory
,它将允许您的任何TyphoonAssembly
接口摆在它前面。 (这样一来,您可以拥有同一个类的多个配置,同时又避免了魔术字符串,并允许在IDE中自动完成等)。 编写TyphoonPatcher
时,它是为您的测试准备一个新的TyphoonComponentFactory
的情况而设计的(推荐),如下所示:
//This is an actual TyphoonAssembly not the factory posing as an assembly
MiddleAgesAssembly* assembly = [MiddleAgesAssembly assembly];
TyphoonComponentFactory* factory = [TyphoonBlockComponentFactory factoryWithAssembly:assembly];
TyphoonPatcher* patcher = [[TyphoonPatcher alloc] init];
[patcher patchDefinition:[assembly knight] withObject:^id
{
Knight* mockKnight = mock([Knight class]);
[given([mockKnight favoriteDamsels]) willReturn:@[
@"Mary",
@"Janezzz"
]];
return mockKnight;
}];
[factory attachPostProcessor:patcher];
Knight* knight = [(MiddleAgesAssembly*) factory knight];
发生了什么:
所以,问题是, TyphoonPatcher
期待TyphoonDefinition
从TyphoonAssembly
,而是它正从一个实际元件TyphoonComponentFactory
。
非常令人困惑,应该弃用获取修补程序的方式。
解:
请改用以下内容:
[patcher patchDefinitionWithSelector:@selector(myController) withObject:^id{
return myFakeController;
}];
这里是一些额外的建议以及主要答案。 。 。
单元测试与集成测试:
在台风中,我们遵循传统术语:
单元测试 :与协作者隔离地测试您的班级。 在这里,您可以插入模拟或存根之类的测试双打来代替所有实际依赖项。
集成测试:使用真正的协作者测试您的班级。 尽管您可能会打补丁我们的组件以使系统处于该测试所需的状态。
因此,任何使用TyphoonPatcher
测试都可能是集成测试。
此处更多信息: 台风整合测试
解决协作程序集:
在早期版本的Typhoon中,这是必需的,但不再需要。 TyphoonAssembly的子类的任何属性都将被视为协作程序集。 删除以下内容:
- (void)resolveCollaboratingAssemblies
{
_services = [TyphoonCollaboratingAssemblyProxy proxy];
_models = [TyphoonCollaboratingAssemblyProxy proxy];
}
测试实例化他们自己的程序集:
我们建议测试实例化并在TyphoonComponentFactory上拆除它们。 优点是:
[TyphoonComponentFactory defaultFactory]
是全局的,有一些缺点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.