如何更好的做单元测试并用它来提升代码质量(下)

简介: ## 现代化的spring-test使用方式 > 以下例子可以在`javaconfig-spring-test`中找到。        在`classic-spring-test`中演示的单元测试,还是用配置文件的方式,但是从Spring4之后,官方就鼓励使用Java的方式对spring进行配置,而不是用以前那

现代化的spring-test使用方式

以下例子可以在javaconfig-spring-test中找到。

       在classic-spring-test中演示的单元测试,还是用配置文件的方式,但是从Spring4之后,官方就鼓励使用Java的方式对spring进行配置,而不是用以前那样的xml配置形式了,因此我们基于注解可以来简化单元测试的编写,我们称之为现代化的spring-test方式。

修改单元测试

       测试不用继承AbstractJUnit4SpringContextTests,通过注解即可,然后对于bean的配置,可以通过Java配置风格完成

注解

       使用RunWithContextConfiguration配置即可将一个类声明为支持Spring容器的测试用例。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MemberJavaConfigTest.MemberServiceConfig.class)
public class MemberJavaConfigTest {
}
注解 说明
RunWith 该注解是junit提供的,表示用那种方式来执行这个测试,这里是SpringRunner,由spring-test提供
ContextConfiguration 对测试的Spring容器的配置,比如:配置的位置等

配置与示例

       通过注解可以声明按照何种方式去执行测试,以及测试的Spring容器如何组装,但是还或缺在Spring容器中如何配置Bean,以前这是通过xml来进行配置的。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MemberJavaConfigTest.MemberServiceConfig.class)
public class MemberJavaConfigTest {

    @Autowired
    private MemberService memberService;

    @Test
    public void insert_member() {
        System.out.println(memberService.insertMember("windowsxp", "abc123"));
        Assert.assertNotNull(memberService.insertMember("windowsxp", "abc123"));
    }

    @Configuration
    static class MemberServiceConfig {

        @Bean
        public MemberService memberService(UserDAO userDAO) {
            MemberServiceImpl memberService =  new MemberServiceImpl();
            memberService.setUserDAO(userDAO);
            return memberService;
        }

        @Bean
        public UserDAO userDAO() {
            UserDAO mock = Mockito.mock(UserDAO.class);
            Mockito.when(mock.insertMember(Mockito.any())).thenReturn(System.currentTimeMillis());
            return mock;
        }
    }
}

       可以看到只需要有一个类,被注解了Configuration,该类就是一个配置类型,而这种Java Config Style已经是Spring官方推荐的方式了。

       Bean注解类似xml中的bean标签,这里配置了两个Bean一个MemberService的实现,另外一个是mock的UserDAO。其中对MemberService的配置需要依赖UserDAO

       剩下的测试过程就和之前classic-spring-test完全一致了,可以看到新的方式没有了恼人的xml配置,变得更加直接和高效。

SpringBoot环境下的测试方法

以下例子可以在spring-boot-test中找到。

       Spring框架实际上是依靠SpringBoot完成了续命,由它焕发了第二春,打开了一个全新的战场。在今天微服务大放异彩的环境下,针对SpringBoot的测试也会有所不同。

       SpringBoot实际是用来启动你的应用,所以它会有配置以及一系列约定大于配置的环境准备,所以需要依赖spring-boot-test支持来完成单元测试。

修改单元测试

       如果需要在单元测试启动时启动SpringBoot,需要做一下相关的配置,增加一些注解。

依赖

       增加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-test</artifactId>
    <scope>test</scope>
</dependency>

注解

       和JavaConfig的方式非常类似,通过注解可以声明该测试是SpringBootTest,并且可以指定运行的SpringBoot容器的配置。

@SpringBootTest(classes = SpringBootMemberTest.Config.class)
@TestPropertySource(locations = "classpath:test-application.properties")
@RunWith(SpringRunner.class)
public class SpringBootMemberTest {
注解 说明
SpringBootTest 描述了该SpringBoot单元测试是根据哪个配合来启动容器
TestPropertySource 应用的配置使用哪个

配置

       通过注解可以声明按照何种方式去执行测试,以及测试的Spring容器如何组装,还或缺在Spring容器中如何配置Bean。

@SpringBootTest(classes = SpringBootMemberTest.Config.class)
@TestPropertySource(locations = "classpath:test-application.properties")
@RunWith(SpringRunner.class)
public class SpringBootMemberTest {

    @Autowired
    private Environment env;
    @MockBean
    private UserDAO userDAO;
    @Autowired
    private MemberService memberService;

    @Test
    public void environment() {
        Assert.assertEquals("Alibaba", env.getProperty("brand-owner.name"));
    }

