美文网首页
单元测试最佳实践

单元测试最佳实践

作者: JJNile | 来源:发表于2023-08-12 18:08 被阅读0次

    原则

    • [F]AST 快速性
    • [I]solate 隔离性
    • [R]epeatabel 可重复性
    • [S]elf-Validating 自验证性
    • [T]imely 及时性

    Fast 快速

    单元测试应该尽量保持简单和快速,应该做到毫秒级

    需要考虑输入数据的大小、数量和质量对代码性能的影响,并对测试用例的执行时间进行评估。

    • 采用内存数据库
    • 依赖的外部接口,采用 Mock 的方式进行测试

    Isolate 隔离

    测试用例应该是独立的,不受其他测试用例的影响,不依赖其他单元测试的执行顺序,不依赖外部资源。

    • 对于依赖的外部接口,外部存储都是用 Mock 解决
    • 对于 DI 框架中所依赖的类,直接注入到内存
    • 一个单元测试应该关注与逻辑的一个方面(存在多个断言应思考是否真正需要,真正需要是否抽离新的测试用例方法)

    多关注单测的隔离性,同时也会驱动类、方法实现单一职责(SRP)

    Repeatabel 可重复性

    每次都是重复的入参和结果,运行都是固定不变的

    不受外界的影响,如:

    • 时间,经典的例子就是判断时间的业务中,某些时间运行成功,某些时间运行失败

    Self-Validating 自验证性

    必须使用断言 Assert 方式来进行正确性验证,而不是 var_dump、System.out、Println 等输出进行人肉校验。

    单测必须走持续集成&自动化测试,而不是人工上线运行。

    Timely 及时性

    单元测试应该具有及时性,先于开发而不是后续开发完在补充。

    如何限制后续补充单元测试的坏习惯:

    • 代码 review 时查看
    • git 提交阶段或服务部署阶段使用工具限制

    除了 FIRST 原则还有 AIR 原则、ASCII 原则等,具体都没啥大区别,可参考那些年,我们写过的无效单元测试_阿里云云栖号的博客-CSDN 博客

    其他准则

    BCDE 原则,以保证被测试模块的交付质量。

    • B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。

    • C:Correct,正确的输入,并得到预期的结果。

    • D:Design,与设计文档相结合,来编写单元测试。

    • E:Error,强制错误信息输入(如:非法数据、异常流程、业务允许外等),并得到预期的结果。

    • 合理的命名测试用例:确保每个方法只测试 "被测类" 的一个明确特性, 并相应的命名测试方法. 典型的命名俗定是 test[what], 比如 testSaveAs(), testAddListener(), testDeleteProperty()

    • 避免过度断言:永不失败的测试没有价值,一个测试应该只有一个失败的原因。

    • 数据操作有完整流程,且数据有标记:不应该手动在数据库插入数据,而是由程序插入或者导入数据的方式来准备数据,并且自动回滚机制,不给数据库造成脏数据,如无法避免产生的数据有明确的前后缀标识。

    用例结构

    一般都是一个三段式的用例结构,目前较常见的就是 AAA 和 GWT,整体测试结构时相同的。如果说两者有什么区别的话,那区别在于 AAA 的构思更倾向于技术实现,GWT 更倾向于业务流程。

    与其说 AAA 与 GWT 区别,不如说 TDD(Test-Driven Development) 和 BDD(Behavior-Driven Development),区别。

    AAA

    Arrange-Act-Assert 是单元测试时的常见模式。顾名思义,它由三个主要操作组成:

    • Arrange:准备测试环境,包括设置对象的状态、参数、依赖项等。
    • Act:执行测试代码,即调用要测试的方法或代码块。
    • Assert:验证测试结果是否符合预期,即判断输出结果是否正确或符合要求。

    例如,对于以下方法的测试:

    public int add(int x, int y) {
        return x + y;
    }
    

    使用 AAA 方法的测试代码如下:

    @Test
    public void testAdd() {
        // Arrange
        int x = 3;
        int y = 4;
        MyClass myClass = new MyClass();
    
        // Act
        int result = myClass.add(x, y);
    
        // Assert
        assertEquals(7, result);
    }
    

    GWT

    Given-When-Then

    • Given:设置测试环境,包括设置对象的状态、参数、依赖项等。
    • When:执行测试代码,即调用要测试的方法或代码块。
    • Then:验证测试结果是否符合预期,即判断输出结果是否正确或符合要求。

    例如,对于以下方法的测试:

    public String greeting(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Name cannot be null");
        }
        return "Hello, " + name + "!";
    }
    

    使用 GWT 方法的测试代码如下:

    @Test
    public void testGreeting() {
        // Given 准备用例所需的上下文
        String name = "Alice";
        MyClass myClass = new MyClass();
    
        // When 调用待测的函数
        String result = myClass.greeting(name);
    
        // Then 断言
        assertEquals("Hello, Alice!", result);
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void testGreetingWithNullName() {
        // Given
        String name = null;
        MyClass myClass = new MyClass();
    
        // When
        myClass.greeting(name);
    
        // Then (expecting exception)
    }
    

    参考

    二、优秀单元测试的五个特征 FIRST - 纪玉奇 - 博客园
    单元测试准则
    要对自己写的代码负责 | 蓝士钦
    有效的单元测试之一--代码坏味道(读书笔记精华带源码)
    unit-test-specification/README.md at master · cyneck/unit-test-specification · GitHub

    相关文章

      网友评论

          本文标题:单元测试最佳实践

          本文链接:https://www.haomeiwen.com/subject/hzwxmdtx.html