欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《有用的单元测试》 读书笔记

程序员文章站 2022-06-01 16:43:22
...

有效的测试

第4章 断言

  • 看见魔数的时候,或者!=== 这样的判断的时候,要考虑是否足够抽象,
  • 一个测试应该只有一个失败的原因,过度assert
  • 不要过度断言,任何一点小的改变都会使得测试失败
  • 不要在断言中使用位运算
  • 准备-执行-断言
  • 内敛数据或者逻辑,不要轻易使用外部数据文件
  • 不要使用魔数
  • 冗长的setup,可以封装到其他的private方法中

第5章 可维护性

  • 重复:语法重复和语义重复,使用参数化测试减少模版重复代码
  • 参数化测试,不能精准定位到到底是哪一组测试数据出了问题,需要在断言的失败消息中表明当前测试数据
  • 尽量避免条件逻辑,if、switch、while、for,
  • 因为网络、io、时间戳、随机数、多线程等导致测试不确定性失败
  • 使用Stream流代替File,因为File类是final的,不容易mock
  • 避免硬编码绝对路径,使用相对路径,将所有测试文件放到classpath下面,
  • 能不用物理文件就不用
  • 使用countdownlatch 代替Thread.sleep获取线程正确的结束时间,确保线程执行完

第6章 可信赖

  • 不要遗留注释掉的测试,要么重构,要么删除
  • 避免永不失败的测试,尝试使测试失败,expected,注意try-catch导致测试永远不抛出异常
  • 跟平台相关,可以使用assumeTrue,
  • 存在有条件的测试,可以将if换成断言

第7章 指南

  • 避免静态初始化,导致无法实例一个对象
  • 避免复杂的private方法,尝试短小的private方法,或者单独一个public方法或者一个类
  • 避免final方法,final是为了覆盖,以及内联带来性能优势,但是final方法不易测试,
  • 避免将可能会打桩的方法设置为static,比如产生随机数的方法
  • 在方法中,慎用new产生新的实例,这样导致测试很难覆盖该对象
  • 避免在构造函数中添加复杂的逻辑,确保构造函数中的代码,不会在单元测试中替换他们,可以将其抽取到子类容易覆盖的protected方法中
  • 避免单例,也可以单例的getinstance()方法,返回一个接口,而不是类接口更容易伪造
  • 降低代码重复时,多考虑使用组合,而不是继承

第9章 加速执行测试

  • 通过maven的几个插件,能够查看测试执行的时间和每个单元测试执行的时间,sufire,找到最慢的
  • countdownlatch替代sleep
  • 注意测试基类和setup、teardown方法中是否有大量的代码,仅仅只是针对某个测试,而其他测试用不到
  • 执行一个测试的顺序,
    • 找到所有父类和本类的@beforclass 方法,
    • 找到所有父类和本类的@before方法
    • 执行测试
    • 追踪父类以及本类的@after方法
    • 追踪父类以及本类的@afterclass方法
  • 当一个@befor中的方法只用被执行一次,可以考虑将其放到@beforclass
  • 保持本地运行,使用mock避免网络、io等
  • 最小化对数据库的访问
  • 考虑关闭日志,测试失败了之后,再开启执行一次
  • 如果cpu够用,可以考虑并行构建和并行测试
    • 并行构建
      • mvn -T {线程数} clean install
      • mvn -T 1C clean install
    • 并行测试
      • 在suefire插件中配置并行测试

junit

测试类

  • 测试方法必须是public void修饰,没有任何参数,并使用@Test注解
  • 测试方法的生命周期
    • 实例化测试类的一个实例
    • 调用父类以及当前类所有的setup
    • 执行测试方法
    • 调用父类以及当前类所有的teardown
  • setup和teardown可以在本类中定义,也可以在父类中定义
  • 多个setup和teardown都会被执行,但是不保证执行顺序
  • @beforclass 和 @afterclass针对测试类不是测试方法,每个测试类只会被执行一次

断言

  • 断言api org.junit.Assert
  • @Test(expected="异常")替代try-catch
  • 如果想要检查异常的信息,要回滚到try-catch
  • 使用try-catch要注意在try中最后调用fail(),防止不抛出异常测试通过
  • assertThat(someobj,matchthiscondition),matchthiscondition是Hamcrest匹配器,并且可以自定义实现Hamcrest的Matcher接口,

扩展junit

  • class-level注解@RunWith选择运行器,比如参数化测试@Runwith(Parameterized.class),通常默认runner在最后
  • 为单个测试方法配置超时@Test(timeout=x)
  • 为一个测试类中所有的方法配置超时,
@Rule
public MethodRule globalTimeout = new Timeout(20)
  • 除了@Test(expected=x),还以使用ExpectedException为单个方法甚至是测试类中的所有方法设置异常
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void thispassed() {}

@Test
public void throwExpectedException() {
    thrown.expected(Exception.class);
}
  • 临时文件夹

@Rule
public TemporaryFolder folder = new TemporaryFolder();
相关标签: unit test