SpringBoot入门教程02——如何优雅地使用spring-boot-starter-test做单元测试
前言
看过一些springboot教程博客,单元测试这里的教程大都比较旧,还有一些博客甚至让exclusions掉spring-boot-starter-test下的包,然后重新引入junit4去做单元测试。
笔者认为这些方法不是不行,只是不够优雅,下面我们讲一下如何优雅地使用内置的spring-boot-starter-test做单元测试
环境说明
- 我们使用第1节教程构建出的项目 传送门
- springboot版本号2.3.1.RELEASE
- pom文件直接使用Spring Assistant构建项目时生成的,一行都不需要改动
第一个测试案例
打开test/java目录,我们发现贴心的Spring Assistant已经帮我们构建好了第一个测试案例——SpringbootDemoApplicationTests
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootDemoApplicationTests {
@Test
void contextLoads() {
}
}
如果你是idea工具开发,在contextLoads()方法这一行最左边会有一个三角符号,点击即可运行该测试方法。
运行之后,我们可以看到控制台输出了一堆看不懂的日志,然后提示"Tests passed"
这时候你一定有以下2个疑问
- @SpringBootTest注解究竟做了什么
- 为什么内置测试案例的方法名叫contextLoads
@SpringBootTest注解究竟做了什么
我们对第一个测试案例进行一个改造,代码如下:
@Test
void contextLoads(ApplicationContext ctx) {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
System.out.println(String.format("Spring applicationtext has %d beans total !", beanNames.length));
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
运行日志如下:
Let's inspect the beans provided by Spring Boot:
Spring applicationtext has 125 beans total !
applicationAvailability
applicationTaskExecutor
basicErrorController
beanNameHandlerMapping
beanNameViewResolver
characterEncodingFilter
...
我们可以发现spring容器中一共有125个beans,而且我们自定义的helloController也在里面。所以上一章的2个小问题就很好回答了
- @SpringBootTest注解会模拟SpringBoot应用启动,创建ApplicationContext容器,并把内置的beans以及用户开发的beans注入到ApplicationContext容器中。
- 以上过程我们可以称之为容器加载的过程,所以方法命名为contextLoads
既然我们自定义的helloController也在spring容器中,那我们该如何调用它呢?
调用SpringBoot应用中自定义的Controller
在src/test/java的com.henry包下新建controller包,再新建HelloControllerTest类,代码如下:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerTest {
@LocalServerPort
private int port;
private String baseUrl;
private TestRestTemplate restTemplate;
@BeforeEach
public void setUp(){
this.baseUrl="http://127.0.0.1:"+port;
this.restTemplate = new TestRestTemplate();
}
@Test
public void index() {
System.out.println("request url is: "+baseUrl);
ResponseEntity<String> response = restTemplate.getForEntity(baseUrl, String.class);
System.out.println(response.getBody());
Assertions.assertEquals(response.getBody(),"Welcome to Spring Boot!");
}
}
运行index方法,即可看到可以成功调用springboot应用中的helloController
下面我们对几个参数进行一个说明
- webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT //指定一个随机端口,去启动内置的web应用服务器
- @LocalServerPort 该注解会把springboot内置web应用服务器启动端口赋给其注解的字段,也就是port字段
- @BeforeEach junit5新增注解,等同于junit4以前的@Before注解,会在@Test注解之前执行
如果webEnvironment不指定属性,那么@SpringBootTest注解只会初始化spring容器并注入beans,但是不会启动内置的web应用服务器。指定RANDOM_PORT就是随机端口,随机端口的好处是应用启动的时候也能玩。指定DEFINED_PORT用的就是配置文件中配置的端口,默认是8080,如果应用是运行状态,运行测试案例会端口冲突。
贴一下WebEnvironment源码,大家一看便知
public static enum WebEnvironment {
MOCK(false),
RANDOM_PORT(true),
DEFINED_PORT(true),
NONE(false);
private final boolean embedded;
private WebEnvironment(boolean embedded) {
this.embedded = embedded;
}
public boolean isEmbedded() {
return this.embedded;
}
}