junit 单元测试 规范

一、测试准则

  1. 必须遵守AIR原则

    • A(自动化):单元测试应该是全自动执行的,并且非交互。单元测试中不准使用System.out来进行人工验证,必须使用assert来验证。
    • I (独立性):单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。
      **反例:**method2需要依赖method1的执行,将执行结果作为method2的输入。
    • R(可重复):单元测试是可以重复执行的,不能受到外界环境的影响。
  2. 保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别。

  3. 核心业务、核心应用、核心模块的增量代码必须确保单元测试通过。
    说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。

  4. 单元测试的基本目标:语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都要达到100%。
    **说明:**在工程规约的应用分层中提到的DAO层,Manager层,可重用度高的Service,都应该进行单元测试。

  5. 对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的,或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。
    **反例:**删除某一行数据的单元测试,在数据库中,先直接手动增加一行作为删除目标,但是这一行新增数据并不符合业务插入规则,导致测试结果异常。

  6. 确保测试与时间无关,尽可能避免使用可能过期的数据;此类数据应手动或以编程方式刷新。

  7. 在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例。

  8. 单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项目提测前完成单元测试。

  9. 不要在单元测试中打印任何内容。

  10. 不要在单元测试类的构造函数中初始化;使用@Before方法代替。

  11. 在测试类中,不要声明方法抛出任何特定类型的异常

    说明:声明它们抛出一种特定类型的异常的测试方法非常脆弱,因为每当被测试的方法发生变化时,都必须对其进行更改。

  12. 不要在单元测试中使用Thread.sleep

  13. 不要对单元测试存在如下误解:

    • 那是测试同学干的事情。
    • 单元测试代码是多余的。
    • 单元测试代码不需要维护。一年半载后,那么单元测试几乎处于废弃状态。
    • 单元测试与线上故障没有辩证关系。好的单元测试能够最大限度地规避线上故障。

二、结构规范

  1. 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。
    **说明:**源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。

  2. 测试类命名规范:被测试的业务+Test、被测试的接口+Test、被测试的类+Test

    正例:例如当前的测试类为MessageLog该类的测试用例应为MessageLogTest。

  3. 测试用例命名规范:测试用例命名应做到简洁全面,可描述。避免使用test1、test2没有含义的名称。其次需要有必要的函数方法注释。

    推荐的命名法:MethodName_StateUnderTest_ExpectedBehavior

    • MethodName 被测试的方法、一组方法或者一组类。
    • StateUnderTest 测试进行的条件以及状态。
    • ExpectedBehavior 在测试场景指定的条件下,你对被测试方法行为的预期。

    正例:sum_simpleValues_calculated

    这种方式可以在不用看方法细节的情况明白方法要做的事情。同时,还建议在MethodName前加test,这样方便在ide中快速搜索到你要找的代码

  4. 测试开发包,测试包保持和被测包一致。

