如何使用 Typhoon 为集成测试注入虚假、存根或模拟依赖项

Posted

技术标签:

【中文标题】如何使用 Typhoon 为集成测试注入虚假、存根或模拟依赖项【英文标题】:How to inject fake, stubbed or mock dependencies for Integration tests using Typhoon 【发布时间】:2015-11-30 17:05:37 【问题描述】:

我正在尝试使用 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) 我不想为集成测试修改生产代码。

【问题讨论】:

【参考方案1】:

单元测试

如果您希望用测试替身替换给定类的所有依赖项,从而独立于其协作者来测试一个类,这将是一个单元测试。只需实例化一个用于测试的实例,将您的测试替身(模拟、存根等)作为协作者传递。

集成测试

如果您希望使用测试替身修补程序集中的一个或多个实例,以使系统进入集成测试所需的状态,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 部分找到有关此方法的更多信息。

模块化

或者,您可以模块化您的程序集,并使用子类或替代实现激活,它提供某些类的另一个实现,例如:

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

SignUpViewController* viewController = [uiAssembly signUpViewController];

可以在用户指南的modularization section 中找到有关此方法的更多信息。

【讨论】:

【参考方案2】:

如果您想修补情节提要使用并使用 Plist 集成初始化的程序集,则可以通过调用使该程序集成为默认值:

[yourAssembly makeDefault];

你可以通过调用在你的测试用例中得到这个程序集:

[yourAssembly defaultAssembly];

然后,您可以轻松地修补一些定义。在测试开始之前让你的程序集默认是很重要的,所以也许应用程序委托将是一个很好的地方。这可能不是最好的解决方案,但看起来您想要实现对程序集的一些全局访问。

【讨论】:

以上是关于如何使用 Typhoon 为集成测试注入虚假、存根或模拟依赖项的主要内容,如果未能解决你的问题,请参考以下文章

如何存根/注入视图控制器以在 iOS 5 运行时进行基于状态的测试?

如何在 Flutter 集成测试中最好地存根/模拟 rest API 调用

创建存根 bigquery 表

单元测试术语的概述(存根与模拟,集成与交互)?

测试目标中的 Swift Typhoon 错误 - 不是 Typhoon Assembly 的子类

台风从情节提要将属性注入视图控制器