iOS单元测试-代码实现

iOS的单元测试代码我们使用Xcode自带的XCTest,XCTest可以从逻辑和性能两个方向进行测试,下面我们来一起看下如何实现。

逻辑测试

1.为工程添加一个单元测试的Target(已经有的可以略过这一步)

20190109-1

2.为单元测试Target添加测试文件

20190109-2

3.编写测试逻辑

//每个用例方法执行前会执行的方法,为用例准备环境
- (void)setUp {
    // Put setup code here. This method is called before the invocation of each test method in the class.
}

//每个用例方法执行后会执行的方法,对环境进行清洁或者重置
- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

//以test开头的方法是一个可执行的测试用例方法,我们通过这些方法来跑不同的用例
- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
    NSInteger i = 1;
    NSInteger j = 1;
    NSInteger k = i + j;
    XCTAssertEqual(k, 2, @"k的值应该等于2");
    //使用XCTAssert断言来判断执行的结果是否通过
}

4.执行单元测试

20190109-3

5.查看测试结果

20190109-4

异步测试

针对异步执行的代码我们需要这样写

- (void)setUp {
    //超时时间,单位为秒
    self.timeout = 20;
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

- (void)testExample {
    NSString *username = @"shuhai";
    NSString *password = @"123";

    XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];

    [SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
        XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
        [expectation fulfill];
    }];
    //waitForExpectationsWithTimeout:handler:方法会阻塞当前测试线程,直到所有的XCTestExpectation fulfill
    [self waitForExpectationsWithTimeout:self.timeout handler:nil];
}

另外waitForExpectations:timeout:方法可以等待多个特定的XCTestExpectation

- (void)setUp {
    //超时时间,单位为秒
    self.timeout = 20;
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

- (void)testExample {
    NSString *username = @"shuhai";
    NSString *password = @"123";

    XCTestExpectation *noticeExpectation = [self expectationWithDescription:@"登录通知返回"];
    [SHUserCenter registerNotice:SHUCenterNoticeTypeLoginSuccess handle:^(NSDictionary *data) {
    XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
    [noticeExpectation fulfill];
}];

    XCTestExpectation *expectation = [self expectationWithDescription:@"登录请求返回"];
    [SHUserCenter loginWithUserName: username password: password callback:^(NSDictionary *data) {
        XCTAssertNotNil([data objectForKey:@"data"],@"返回结果不能为nil");
        [expectation fulfill];
    }];
    [self waitForExpectations:@[noticeExpectation,expectation] timeout:self.timeout];
}

笔者推荐使用waitForExpectationsWithTimeout:handler:方法。

性能测试

需要了解一段代码执行了多长时间,可以采用如下方法

- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
        sleep(5);
    }];
}

这个方法measureBlock在执行时会执行10次,并且在执行完时会告诉你执行10次的时间。第一次执行testPerformanceExample完时会提示你输入一个该方法执行时间的参考值(baseline)作为参考。同时你也可以点击数字查看每次具体执行的结果,如下图:

20190109-7

查看单测覆盖率

首先在Edit scheme->Test->Options下勾选Code Coverage

20190109-5

然后运行一次单元测试,在report navigator上选择刚刚单测的Coverage选项,就可以看到单元测试覆盖率了

20190109-6

常用的XCTAssert断言

XCTFail(...)    强制失败断言
XCTAssertNil(expression, ...)        为nil时通过
XCTAssertNotNil(expression, ...)    不为nil时通过
XCTAssertTrue(expression, ...)    为true时通过
XCTAssertFalse(expression, ...)    为false时通过
XCTAssertEqualObjects(expression1, expression2, ...)    两个对象相等时通过
XCTAssertNotEqualObjects(expression1, expression2, ...)    两个对象不相等时通过
XCTAssertEqual(expression1, expression2, ...)        相等时通过
XCTAssertNotEqual(expression1, expression2, ...)    不相等时通过

更多的可以查看系统XCTestAssertions.h文件当中的注释

其他

在编写单元测试时如果遇到问题可以参考iOS单元测试-各种问题