    @Before
    public void init() {
        Mockito.when(userDAO.insertMember(Mockito.any())).thenReturn(System.currentTimeMillis());
    }

    @Test
    public void insert_member() {
        System.out.println(memberService.insertMember("windowsxp", "abc123"));
        Assert.assertNotNull(memberService.insertMember("windowsxp", "abc123"));
    }

    @Configuration
    static class Config {

        @Bean
        public MemberService memberService(UserDAO userDAO) {
            MemberServiceImpl memberService =  new MemberServiceImpl();
            memberService.setUserDAO(userDAO);
            return memberService;
        }
    }

}

       可以看到新增了一个注解MockBean,这个用来帮助我们创建一个Mock的UserDAO,而不用通过编码来进行创建,回忆之前在classic以及javaconfig中的Mock方式,都需要调用Mockito.mock(Class type)方法来创建一个Mock对象,而在SpringBootTest中就不需要了,直接在成员变量上增加MockBean的注解就可以了。

       同时可以看到在单元测试中增加了一个注入属性,Environment,它代表Spring运行的环境,可以从中获取配置,以下是test-application.application中的内容:

brand-owner.name=Alibaba
brand-owner.company=Alibaba-inc.

       在environment测试方法中,可以访问测试的配置内容,从这里可以看到SpringBootTestspring-test基础上,除了启动一个Spring容器,还准备好了一个SpringBoot运行时环境。

       但是从侧面上讲,使用SpringBootTest就依赖了运行时环境,这不是一个好的选择,所以在大多数情况下,对于代码的单元测试spring-test就可以完全应对。

单元测试覆盖率

       就像刻意的刷分数一样,单元测试覆盖率也是一个我们追求的目标,当单元测试行覆盖率超过70%的时候,整个项目的质量会很不错。持续稳定的单元测试覆盖率,会保障一个应用一直处于较稳定的状态,后续投入维护的资源会降低。

       在不少IDE中,如:IDEA,都内置了统计单元测试的工具,只需要按照package运行测试即可,在这里我们不依赖具体的IDE,而是用maven插件来做。

jacoco

       该插件对java8的语法支持较好,在pom文件中增加配置。

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.1</version>
    <executions>
        <execution>
            <id>prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>post-unit-test</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
            <configuration>
                <dataFile>target/jacoco.exec</dataFile>
                <outputDirectory>target/jacoco-ut</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

       当运行mvn test时会生产单元测试覆盖率报告。

位置一般在项目的 target/jacoco-ut 目录下。

覆盖率

       打开目录下的index.html可以看到各个类的覆盖率情况。

<img src="https://raw.githubusercontent.com/weipeng2k/mockito-sample/master/resources/chapter6-1.png" />

缺失路径

       点击到对应的package中的类,可以查看缺失的测试路径,这样就可以指导哪些分支没有纳入单测。

<img src="https://raw.githubusercontent.com/weipeng2k/mockito-sample/master/resources/chapter6-2.png" />

测试驱动开发简介

       测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码,然后只编写使测试通过的功能代码,从而以测试来驱动整个开发过程的进行。这有助于编写简洁可用和高质量的代码,有很高的灵活性和健壮性,能快速响应变化,并加速开发过程。

测试开发驱动模式

       测试驱动开发的基本过程如下:

  • 快速新增一个测试
  • 运行所有的测试(有时候只需要运行一个或一部分),发现新增的测试不能通过
  • 做一些小小的改动,尽快地让测试程序可运行,为此可以在程序中使用一些不合情理的方法
  • 运行所有的测试,并且全部通过
  • 重构代码,以消除重复设计,优化设计结构

       简单来说,就是不可运行/可运行/重构——这正是测试驱动开发的口号。

可取之处

       测试驱动开发能够让代码上生产环境之前,能够以使用者的角度审视编写的代码:

  • 如果代码难测,那就是对问题的分析还没有到位
  • 如果大量的Mock,那就是依赖过于复杂

       除了能够通过反向刺激让我们看到代码的不足,它还能以使用者的角度去看:

