• idea spring boot 1.x junit单元测试


      目前最主流的单元测试框架是junit,其中spring boot 1.x系列主要使用junit 4,spring boot 2.x主要使用junit 5;mock类和打桩的主要框架是mockito,主要有1.x(spring boot 1.x依赖),2.x(spring boot 2.0, 2.1依赖),3.x(spring boot 2.2依赖)三个版本。

    0、关于单元测试首先需要理解的是的,单元测试不能代替接口测试,前者是开发的事情,后者是开发为辅、测试为主。其目的是为了验证某个方法自身的逻辑没有问题、而没有职责验证其依赖的服务是否存在问题。因此,单元测试应该是很轻量的,甚至都不应该依赖spring环境,不需要启动servlet容器,否则就成了自动化半集成测试,所以简单的增删改查不适合作为单元测试的对象。

    1、参考https://www.cnblogs.com/Maoscn/p/10313660.html,安装Junit4插件。

    2、复习下junit中的注解。

    @BeforeClass:针对所有测试,只执行一次,且必须为static void
    @Before:初始化方法,执行当前测试类的每个测试方法前执行。

    @SpringBootTest:获取启动类、加载配置,确定装载Spring Boot,如果找不到@SpringBootConfiguration启动类将运行出错;

    @Test:测试方法,在这里可以测试期望异常和超时时间
    @After:释放资源,执行当前测试类的每个测试方法后执行
    @AfterClass:针对所有测试,只执行一次,且必须为static void
    @Ignore:忽略的测试方法(只在测试类的时候生效,单独执行该测试方法无效)
    @RunWith:标识为JUnit的运行环境 ,缺省值 org.junit.runner.Runner,也可以是JUnit4.class。

    一个单元测试类执行顺序为:

    @BeforeClass –> @Before –> @Test –> @After –> @AfterClass

    每一个测试方法的调用顺序为:

    @Before –> @Test –> @After

    断言测试

    断言测试也就是期望值测试,是单元测试的核心之一也就是决定测试结果的表达式,Assert对象中的断言方法:

    • Assert.assertEquals 对比两个值相等
    • Assert.assertNotEquals 对比两个值不相等
    • Assert.assertSame 对比两个对象的引用相等
    • Assert.assertArrayEquals 对比两个数组相等
    • Assert.assertTrue 验证返回是否为真
    • Assert.assertFlase 验证返回是否为假
    • Assert.assertNull 验证null
    • Assert.assertNotNull 验证非null

    除了常规的测试外,JUnit还通过其它特性的测试。

    超时测试
    如果一个测试用例比起指定的毫秒数花费了更多的时间,那么 Junit 将自动将它标记为失败。timeout 参数和 @Test注释一起使用。现在让我们看看活动中的 @test(timeout)。

    @Test(timeout = 1000)
    public void testTimeout() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("Complete");
    }

    上面测试会失败,在一秒后会抛出异常 org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds

    异常测试
    你可以测试代码是否它抛出了想要得到的异常。expected 参数和 @Test 注释一起使用。现在让我们看看活动中的 @Test(expected)。

    @Test(expected = NullPointerException.class)
    public void testNullException() {
        throw new NullPointerException();
    }

    上面代码会测试成功。

    套件测试

    public class TaskOneTest {
        @Test
        public void test() {
            System.out.println("Task one do.");
        }
    }
    
    public class TaskTwoTest {
        @Test
        public void test() {
            System.out.println("Task two do.");
        }
    }
    
    public class TaskThreeTest {
        @Test
        public void test() {
            System.out.println("Task Three.");
        }
    }
    
    @RunWith(Suite.class) // 1. 更改测试运行方式为 Suite
    // 2. 将测试类传入进来
    @Suite.SuiteClasses({TaskOneTest.class, TaskTwoTest.class, TaskThreeTest.class})
    public class SuitTest {
        /**
         * 测试套件的入口类只是组织测试类一起进行测试,无任何测试方法,
         */
    }

    3、Spring Boot 中使用 JUnit

    Spring 框架提供了一个专门的测试模块(spring-test),用于应用程序的集成测试。 在 Spring Boot 中,你可以通过spring-boot-starter-test启动器快速开启和使用它,其中包含了junit、hamcrest、mockito及asset相关类。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    // 获取启动类,加载配置,确定装载 Spring 程序的装载方法,它回去寻找 主配置启动类(被 @SpringBootApplication 注解的)
    @SpringBootTest
    // 让 JUnit 运行 Spring 的测试环境, 获得 Spring 环境的上下文的支持
    @RunWith(SpringRunner.class)
    public class EmployeeServiceImplTest {
        // do 
    }

      在微服务架构中,一般来说前后端是分离的,后端一般controller层会极其弱化,或者rpc服务自动暴露为REST API接口。所以webmvc层的单元测试在设计合理的架构中是不必要的,虽然Spring Boot Test提供了相当完备的功能供单元测试(具体可见https://blog.csdn.net/qq_35915384/article/details/80227297的前半部分。),但是在微服务架构中,它太重了。在一个典型的服务中,它的调用是这样的:

       为了测试A类,必须把B-E类全部服务都构建好,如果其中有其它微服务提供的接口,则不得不依赖挡板或集成测试环境,这样测试成本就会很高。所以,更好的做法是为B、C做mock类(对于每个被测类来说,Mock是类级别的,跟分支数无关),为B、C类的方法做stub(stub是根据B/C类对应方法有多少不同出入参对来决定的,一一对应。注:单元测试几乎所有被测类依赖的有状态类都需要Mock,接口测试则只需要Mock其他微服务的接口),这样就可以不用依赖spring环境完成测试。如下:

      

      但是事情通常要比这更复杂,有些非业务服务类可能需要依赖spring的配置信息,有一些利用了spring ioc的各种特性比如ApplicationContext.getBean()、多数据源切换、AOP拦截器、复杂逻辑生成文件等,对于这些特殊场景,仍然是需要仔细设计单元测试。正常情况下单元测试仅仅是为了测试逻辑,通常不包括事务,否则清理和准备通常也要花费不低的成本。如果一定要测试数据库,在单元测试上方法上增加@Transactional(加上会使得最后所有事务被回滚)注解反而不一定合适了,因为既然要测试数据库,则起码ACID应该测试。

      其次,对于桩而言,通常都是为了返回某个结果,对于一些包含很多字段的pojo和List,每次造这些数据也是比较耗时的,因此建议在json文件中维护相关的dto和pojo实例化数据及其配套工具类,进行统一的注入,这样单元测试的重复代码就可以大大减少。

           对于一个方法来说,单元测试的最低要求是100%的代码覆盖率,至少已知的通过、不通过、抛出的Exception得测到。除了简单的业务查询外,几乎不可能只有一个test case,如果一个方法只有一个单元测试,几乎可以肯定单元测试是为了应付,所以对每个方法,在javadoc上维护好场景清单,至少应包括:场景说明,入参(线程上下文变量),返回值/XXXException很重要,只有这样单测才会有效果。

    4、要实践好单元测试,首先得掌握事半功倍的技巧,不然很容易事倍功半,对单元测试总结得最好的一篇文章之一可以参见https://www.jianshu.com/p/afb04b925db3、https://www.jdon.com/53146也可以参考。

  • 相关阅读:
    .Net5 之 IHttpContextAccessor注册
    Oracle将两张表的数据插入第三张表且第三张表中不存在
    Git使用基础介绍
    Fork之后如何同步远程仓储和更新
    创建Oracle序列sequence
    Python语言规范
    Entity Framework Core Update Database
    OSI 7层模型(OSI 7 layer model)
    Expression<>, Func<>, Action<>的区别与联系
    .Net Core 项目部署在Linux下, 关闭Shell后项目会自动关闭的解决办法
  • 原文地址:https://www.cnblogs.com/zhjh256/p/12240331.html
Copyright © 2020-2023  润新知