【SpringBootTest】【JUnit】Web层测试 Controller Service

导言

使用SpringBoot如何模拟请求只对Web层进行测试?我们可以使用Mockito框架来模拟我们需要的对象,但是模拟一个response显然很复杂。对此,SpringBoot有更好的测试方式——通过MockMvc。

被测试类

@Controller
public class HomeController {
     

	@RequestMapping("/")
	public @ResponseBody String greeting() {
     
		return "Hello, World";
	}

}

@SpringBootApplication
public class TestingWebApplication {
     

	public static void main(String[] args) {
     
		SpringApplication.run(TestingWebApplication.class, args);
	}
}

上面是一个简单的SpringApplication,启动项目之后,我们可以输入网址http://localhost:8080来检查项目的Spring Context是否启动成功。

Test 1. 引入Full Spring Context,不启动服务器

@SpringBootTest
public class SmokeTest {
     

	@Autowired
	private HomeController controller;

	@Test
	public void contextLoads() throws Exception {
     
		assertThat(controller).isNotNull();
	}
}
  • 如果上面这个测试类通过了,说明SpringApplication被成功启动。
  • @SpringBootTest注解会去找带有@SpringBootApplication注解的类,用它来启动Spring Application的环境。controller不为空说明HomeController被成功注入。@Autowired 注解的bean 在测试方法运行前被注入。
  • Spring 提供的测试支持可以将 Application Context 缓存起来,这会使同一个配置类下的上下文资源在不同test case 间共享,所有测试只会产生一次启动应用的开销。
  • 运行测试,通过 console 可以看到没有 Tomcat的日志打出,Tomcat server未启动。

作者:当当一丢丢 链接:https://www.jianshu.com/p/5dae923e64a8 来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Test 2. 引入Full Spring Context,启动服务器

上面的test可以测试Spring是否正常启动,如果我们要测试程序的行为,我们还应该要模拟HTTP请求,并对response进行断言。看下面的测试,被测试类同上一个例子相同:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {
     

	@LocalServerPort
	private int port;

	@Autowired
	private TestRestTemplate restTemplate;

	@Test
	public void greetingShouldReturnDefaultMessage() throws Exception {
     
		assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
				String.class)).contains("Hello, World");
	}
}
  • 使用webEnvironment = WebEnvironment.RANDOM_PORT就会产生一个随机端口号(防止端口冲突)。
  • @LocalServerPort注解将这个端口号注入port中。
  • @TestRestTemlpate Spring boot 提供一个TestRestTemplate,作为 Http Client。
  • 存在启动Tomcat的开销

Test 3. 引入Full Spring Context,不启动服务器,使用MockMvc

上面的测试,每次运行都要启动Spring服务器,显然很麻烦。用Spring的MockMvc来发送请求,就可以启动Full Spring Context而无需启动服务器。下面看例子:

@SpringBootTest
@AutoConfigureMockMvc
public class TestingWebApplicationTest {
     

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() throws Exception {
     
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")));
	}
}
  • @AutoConfigureMockMvc注解将mockMvc自动注入。
  • mockMvc 可以模拟发送Http请求。
  • 运行测试,通过 console 可以看到没有 Tomcat的日志打出,Tomcat server未启动。

Test 4. 只引入Web 层的Spring Context,不启动服务器, 由 MockMVC 发送请求

在上面的测试中,将启动Full Spring Context,但不启动服务器。我们可以通过使用@WebMvcTest将测试范围缩小到web层,看下面的测试:

@WebMvcTest
public class WebLayerTest {
     

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() throws Exception {
     
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")));
	}
}
  • 在上面的测试中,SpringBoot只实例化Web层,而不是整个上下文。如果在一个应用程序中有多个controller,您甚至可以通过以下方式请求只实例化一个特定的controller。
  • @WebMvcTest(SthController.class) 指明具体限制注入的controller 对象,未加 class 属性的时候注入所有 controller。

一个复杂的SpringBootTest,controller 依赖 service

有了上面的学习,我们可以试着写更复杂一点的Spring测试。

@Controller
public class GreetingController {
     

	private final GreetingService service;

	public GreetingController(GreetingService service) {
     
		this.service = service;
	}

	@RequestMapping("/greeting")
	public @ResponseBody String greeting() {
     
		return service.greet();
	}
}

@Service
public class GreetingService {
     
	public String greet() {
     
		return "Hello, World";
	}
}
@WebMvcTest(GreetingController.class)
public class WebMockTest {
     

	@Autowired
	private MockMvc mockMvc;

	@MockBean
	private GreetingService service;

	@Test
	public void greetingShouldReturnMessageFromService() throws Exception {
     
		when(service.greet()).thenReturn("Hello, Mock");
		this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, Mock")));
	}
}
  • 我们使用@MockBeanGreetingService创建并注入一个mock(如果不这样做,应用程序上下文将无法启动),并使用Mockito设置其期望值。
  • 如果使用@WebMvcTest就要将其他层的对象使用@MockBean注入,否则项目无法启动。
  • 但是如果使用@SpringBootTest@AutoConfigureMockMvc则不需要,因为它是full stack的启动。

参考:
SpringBoot 测试-Web层
spring-test - Testing the Web Layer

你可能感兴趣的:(Spring,spring,boot,junit)