  • 这个方法命名是否够妥帖
  • 别人用这个函数会误用吗
  • 这个类是不是承担了过多的职责
目录
相关文章
|
6月前
|
Java 测试技术 开发者
Java单元测试与集成测试:确保代码质量的最佳实践
【4月更文挑战第2天】在软件开发中,单元测试验证单个代码单元(如Java类或方法)的功能,确保其正确性;而集成测试则关注多个组件协作时的交互。JUnit是常见的Java单元测试框架,集成测试则检验组件间接口的兼容性。Spring框架提供了集成测试的支持。遵循良好编码习惯,编写可测试代码,设计全面的测试用例,是保证代码质量和稳定性的关键。
148 0
|
12天前
|
设计模式 关系型数据库 测试技术
进阶技巧:提高单元测试覆盖率与代码质量
【10月更文挑战第14天】随着软件复杂性的不断增加,确保代码质量的重要性日益凸显。单元测试作为软件开发过程中的一个重要环节,对于提高代码质量、减少bug以及加快开发速度都有着不可替代的作用。本文将探讨如何优化单元测试以达到更高的测试覆盖率,并确保代码质量。我们将从编写有效的测试用例策略入手,讨论如何避免常见的测试陷阱,使用mocking工具模拟依赖项,以及如何重构难以测试的代码。
35 4
|
2月前
|
IDE 测试技术 持续交付
Python自动化测试与单元测试框架:提升代码质量与效率
【9月更文挑战第3天】随着软件行业的迅速发展,代码质量和开发效率变得至关重要。本文探讨了Python在自动化及单元测试中的应用,介绍了Selenium、Appium、pytest等自动化测试框架,以及Python标准库中的unittest单元测试框架。通过详细阐述各框架的特点与使用方法,本文旨在帮助开发者掌握编写高效测试用例的技巧,提升代码质量与开发效率。同时,文章还提出了制定测试计划、持续集成与测试等实践建议,助力项目成功。
78 5
|
3月前
|
IDE Java 测试技术
揭秘Java高效编程:测试与调试实战策略,让你代码质量飞跃,职场竞争力飙升!
【8月更文挑战第30天】在软件开发中,测试与调试对确保代码质量至关重要。本文通过对比单元测试、集成测试、调试技巧及静态代码分析,探讨了多种实用的Java测试与调试策略。JUnit和Mockito分别用于单元测试与集成测试,有助于提前发现错误并提高代码可维护性;Eclipse和IntelliJ IDEA内置调试器则能快速定位问题;Checkstyle和PMD等工具则通过静态代码分析发现潜在问题。综合运用这些策略,可显著提升代码质量,为项目成功打下坚实基础。
58 2
|
3月前
|
IDE 测试技术 持续交付
Python自动化测试与单元测试框架:提升代码质量与效率
随着软件行业的发展,代码质量和效率变得至关重要。自动化测试与单元测试是保证质量、提升效率的关键。Python凭借其简洁强大及丰富的测试框架(如Selenium、Appium、pytest和unittest等),成为了实施自动化测试的理想选择。本文将深入探讨这些框架的应用,帮助读者掌握编写高质量测试用例的方法,并通过持续集成等策略提升开发流程的效率与质量。
47 4
|
3月前
|
监控 jenkins 测试技术
自动化测试中的“守护神”: 持续集成与代码质量监控
【8月更文挑战第31天】在软件开发的海洋里,自动化测试犹如一座灯塔,指引着项目向着高质量和高效率的方向前进。本文将深入探讨如何通过持续集成(CI)和代码质量监控相结合的方式,构建起一道坚固的防线,保障软件项目在快速迭代中不失方向。我们将一起探索这一过程中的关键实践,以及它们是如何相互作用,共同提升软件项目的可靠性和稳定性。
|
4月前
|
测试技术 数据库 开发者
开发与运维测试问题之高代码覆盖率意味着高代码质量如何解决
开发与运维测试问题之高代码覆盖率意味着高代码质量如何解决
|
4月前
|
jenkins 数据管理 测试技术
在LabVIEW开发生命周期中提高代码质量的自动化测试方法
在LabVIEW开发生命周期中提高代码质量的自动化测试方法
53 0
|
6月前
|
Java 测试技术 Android开发
Java 测试和调试:提高代码质量的实用策略
【4月更文挑战第27天】测试和调试是软件开发中确保应用稳定、高效且可靠的关键步骤。对于 Java 开发者来说,掌握有效的测试和调试技巧可以大大提高代码质量和减少生产环境下的问题。
72 2
|
6月前
|
IDE 测试技术 持续交付
【专栏】利用Python自动化测试与单元测试框架提升代码质量与效率
【4月更文挑战第27天】本文探讨了Python自动化测试与单元测试框架在提升代码质量与效率中的作用。Selenium、Appium用于Web和移动应用自动化测试,pytest提供强大、易扩展的测试支持。unittest是Python标准的单元测试框架,支持结构化测试用例和丰富的断言。实践中,应制定测试计划,编写高质量测试用例,实行持续集成与测试,并充分利用测试报告。这些工具和策略能有效保障代码质量和提升开发效率。
56 1