03|TDD演示(3):按测试策略重组测试

TDD 学习笔记 | 极客时间 | 徐昊·TDD 项目实战 70 讲

🏆 课程原文链接 🍿 源码地址

测试代码需要重构

  • 重构生产代码的时候不要改测试代码
  • 重构测试代码的时候不要改生产代码

随着功能迭代,测试代码也会冗余,需要及时重构,消除冗余代码

实际开发过程:

  • 按功能写出出任务列表
  • 先按照任务列表完成了一块功能点
  • 发现了坏味道,开始重构
  • 通过重构引入了新的组件,改变了架构
  • 于是剩余的任务列表随之改变
  • 陆续完成这些任务,发现不一致的测试策略,重组测试

任务列表

  • 是随代码结构(重构)、测试策略(在哪个范围内测试)、代码实现情况(存在哪些缺陷)等因素而动态调整的列表
  • 它的内容体现了我们最新的认知
  • 它的变化记录了我们认知改变的过程

提前将 Option 对象提取出 flag 的代码在 bad path 翻车了

  • 因为获取不到原始输入参数,无法给出友好提示

过早优化是万恶之源 —— 克努特优化原则,源于Donald Knuth的书《计算机编程艺术》(最早由Tony Hoare提出)

BooleanOptionParser.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 徐昊老师的代码
public Object parse(List<String> arguments, Option option) {
    int index = arguments.indexOf(flag);
    if (!arguments.get(index + 1).startsWith("-")) {
        throw new TooManyArgumentsException(option.value);
    }
    return index != -1;
}

// 我的代码
public Object parse(List<String> arguments, String flag) {
    int index = arguments.indexOf(flag);
    if (!arguments.get(index + 1).startsWith("-")) {
        throw new TooManyArgumentsException("拿不到原始输入参数❗️🙀🙀🙀");
    }
    return index != -1;
}

视频中代码的坏味道

if 没有 “{}”

BooleanOptionParser.java

1
2
3
4
5
public Object parse(List<String> arguments, Option option) {
    int index = arguments.indexOf("-" + option.value());
    if (!arguments.get(index + 1).startsWith("-")) throw new TooManyArgumentsException(option.value());
    return index != -1;
}

C 语言伪代码

1
2
3
4
5
6
7
    if ((error = doSomething()) != 0)
        goto fail;         
        goto fail;    
    if ((error= doMore()) != 0)        
        goto fail;
fail:    
    return error;

吞灭异常信息,无法追溯错误原因,导致排错困难

Args.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public static <T> T parse(Class<T> optionsClass, String... args) {
    Constructor<?> constructor = optionsClass.getDeclaredConstructors()[0];
    try {
        List<String> arguments = Arrays.stream(args).toList();
        Object[] values = Arrays.stream(constructor.getParameters())
                .map(it -> parseOption(arguments, it))
                .toArray();

        return (T) constructor.newInstance(values);
    } catch (Exception e) {
        // 此处视频中没有打印异常信息(吞灭了异常,无法追溯错误原因)
        // 第一次遇到数组越界的问题时,如果不听视频中的介绍
        // 从错误日志中只能看到测试结果不匹配的失败信息,无法定位错误原因
        e.printStackTrace();
        throw new RuntimeException();
    }
}
comments powered by Disqus