iOS系统中的测试驱动开发TDD理论与实战(一)

发表于:2021-8-30 09:49

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:BBC6BAE9    来源:掘金

#
BDD
  前言
  TDD技术主要分为两个模块需求拆分技术和TDD流程。
  需求分析技术是将我们的一个完整的需求拆解成功能单元的技术TDD流程解决的问题是如何使用TDD流程完成功能单元的代码编写。

  问题描述
  火星漫步者在某块区域中根据指令进行移动,然后采集相应位置的火星数据。
  火星车收到的指令分为四类:
  探索区域信息:告知火星车,整片区域的长度(X)和宽度(Y)有多大;
  初始化信息:火星车的降落地点(x, y)和朝向(N, S, E, W)信息;
  移动指令:火星车可以前进(M);
  转向指令:火星车可以左转 90 度(L)或右转 90 度(R)。
  由于地球和火星之间的距离很远,指令必须批量发送,火星车执行完整批指令之后,再回报自己所在的位置坐标和朝向。

  一、需求分析技术

  任务拆解(1)每个单元,都需要明确输入和输出。如果你不能确定这些输入和输出,那么你现在要做的事情就是继续理解需求,或者是找产品经理同学讨论。 (2)进行实现的时候,要遵循从右往左的原则。直到需求完全得到实现。

  二、TDD流程

  红:编写测试用例确保测试不通过;
  绿:以最低的成本实现函数,确保测试用例通过;
  重构:使代码更加简洁。
  重复上述步骤,直到完成需求。
  下面我们就将以【转向】这个函数为例进行示范。转向输入输出分析。
typedef enum DIREDRTION {
    DIREDRTION_N = 0, // 北
    DIREDRTION_E, // 东
    DIREDRTION_S, // 南
    DIREDRTION_W, // 西
    DIREDRTION_UNKNOW // 未知方向
} DIREDRTION;

- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
return DIREDRTION_UNKNOW;
}

  2.1 红
  新建一个测试用例,测试用例是我们已知的事实,是我们对函数行为的预期。抽象地说就是:我们明确地知道输入将得到一个怎样的输出。
// 转向函数
// 输入:当前方向(向南)、转向命令(右转)
// 输出:转向后方向(向西)
- (void)testTurnCurDirection{
    MarsRover *rover = [[MarsRover alloc] init];
    {
        DIREDRTION direction = [rover turn:@"R" curDirection:DIREDRTION_S];
        XCTAssertTrue(direction == DIREDRTION_W,"turn:curDirection函数验证失败");
    }
}

  运行测试,测试不通过。红!

  2.2 绿
  现在我们将以最小的成本去满足这个这个case,这样可以避免我们的程序的过度设计。
// 转向函数
// 输入:当前方向(向南)、转向命令(右转)
// 输出:转向后方向(向西)
- (void)testTurnCurDirection{
    MarsRover *rover = [[MarsRover alloc] init];
    {
        DIREDRTION direction = [rover turn:@"R" curDirection:DIREDRTION_S];
        XCTAssertTrue(direction == DIREDRTION_W,"turn:curDirection函数验证失败");
    }
}

  运行测试,通过。绿!

  2.3重构
  在写代码的过程中我们往往会闻到一些代码的坏味道,如果闻到了,请立即重构。暂时没闻到,咱们接着进行...

  2.4 重复上述过程
  我们不停的增加测试用例,并且不断的以最小代价完成代码功能以通过测试。
-(DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    cmd = [cmd uppercaseString];
    if (!([cmd isEqualToString:@"L"] || [cmd isEqualToString:@"R"])) {
        return  -1;
    }
    if ([cmd isEqualToString:@"L"]) {
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_E;
        }else if (curDirection == DIREDRTION_N) {
            return DIREDRTION_W;
        }else if (curDirection == DIREDRTION_E) {
            return DIREDRTION_N;
        }else{
            return DIREDRTION_S;
        }
    }
    if ([cmd isEqualToString:@"R"]) {
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_W;
        }else if (curDirection == DIREDRTION_N) {
            return DIREDRTION_E;
        }else if (curDirection == DIREDRTION_E) {
            return DIREDRTION_S;
        }else{
            return DIREDRTION_N;
        }
    }

    return  -1;
}

- (void)testTurn{
    MarsRover *rover = [[MarsRover alloc] init];
    {
        DIREDRTION direction = [rover turn:@"L" curDirection:DIREDRTION_E];
        XCTAssertTrue(direction == DIREDRTION_N,"turn:curDirection函数验证失败");
    }
    {
        DIREDRTION direction = [rover turn:@"L" curDirection:DIREDRTION_S];
        XCTAssertTrue(direction == DIREDRTION_E,"turn:curDirection函数验证失败");
    }
    
    {
        DIREDRTION direction = [rover turn:@"L" curDirection:DIREDRTION_W];
        XCTAssertTrue(direction == DIREDRTION_S,"turn:curDirection函数验证失败");
    }
}

  2.5 重构
  目前为止,我们已经完成了“转向”函数的编写,但是我们似乎可以闻到一些代码的坏味道,无论是测试代码还是被测代码。事实上他们都是需要被重构的,此处我们拿被测代码作为演示,并且体会单元测试是如何帮助我们进行代码重构的。
  DIREDRTION_N = 0, // 北 DIREDRTION_E = 1, // 东 DIREDRTION_S = 2, // 南 DIREDRTION_W = 3 // 西
  经过观察,我们很容容易发现如下规律:
  左转:最终方向 = (当前方向 + 3) % 4 右转:最终方向 = (当前方向 + 5) % 4
  现在我们可以大刀阔斧的重构函数,因为如果我们的修改是不正确的,单元测试的case必然不会通过。重构之后的函数如下所示:
- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    cmd = [cmd uppercaseString];
    if (!([cmd isEqualToString:@"L"] || [cmd isEqualToString:@"R"])) {
        return  DIREDRTION_UNKNOW;
    }
    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;
}

  运行单元测试,测试通过。
  事实上不仅仅是业务代码需要重构,我们的测试代码也需要重新构。

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号