三、编码原则

  1. 编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。

    • B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
    • C:Correct,正确的输入,并得到预期的结果。
    • D:Design,与设计文档相结合,来编写单元测试。
    • E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果。
  2. 和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者对单元测试产生的数据有明确的前后缀标识。
    正例:在RDC内部单元测试中,使用RDCUNIT_TEST的前缀标识数据。

  3. 对于不可测的代码建议做必要的重构,使代码变得可测,避免为了达到测试要求而书写不规范测试代码。

  4. 为了更方便地进行单元测试,业务代码应避免以下情况:

    • 构造方法中做的事情过多。
    • 存在过多的全局变量和静态方法。
    • 存在过多的外部依赖。
    • 存在过多的条件语句。

    说明:多层条件语句建议使用卫语句、策略模式、状态模式等方式重构。

  5. 编写测试时考虑语言环境,例如:

    对于下面的日期测试。

    Date date = DateFormat.getInstance ().parse ("dd/mm/yyyy");
    

    但是该代码不能在使用不同语言环境的机器上工作。因此,下面代码将会好很多

    Calendar cal = Calendar.getInstance ();
    Cal.set (yyyy, mm-1, dd);
    Date date = Calendar.getTime ();
    
  6. 利用JUnit的assert / fail方法和异常处理获得干净的测试代码

    反例

    public void exampleTest () {
       try {
          // do some test
       } catch (SomeApplicationException e) {
          fail ("Caught SomeApplicationException exception");
       }
    }
    

    正例

    public void exampleTest () throws SomeApplicationException {
       // do some test
    }
    

    说明:JUnit自动捕获异常。它认为未捕获的异常是错误

  7. 使用各种断言方法以一种更简单的方式表达你的意图。

    反例

    assert (creds == 3);
    

    正例

    assertEquals ("The number of credentials should be 3", 3, creds);
    

    说明:上面的示例对代码阅读器有用得多。如果断言失败,它将为测试人员提供更多信息。使用assertSame()测试两个引用指向同一个对象。使用assertEquals()测试两个对象是相等的。

  8. 不要假设测试用例中测试的运行顺序,不应该假定将以任何特定顺序调用测试。

    考虑以下代码段:

    public class SomeTestCase extends TestCase {
    public SomeTestCase (String testName) {
        super (testName);
    }
    public void testDoThisFirst () {
        ...
    }
    public void testDoThisSecond () {
    }
    }
    

    说明:在此示例中,不确定使用反射时JUnit将以任何特定顺序运行这些测试。因此,除非将测试设计为按任何顺序运行,否则在不同的平台和Java VM上运行测试可能会产生不同的结果。避免时间耦合将使测试用例更加健壮,因为顺序的更改不会影响其他测试。如果测试是耦合的,则可能很难发现由次要更新导致的错误。

  9. 不要使用测试用例构造函数来设置测试用例。
    反例:

    public class SomeTest extends TestCase
       public SomeTest (String testName) {
          super (testName);
          // Perform test set-up
       }
    }
    
  10. 不要从文件系统上的硬编码位置加载数据

    反例

    public void setUp () {
       FileInputStream inp ("C:\\TestData\\dataSet1.dat");
    }
    

    说明:上面的代码依赖于C:\ TestData路径中的数据集。考虑在两种情况下是不正确的:1.测试人员没有空间将测试数据存储在C:上而是将其存储在另一个磁盘上;2.这些测试可能是在其他平台(例如Unix)上运行的。

    常见的方法将测试放在与源代码相同的位置。如:

    public void setUp () {
       FileInputStream inp ("dataSet1.dat");
    }
    
  11. 使用最合适的断言方法

    JUnit的断言方法有许多,应该了解最新版本的JUnit中的断言,并使用最合适的断言来获得最具可读性的测试代码。例如:

    • 使用assertTrue(classUnderTest.methodUnderTest())而不是assertEquals(true, classUnderTest.methodUnderTest())
    • 使用assertEquals(expectedReturnValue, classUnderTest.methodUnderTest())而不是assertTrue(classUnderTest.methodUnderTest().equals(expectedReturnValue))
    • 使用assertEquals(expectedCollection, classUnderTest.getCollection())而不是声明集合的大小和集合的每个成员。

四、如何测试dao层

  1. 对于dao的测试,我们应根据实际需要在测试类中继承 AbstractTransactionalJUnit4SpringContextTests 和AbstractJUnit4SpringContextTests中 的一个类。

    • AbstractTransactionalJUnit4SpringContextTests提供了数据库自动回滚,也就是说测试前和测试后数据库是一样的。
    • AbstractJUnit4SpringContextTests不提供数据库自动回滚,测试会破坏数据库。
  2. 如果你觉得每个测试用例都要配置spring环境很麻烦的话,可以先建立一个父类:BasicTest,然后继承该类即可。

  3. 如果你使用的是springboot + junit,那么测试环境可以这样配置

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = DemoApplication.class)
    public class BasicTest {
    }
    
    • RunWith中如果要使用SpringRunner你的junit版本需要在4.12以上,若在4.12以下,您应该这样写:@RunWith(SpringJUnit4ClassRunner.class),更多参数可自行百度。
    • SpringBootTest中classes的值为springboot的启动项,这里也可以省略。
  4. 理想情况下,测试应使用与应用程序相同的配置。是可能会有一些仅针对单元测试的更改。要解决此问题,您应该创建另一个特定于测试的配置文件,并添加/覆盖特定于测试的配置更改。

    例如在主应用程序中,如果是配置文件application-context.xml,则应创建另一个文件application-context-test.xml,并将原始配置导入到该文件的顶部。然后覆盖您可能需要的bean定义。

    application-context-test.xml

    <beans ····>
        <import resource="application-context.xml"/>
       <bean>要覆盖的bean</bean>
    </beans>                                
    

五、如何测试controller层

  1. .验证HTTP请求匹配

    我们应测试控制器应响应某些URL,HTTP方法和内容类型,即对控制器的请求方式,请求路径,参数类型等进行验证,保证控制器能准确的拦截非法数据。

  2. 输入验证

    假如实体类用了@NotNUll来拒绝null值,像这样

    public class UserResource {
    
      @NotNull
      private final String name;
    
      @NotNull
      private final String email;
        
    }
    

    此时就需要对控制器中的参数进行校验,同时如果控制器对参数添加了一些注解,如@Valid,也需要进行测试。

  3. 业务逻辑测试

    即我们需要测试方法是否按预期方式调用了业务逻辑。

  4. 验证输出序列化

    调用业务逻辑后我们希望控制器将结果映射到JSON字符串并将其包含在HTTP响应中,即判断返回的数据是否为我们期望的数据。

  5. 异常验证测试

    通常,如果发生异常,则控制器应返回某个HTTP状态。400,如果请求有问题,500,如果出现异常,依此类推。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值