乐趣区

关于java:别再用main方法测试了太Low这才是专业的SpringBoot项目测试方法

之前把我的 mall 我的项目降级到 Spring Boot 2.7 的时候,很多之前的测试方法都不能用了,原来是 Spring Boot Test 曾经降级反对 JUnit 5 了。明天咱们来聊聊新版 Spring Boot Test 的应用,有了它,咱们就不须要再应用 main 办法来测试了!

SpringBoot 实战电商我的项目 mall(50k+star)地址:https://github.com/macrozheng/mall

JUnit 简介

JUnit 是一款 Java 语言的单元测试框架,目前大多数 Java 开发环境都曾经反对它了。JUnit 测试也就是所谓的白盒测试,在程序员晓得程序外部逻辑的根底上进行的测试,应用 JUnit 能让咱们疾速地实现单元测试。Spring Boot Test 将 JUnit 和其余测试框架联合起来,提供了便捷高效的测试伎俩,目前 Spring Boot 2.7 版本采纳的是 JUnit 5。

罕用注解

在应用 Spring Boot Test 之前,咱们先来理解下它罕用的注解,这对应用它很有帮忙,具体参考下表即可!

注解 作用
@SpringBootTest 用于指定测试类启用 Spring Boot Test,默认会提供 Mock 环境
@ExtendWith 如果只想启用 Spring 环境进行简略测试,不想启用 Spring Boot 环境,能够配置扩大为:SpringExtension
@Test 指定办法为测试方法
@TestMethodOrder 用于配置测试类中办法的执行程序策略,配置为 OrderAnnotation 时,按 @Order 程序执行
@Order 用于配置办法的执行程序,数字越低执行程序越高
@DisplayName 用于指定测试类和测试方法的别名
@BeforeAll 在测试类的所有测试方法前执行一次,可用于全局初始化
@AfterAll 在测试类的所有测试方法后执行一次,可用于全局销毁资源
@BeforeEach 在测试类的每个测试方法前都执行一次
@AfterEach 在测试类的每个测试方法后都执行一次
@Disabled 禁用测试方法
@RepeatedTest 指定测试方法反复执行
@ParameterizedTest 指定参数化测试方法,相似反复执行,从 @ValueSource 中获取参数
@ValueSource 用于参数化测试指定参数
@AutoConfigureMockMvc 启用 MockMvc 的主动配置,可用于测试接口

根本应用

上面咱们来聊聊这些注解的根本应用,通过它们能够实现一些根本的单元测试。

集成 Spring Boot Test

如果你想在我的项目中集成 Spring Boot Test 的话,须要先在 pom.xml 中增加如下依赖。

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

最简略的测试

  • 咱们先来一个最简略的单元测试,应用 @SpringBootTest 注解启用单元测试,应用 @Test 指定测试方法,应用 Assertions 类的办法来断言后果是否合乎预期,具体代码如下。
/**
 * JUnit 根本测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class FirstTest {
    @Test
    public void test() {
        int a=1;
        Assertions.assertEquals(1,a);
    }
}
  • 而后点击测试方法左侧按钮即可进行测试。
  • 执行实现后咱们在 IDEA 的执行窗口中就能够看到办法测试通过了,因为应用 @SpringBootTest 启用了 Spring Boot 环境,日志中会输入 Spring Boot 的 banner。

指定测试方法程序

  • 咱们能够通过 @TestMethodOrder 注解和 @Order 注解来指定所有测试方法的执行程序,具体代码如下。
/**
 * JUnit 指定办法测试程序
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MethodOrderTest {private static final Logger LOGGER = LoggerFactory.getLogger(MethodOrderTest.class);

    @Test
    @Order(1)
    @DisplayName("order 为 1 的办法")
    void lowOrder(){LOGGER.info("lowOrder method");
    }

    @Test
    @Order(10)
    @DisplayName("order 为 10 的办法")
    void highOrder(){LOGGER.info("highOrder method");
    }
}
  • 点击类左侧测试按钮,能够间接运行该类中的所有测试方法。
  • 这里因为咱们应用了 @DisplayName 注解给测试方法取了个别名,而且咱们应用了 @ExtendWith 指定了运行环境为 Spring 而不是 Spring Boot,所以日志中不会呈现 Spring Boot 的 banner,执行速度也更快。

生命周期测试

  • 咱们还能够通过 JUnit 5 的生命周期注解来执行测试方法,比方在 @BeforeAll 注解指定的办法中做全局初始化,在 @AfterAll 注解指定的办法中做资源的销毁,具体代码如下。
/**
 * JUnit 生命周期测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class LifecycleTest {private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);

    @BeforeAll
    static void allInit(){LOGGER.info("allInit():在所有办法前执行,只执行一次");
    }

    @BeforeEach
    void eachInit(){LOGGER.info("eachInit():在测试方法前执行,每个测试方法前都执行");
    }

    @Test
    void successTest() {LOGGER.info("successTest():办法执行胜利");
    }

    @AfterEach
    void eachDown(){LOGGER.info("eachDown():在测试方法后执行,每个测试方法后都执行");
    }

    @AfterAll
    static void allDown(){LOGGER.info("allDown():在测试方法后执行,每个测试方法后都执行");
    }

}
  • 测试实现后,控制台输入日志如下。

断言的应用

咱们能够通过 Assertions 类中提供的断言 API 来断言测试后果。

  • 例如咱们能够应用 fail 办法间接断言办法执行失败并输入提示信息。
/**
 * JUnit 断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {

    @Test
    void failTest() {Assertions.fail("failTest():办法执行失败");
    }
}
  • 测试方法执行后会间接抛出异样信息。
  • 还能够通过 assertTrueassertNullassertEquals 这类办法来断言后果是否合乎预期。
/**
 * JUnit 断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {

    @Test
    void failTest() {Assertions.fail("failTest():办法执行失败");
    }

    @Test
    void trueTest(){Assertions.assertTrue(1==1);
    }

    @Test
    void trueFalse(){Assertions.assertFalse(3<=2);
    }

    @Test
    void nullTest(){
        String str = null;
        Assertions.assertNull(str);
    }

    @Test
    void notNullTest(){
        String str = "test";
        Assertions.assertNotNull(str);
    }

    @Test
    void equalsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertEquals(str1,str2);
    }

    @Test
    void notEqualsTest(){
        String str1 = "test";
        String str2 = "test";
        Assertions.assertNotEquals(str1,str2);
    }
}
  • 也能够应用 assertThrows 办法来断言办法中抛出的异样。
/**
 * JUnit 断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);
    @Test
    void throwsTest(){Assertions.assertThrows(NullPointerException.class,()->{
            String str = null;
            LOGGER.info(str.toLowerCase());
        });
    }
}
  • 还可通过 assertTimeout 办法断言办法的执行工夫。
/**
 * JUnit 断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {
    @Test
    void timeoutTest(){Assertions.assertTimeout(Duration.ofMillis(1000),()->{
            long sleepTime = 2000;
            ThreadUtil.sleep(sleepTime);
            LOGGER.info("timeoutTest(): 休眠 {} 毫秒",sleepTime);
        });
    }
}
  • 或者通过 assertAll 办法将几个断言联合起来应用,Assertions类中提供的工具办法很多,具体能够参考它的代码。
/**
 * JUnit 断言测试
 * Created by macro on 2022/10/11.
 */
