• 你真的会写单测吗?TDD初体验


    前言:

      昨天读到了一篇文章,讲的是TDD,即Test-Driven Development,测试驱动开发。大体意思是,它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。

      初读之时,瞬间感受到了震撼,感觉和自己之前的开发流程全都不一样,之前是由始至终,而这种思想确实以终为始。后来一查这种思想早在前几年甚至前几十年就被提出了,进而被广泛运用到了敏捷开发中。看来是自己孤落寡闻了,于是我准备将这种思想用到今后的开发中,要做的第一件事,就是温习如何写用例。

    为什么是温习?

      早在实习的时候,我们研发组就有写用例的习惯,但是随着开发逐渐熟悉,这种习惯不知不觉就被丢弃了,有页面的点点点,没页面的看逻辑。相信有很多人也像我一样,不知不觉就把这项技能丢弃了,接下来就让我们一起,去重新捡起这项技能。

    工具选择

    Junit

    对于一个Java开发工程师来说,一提到写单测,我们最先想到的,一定是Junit。下面是maven坐标

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>

    用Junit我们可以快速的,简洁的用注解进行单元测试。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:conf/core/*.xml")
    public class ObjTest {
    
      @Test
      public void testFunc(){
         //todo test          
      }
    }

    这里要注意的是@ContextConfiguration注解中的路径是Spring配置文件的位置。测试的方法必须是public的,且没有返回值。

    mockito

    mockito是一个用于模拟对象的工具,我认为他也是测试工作中必不可少的一部分,详细的介绍我推荐可以看一下:

    人生苦短,我用Mockito https://zhuanlan.zhihu.com/p/59275373

    比较不错的入门案例,它的maven坐标地址为:

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.5</version>
        <scope>test</scope>
    </dependency>

    Mock这种测试方法, 对比传统的Junit测试,有如下好处:

      1. 不用每次测试的是时候,都初始化Spring容器,采用Mock的方式模拟对象,效率高
      2. 对象间的依赖关系,可以用Mock去表达,同时,我们不关心的部分,我们都可以用Mock的方式代替(比如对象A引用对象B的某某方法,但是我们不关系对象B方法实现,只想借助方法,这个时候就可以Mock)
      3. 可以应对复杂的测试环境,比如方法调用顺序、方法调用次数等等。

    以下是Mock的一个小案例:

    @RunWith(MockitoJUnitRunner.class)
    public class MockitoTest {
        /**
         * mock对象
         */
        @Mock
        List<String> mockedList;
    
        @Before
        public void setUp() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testMock() {
            // mock对象行为
            Mockito.when(mockedList.get(0)).thenReturn("one");
            Assert.assertEquals("one", mockedList.get(0));
            // 仅仅是mock了对象的行为,实际上列表还是空的
            Assert.assertEquals(0, mockedList.size());
    
            //验证mock对象的get方法被调用过,且调用时的参数是0
            Mockito.verify(mockedList).get(0);
        }
    }

    这里在使用@Mock的时候,必须事先调用MockitoAnnotations.initMocks(this),且使用@RunWith(MockitoJUnitRunner.class)

    Jacoco

    JaCoCo是一个开源的覆盖率工具,支持多种覆盖率的统计,其中包括:

      1. 行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。
      2. 类覆盖率:度量计算class类文件是否被执行。
      3. 分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量。
      4. 方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。
      5. 指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。
      6. 圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测 试案例没有完全覆盖到这个模块。

    下面是它的maven坐标:

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.5</version>
        <scope>test</scope>
    </dependency>

    接下来我们用maven插件的方式,对jacoco进行配置

    <plugin>
         <groupId>org.jacoco</groupId>
          <artifactId>jacoco-maven-plugin</artifactId>
           <version>0.8.3</version>
           <configuration>
                <includes>
                   <include>com/**/*</include>
                </includes>
                <!-- rules裏面指定覆蓋規則 -->
                <rules>
                <rule implementation="org.jacoco.maven.RuleConfiguration">
                   <element>BUNDLE</element>
                   <limits>  
                   <!-- 指定方法覆蓋到50% -->
                   <limit implementation="org.jacoco.report.check.Limit">
                       <counter>METHOD</counter>
                       <value>COVEREDRATIO</value>
                       <minimum>0.50</minimum>
                    </limit>
                    <!-- 指定分支覆蓋到50% -->
                    <limit implementation="org.jacoco.report.check.Limit">
                        <counter>BRANCH</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.50</minimum>
                     </limit>
                     <!-- 指定類覆蓋到100%,不能遺失任何類 -->
                     <limit implementation="org.jacoco.report.check.Limit">
                        <counter>CLASS</counter>
                        <value>MISSEDCOUNT</value>
                        <maximum>0</maximum>
                      </limit>
                      </limits>
                  </rule>
                  </rules>
             </configuration>
             <executions>
                 <execution>
                    <id>pre-test</id>
                      <goals>
                           <goal>prepare-agent</goal>
                       </goals>
                 </execution>
                 <execution>
                       <id>post-test</id>
                       <phase>test</phase>
                       <goals>
                           <goal>report</goal>
                       </goals>
                 </execution>
           </executions>
      </plugin>

      这里值得注意的是<include>com/**/*</include>指的是class文件的位置。做完这些以后,我们就可以生成报表了。因为我们是用maven插件的方式进行配置的,所以如果我们使用idea进行开发的时候,就可以看到右侧maven一栏中出现了jacoco插件

     最常用的就是这两个,一个是检查配置是否正确,第二个是用来将exec文件,生成index.html用来进行观察覆盖率。

    我们先执行maven中的test指令,这时,我们在target中就可以看到一个jacoco.exec文件。

    有了这个jacoco.exec文件,就可以使用jacoco的report方法,来生成文件。

     右键index.html文件,选择Reveal in Finder(Mac),windows也是类似,打开文件磁盘的位置。

     可以看到,由于这个项目之前没有几个单测,所以覆盖率特别低。点开之后,就可以看到具体的代码,非常的方便。

     最后今天配置jacoco的时候,踩了2个坑:

    1 用idea进行开发的同学。使用jacoco的时候,不要勾选这个按钮,它会跳过你测试阶段的代码执行,进而不会生成jacoco.exec文件。

     2 保证自己测试代码没有错误(尤其是项目中,由于代码更新,测试用例没有更新,导致的测试不可用)

    这里的现象是虽然可以生成jacoco.exec 文件,而且可以report成文档,但是打开之后发现,代码覆盖率都是0。

    最后:

    希望大家都可以保持写测试用例的好习惯,谢谢

  • 相关阅读:
    CCF NOI1032 菱形
    CCF NOI1031 等腰三角形
    CCF NOI1030 角谷猜想
    CCF NOI1029 信息加密
    CCF NOI1028 判断互质
    CCF NOI1027 数字之和
    CCF NOI1026 表演打分
    CCF NOI1025 统计奖牌
    CCF NOI1024 因子个数
    CCF NOI1023 最大跨度
  • 原文地址:https://www.cnblogs.com/nedulee/p/11863605.html
Copyright © 2020-2023  润新知