[S01E02] iOS单元测试

Posted 测试baby

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[S01E02] iOS单元测试相关的知识,希望对你有一定的参考价值。

what:什么是单元测试
how: 如何进行单元测试
why: 为什么要做单元测试

一、什么是单元测试

1、单元测试

单元可以简单地理解为一个方法,那么单元测试就是针对方法的测试

2、单元测试在测试中所处的位置

一般来讲,小型测试用来保障代码质量,中、大型测试用来保障产品质量。

测试金字塔从下往上依赖于来越多,但是我们对整个产品质量的信心是越来越强的。我们需要为单元测试提供mock环境。
一般的,单元测试不依赖任何外部环境、网络环境、数据库环境。

单元测试是整个测试金字塔的基石。在整个测试中占据非常重要的位置。

参考:《Google软件测试之道》

二、如何进行单元测试

2.1 如何给项目新增单元测试Scheme

如果我们在创建项目的时候没有勾选include Tests,那我们就需要给项目新增测试的Scheme

2.2 新增一个测试用例

在新建的DemoTests下面的DemoTests.m中新增一条测试用例,测试用例必须是实例方法,返回值为void并且以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.
}

2.3 常用断言

XCTFail(@"Fail");
XCTAssertNil(@"not nil string", @"string must be nil");
XCTAssertNotNil(@"not nil string", @"string can not be nil");
XCTAssert((2 > 2), @"expression must be true");
XCTAssertTrue(1, @"Can not be zero");
XCTAssertFalse((2 < 2), @"expression must be false");
XCTAssertEqualObjects(@"1", @"1", @"[a1 isEqual:a2] should return YES");
XCTAssertNotEqualObjects(@"1", @"2", @"[a1 isEqual:a2] should return NO");
XCTAssertEqual(1, 2, @"a1 = a2 shoud be true"); 
XCTAssertEqual(str1, str2, @"a1 and a2 should point to the same object"); 

2.4 测试文件中各个函数执行的顺序

控制台输出的结果如下

2021-07-08 14:29:10.782883+0800 Demo[19597:31722127] 🍺 +setUp
2021-07-08 14:29:10.784474+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.784667+0800 Demo[19597:31722127] 🍺 testExample1
2021-07-08 14:29:10.784857+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.785971+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.786121+0800 Demo[19597:31722127] 🍺 testExample2
2021-07-08 14:29:10.786270+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.787399+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.787548+0800 Demo[19597:31722127] 🍺 testExample3
2021-07-08 14:29:10.787700+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.788271+0800 Demo[19597:31722127] 🍺 +tearDown

值得注意的是,testExample1、testExample2、testExample3的执行并没有一定的顺序。

2.2 如何在Xcode中查看单元测试覆盖率

单元测试的覆盖率问题默认是关闭的,需要在Xcode中打开


三、为什么要做单元测试

  • 检查代码逻辑

  • 验证边界条件

  • 保障重构的顺利进行

3.1 检查代码逻辑

单元测试最基本的功能就是验证我们函数的逻辑是否符合预期。

3.2 验证边界条件

点赞数显示字符串转换,例如本条视频的真实点赞数字是23848999,我们显示2384.8

/// 将点赞的数量转换成字符串
/// @param count 点赞数量
/// @discussion 数字大于等于1万时,保留一位小数点
/// @discussion 数字低于1万时,展示真实数字
- (NSString *)descForLikeCount:(NSInteger)count{
    NSString *desc = nil;
    if (count >= 10000) {
        desc = [NSString stringWithFormat:@"%.1f万", (count / 10000.f)];
    }else{
        desc = [NSString stringWithFormat:@"%ld", count];
    }
    return desc;
}

上面的函数乍一看并没有什么问题,但是当我们输入99999的时候函数的返回值却是10.0万并不符合我们的预期。类似这样的错误甚至在代码评审(Code Review)中也很难被发现。

3.3 保障重构的顺利进行

定义方向枚举

typedef enum{

    DIREDRTION_UNKNOW = -1, // 未知方向
    DIREDRTION_N = 0, // 北
    DIREDRTION_E = 1, // 东
    DIREDRTION_S = 2, // 南
    DIREDRTION_W = 3 // 西

}DIREDRTION;

重构前的函数:

- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    if ([cmd isEqualToString:@"L"]){
        if (curDirection == DIREDRTION_N) {
            return DIREDRTION_W;
        }
        if (curDirection == DIREDRTION_W) {
            return DIREDRTION_S;
        }
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_E;
        }
        if (curDirection == DIREDRTION_E) {
            return DIREDRTION_N;
        }
    }else if ([cmd isEqualToString:@"R"]){
        if (curDirection == DIREDRTION_N) {
            return DIREDRTION_E;
        }
        if (curDirection == DIREDRTION_W) {
            return DIREDRTION_N;
        }
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_W;
        }
        if (curDirection == DIREDRTION_E) {
            return DIREDRTION_S;
        }
    }
    return  DIREDRTION_UNKNOW;
}

这时候我们需要一些单元测试作为保障

随着工作的深入,我们发现了一个规律

左转:最终方向 = (当前方向 + 3) % 4

右转:最终方向 = (当前方向 + 5) % 4
重构后的函数:

- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    if ([cmd isEqualToString:@"L"]) {
        DIREDRTION direction = (curDirection + 3) % 4;
        return direction;
    }
    if ([cmd isEqualToString:@"R"]) {
        DIREDRTION direction = (curDirection + 5) % 4;
        return direction;
    }
    return  DIREDRTION_UNKNOW;
}

重构完成之后,运行单元测试。依旧全部通过。证明此次重构并没有引入新的问题。

到目前为止,我想你已经单元测试坚定的拥护者了。但是我们之前说过,单元测试并不依赖网络环境、系统时间、数据库,只是单纯的对函数功能的测试。


这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

关注我的微信公众号:【伤心的辣条】免费获取~

软件测试技术交流群: 902061117 同行大牛交流学习解惑!

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!

以上是关于[S01E02] iOS单元测试的主要内容,如果未能解决你的问题,请参考以下文章

单元测试 NPE,当我添加片段自定义转换时

iOS单元测试的那些事儿

iOS 单元测试

四则运算单元测试

IOS-使用XCODE自带的单元测试UnitTest

cocoapods 库的代码覆盖率 - iOS 单元测试