@ExtendWith(SpringExtension.class)
public class AssertTest {
    @Test
    void assertAllTest(){Assertions.assertAll(()->{trueTest();
        },()->{nullTest();
        },()->{equalsTest();
        });
    }
}

其余测试

  • Spring Boot Test 除了上述测试性能,还能够应用 @Disabled 来禁用某个测试方法。
/**
 * JUnit 其余测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {
    @Test
    @Disabled("用于测试 @Disabled 注解")
    void disabledTest() {LOGGER.info("disabledTest():办法被执行");
    }
}
  • 也能够应用 @RepeatedTest 来实现循环测试。
/**
 * JUnit 其余测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);
    private static int count = 0;

    @RepeatedTest(3)
    void repeatedTest() {
        count++;
        LOGGER.info("repeatedTest():反复执行第 {} 次",count);
    }
}
  • 还能够通过 @ParameterizedTest 来进行参数化测试。
/**
 * JUnit 其余测试
 * Created by macro on 2022/10/10.
 */
@ExtendWith(SpringExtension.class)
public class OtherTest {private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleTest.class);

    @ParameterizedTest
    @ValueSource(ints = {1,2,3})
    public void parameterizedTest(int a){LOGGER.info("parameterizedTest():a={}",a);
    }
}
  • 运行以上测试方法后,具体测试后果如下。

我的项目实战

下面介绍了 Spring Boot Test 的根本应用,上面咱们联合我的项目来应用下它。

Dao 层测试

如果咱们的我的项目须要对数据拜访层 Dao 中的办法进行测试的话,间接注入 Mapper 接口,在测试方法中间接调用即可,这里对依据 ID 查问品牌的 Mapper 办法进行测试。

/**
 * Dao 层办法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class MapperTest {private static final Logger LOGGER = LoggerFactory.getLogger(MapperTest.class);

    @Autowired
    private PmsBrandMapper brandMapper;

    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandMapper.selectByPrimaryKey(id);
        LOGGER.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}

Service 层测试

对业务层 Service 中的办法测试也是一样的,间接注入 Service 接口,在测试方法中间接调用即可,这里对依据 ID 查问品牌的 Service 办法进行测试。

/**
 * Service 层办法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
public class ServiceTest {private static final Logger LOGGER = LoggerFactory.getLogger(ServiceTest.class);
    @Autowired
    private PmsBrandService brandService;

    @Test
    void testGetById(){
        long id = 6;
        PmsBrand pmsBrand = brandService.getBrand(id);
        LOGGER.info("brand name:{}",pmsBrand.getName());
        Assertions.assertEquals("小米",pmsBrand.getName());
    }
}

Controller 层测试

对于 Controller 层办法进行测试,有时咱们须要模仿申请,应用 MockMvc 即可,这里模仿测试下分页查问品牌列表的接口。

/**
 * Controller 层办法测试
 * Created by macro on 2022/10/11.
 */
@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void mvcTest() throws Exception{
        // 模仿发送一个申请拜访分页查问品牌列表的接口
        mockMvc.perform(MockMvcRequestBuilders.get("/brand/list") // 设置申请地址
                .param("pageNum","1") // 设置申请参数
                .param("pageSize","5"))
                .andExpect(MockMvcResultMatchers.status().isOk()) // 断言返回状态码为 200
                .andDo(MockMvcResultHandlers.print()) // 在控制台打印日志
                .andReturn(); // 返回申请后果}
}

因为咱们抉择了在控制台输入日志,控制台将输入如下信息。

总结

明天带大家体验了一把 Spring Boot Test,作为 Spring Boot 官网测试框架,的确性能很弱小。因为其次要基于 JUnit 5,和 JUnit 5 的用法基本一致。应用它进行单元测试,无需启动整个我的项目,更快更好用!

参考资料

JUnit 5 官网文档:https://junit.org/junit5/docs…

我的项目源码地址

https://github.com/macrozheng…

退出移动版