BeKindRewind 2.3.2

BeKindRewind 2.3.2

测试已测试
语言 Obj-CObjective C
许可证 MIT
发布最后发布2016年5月

Jordan Zucker维护。



集成测试中记录和重放网络事件的简单的 XCTestCase 子类

特性

  • 网络事件记录和重放简单易用
  • 简单易于使用的 XCTestCase 子类提供全部功能
  • 可以创建自定义匹配器以构建网络存根

描述

这是一个用于自动化集成测试中记录和重放网络事件的简单测试框架。为了避免创建网络存根的繁琐,它将实时网络请求和响应记录下来,然后在后续运行中回放它们(多亏了绝佳的OHHTTPStubs),这样您的软件可以不间断地进行集成而不会出现故障。

用法

要运行示例项目,请克隆仓库,然后首先从 Example 目录运行 pod install

安装

BeKindRewind 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中。

pod "BeKindRewind"

示例

#import <BeKindRewind/BeKindRewind.h>

@interface BeKindRewindExampleTestCase : BKRTestCase
@end

@implementation BeKindRewindExampleTestCase

- (BOOL)isRecording {
    return YES;
}

- (void)testSimpleNetworkCall {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://httpbin.org/get?test=test"]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];

    // don't forget to create a test expectation, this has the __block annotation because to avoid a retain cycle
    // XCTestExpectation is necessary for asynchronous network activity, BeKindRewind will take care of everything else
    __block XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network"];
    NSURLSessionDataTask *basicGetTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        XCTAssertNil(error);
        XCTAssertNotNil(response);
        XCTAssertNotNil(data);
        NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        XCTAssertNil(error);
        XCTAssertEqualObjects(dataDict[@"args"], @{@"test" : @"test"});
        // fulfill the expectation
        [networkExpectation fulfill];
    }];
    [basicGetTask resume];
    // explicitly wait for the expectation
    [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
        // Assert fail if timeout encounters an error
        XCTAssertNil(error);
    }];
}

@end

运行此测试后,将在控制台输出类似以下内容的日志文件

2016-03-02 10:09:27.630 xctest[92201:14865359] <BKRRecordableVCR: 0x7fb0a9714ce0>: trying to write cassette to: /Users/jordanz/Library/Developer/CoreSimulator/Devices/611CC72A-11D4-4DD2-8471-FF2F65413BC7/data/Documents/BeKindRewindExampleTestCase.bundle/testSimpleNetworkCall.plist

将此文件拖到您的项目中,并以您的测试用例名命名(它会自动命名)。

然后将 isRecording 值设置为 NO

- (BOOL)isRecording {
    return NO;
}

然后在后续运行中,测试将使用记录的文件来响应匹配的网络请求。为了确保使用了记录,请在与您的记录相关的信息上断言。以下是修改以上测试以测试测试执行期间返回的 NSHTTPURLResponse 对象中的 Date header 的示例。这是在创建记录 después 更新测试的示例。

#import <BeKindRewind/BeKindRewind.h>

@interface BeKindRewindExampleTestCase : BKRTestCase
@end

@implementation BeKindRewindExampleTestCase

- (BOOL)isRecording {
    return NO;
}

- (void)testSimpleNetworkCall {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://httpbin.org/get?test=test"]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];

    // don't forget to create a test expectation, this has the __block annotation to avoid a retain cycle
    // XCTestExpectation is necessary for asynchronous network activity, BeKindRewind will take care of everything else
    __block XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network"];
    NSURLSessionDataTask *basicGetTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        XCTAssertNil(error);
        XCTAssertNotNil(response);
        XCTAssertNotNil(data);
        NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        XCTAssertNil(error);
        XCTAssertEqualObjects(dataDict[@"args"], @{@"test" : @"test"});
        // Now this ensures that the recording returned is the stub and not an accidental "live" request. This is
        // important for stable testing
        XCTAssertEqualObjects([(NSHTTPURLResponse *)response allHeaderFields][@"Date"], @"Wed, 02 Mar 2016 18:09:28 GMT");
        // fulfill the expectation
        [networkExpectation fulfill];
    }];
    [basicGetTask resume];
    // explicitly wait for the expectation
    [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
        // Assert fail if timeout encounters an error
        XCTAssertNil(error);
    }];
}

@end

BKRTestCase 默认值

这些将自动设置。如果这些值足够,您可以随意覆盖它们,但如果没有必要,则不需要这样做。这些默认值可能在版本 1.0 之前发生变化。关于记录和播放的说明:它们只有在 [super setUp][super tearDown] 之间才有效。

- (BOOL)isRecording {
    return YES;
}

- (NSString *)baseFixturesDirectoryFilePath {
    return [BKRTestCaseFilePathHelper documentsDirectory];
}

- (BKRTestConfiguration *)testConfiguration {
    return [BKRTestConfiguration defaultConfigurationWithTestCase:self];
}

- (id<BKRTestVCRActions>)testVCRWithConfiguration:(BKRTestConfiguration *)configuration {
    return [BKRTestVCR vcrWithTestConfiguration:configuration];
}

- (NSString *)recordingCassetteFilePathWithBaseDirectoryFilePath:(NSString *)baseDirectoryFilePath {
    NSParameterAssert(baseDirectoryFilePath);
    return [BKRTestCaseFilePathHelper writingFinalPathForTestCase:self inTestSuiteBundleInDirectory:baseDirectoryFilePath];
}

- (BKRCassette *)playingCassette {
    NSDictionary *cassetteDictionary = [BKRTestCaseFilePathHelper dictionaryForTestCase:self];
    XCTAssertNotNil(cassetteDictionary);
    return [BKRCassette cassetteFromDictionary:cassetteDictionary];
}

- (BKRCassette *)recordingCassette {
    return [BKRCassette cassette];
}

处理失败的请求匹配

在测试过程中,您可能会发现需要在请求未匹配时获取信息。在这种情况下,您可以在BKRTestConfiguration对象中添加一个区块,当请求在播放时无法匹配时会执行此区块。这对于调试很有用,甚至可以用来在匹配严格的情况下使测试失败。

以下是一个配置您的BKRTestCase子类以记录失败请求的示例

- (BKRTestConfiguration *)testConfiguration {
    BKRTestConfiguration *configuration = [super testConfiguration];
    configuration.requestMatchingFailedBlock = ^void (NSURLRequest *request) {
        NSLog(@"Failed to match request: %@", request);
    };
    return configuration;
}

以下是一个使XCTestCase更加严格的示例,如果匹配失败将测试用例标记为失败

- (BKRTestConfiguration *)testConfiguration {
    BKRTestConfiguration *configuration = [super testConfiguration];
    configuration.requestMatchingFailedBlock = ^void (NSURLRequest *request) {
        XCTFail(@"Failed to match request: %@", request);
    };
    return configuration;
}

备注

BeKindRewind仅在接收到resume消息时记录网络事件。当创建NSURLSessionTask对象时,它们处于NSURLSessionTaskStateSuspended状态,并且只有在发送resume消息后才会开始记录。一旦发送了resume,它将记录直到测试结束(由您的XCTestExpectation保护)。

建议您使用BKRTestVCR子类进行记录。它自动处理异步执行和XCTestCase等方面的问题。

默认情况下,BeKindRewind在播放时(模拟)网络活动时会期待每个测试用例都存在一个属性列表的固定文件。如果不存在,则抛出异常。这可以在BKRTestConfiguration对象中重写(或者其父类BKRConfiguration)。

如果您在测试期间看到类似于*** 应用因未捕获的异常 'NSInternalInconsistencyException' 而终止,原因是:API 故障 - 在等待模式下创建期望。的异常,那么您可能需要调整您的BKRTestConfiguration对象。默认的BKRTestConfiguration构造函数(defaultConfigurationWithTestCase:)在开始和结束网络操作时调用一个块以在操作周围创建一个XCTestExpectation,以确保录音不被截断。如果您有在测试期间被触发的多个网络操作,其中一些发生在调用waitForExpectationsWithTimeout: handler:之后,那么在测试用例运行期间应该将这些块设置为nil。你应该在BKRTestCase子类中重写这一点(或者如果你自己实现了BKRVCR,则在适当的地方重写。以下是在BKRTestCase中重写的示例)

- (BKRTestConfiguration *)testConfiguration {
    BKRTestConfiguration *defaultConfiguration = [super testConfiguration];
    defaultConfiguration.beginRecordingBlock = nil;
    defaultConfiguration.endRecordingBlock = nil;
    return defaultConfiguration;
}

如果您看到由于名为reset的XCTestExpectation的异步等待超时而导致测试失败,那么这很可能是由于测试用例在移除存根的同时尝试存根请求而导致的。失败将类似于以下内容

[21:32:10]: ▸ ✗ testCopyConfigurationWithSubscribedChannelsAndCallbackQueue, ((error) == nil) failed: "Error Domain=com.apple.XCTestErrorDomain Code=0 "The operation couldn’t be completed. (com.apple.XCTestErrorDomain error 0.)""
[21:32:10]: ▸ ✗ testCopyConfigurationWithSubscribedChannelsAndCallbackQueue, Asynchronous wait failed: Exceeded timeout of 60 seconds, with unfulfilled expectations: "reset".

为了修复此问题,请确保适当地拆除并停止任何可能的长运行或后台异步进程,特别是涉及网络的操作,以避免将这些竞争条件引入到测试中。

基本测试策略

尽量编写不依赖于状态的测试。相反,确保当isRecording == YES时,测试可以完全记录以进行回放,包括setUp和tearDown。这有助于开发,并确保测试不会被写入不能在其他开发者在尝试使用新的记录更新您的测试时重创的条件。

对iOS 7的支持

如果您要测试iOS 7或更低版本,请使用0.6.x或更低版本的JSZVCR

作者

乔丹·祖克,[电子邮件保护]:bcb0b9aeacacac6c096898c9e9aeaceeaclatloaticb9bcac

授权

BeKindRewind 是在 MIT 许可证下可用的。更多信息请参阅 LICENSE 文件。

未来功能

  • Swift 测试(至少基本测试)
  • 完全泛型审计
  • Swift 包管理器
  • 详细介绍如何将录制拖入项目的教程
  • 解释固定写目录的破解方法,以便于录制(以及修复和添加测试)
  • 拆分为子规范
  • 添加代码覆盖率
  • 测试不同的错误类型(超时、无效 URL)以进行播放/录制
  • 不同类型的 NSURLSession(共享的、短暂的、标准的、后台等)的测试。为每种类型的会话创建不同的测试用例类
  • afnetworking 测试
  • 测试其他类型的网络请求(流式传输)
  • 博客文章
  • 除了 plist 序列化外,还支持 JSON 序列化