Spring Boot 集成测试自动配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41723615/article/details/88376955

介绍:

1.Spring Framework的核心工作是将所有组件编织在一起,构成一个应用程序。整个过程就是读取配置说明(可以是XML、基于Java的配置、基于Groovy的配置或其他类型的配置),在应用程序上下文里初始化Bean,将Bean注入依赖它们的其他Bean中。

2.对Spring应用程序进行集成测试时,让Spring遵照生产环境来组装测试目标Bean是非常重要的一点。

3.自Spring 2.5开始,集成测试支持的形式就变成了SpringJUnit4ClassRunner。这是一个JUnit类运行器,会为JUnit测试加载Spring应用程序上下文,并为测试类自动织入所需的Bean。

用SpringJUnit4ClassRunner对Spring应用程序进行集成测试

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration( //加载应用程序上下文
      classes=AddressBookConfiguration.class)
public class AddressServiceTests { 
    @Autowired 
    private AddressService addressService; //注入地址服务
    @Test 
    public void testService() {  //测试地址服务
        Address address = addressService.findByLastName("Sheman"); 
        assertEquals("P", address.getFirstName()); 
        assertEquals("Sherman", address.getLastName()); 
        assertEquals("42 Wallaby Way", address.getAddressLine1()); 
        assertEquals("Sydney", address.getCity()); 
        assertEquals("New South Wales", address.getState()); 
        assertEquals("2000", address.getPostCode()); 
 } 
} 

@RunWith的参数是SpringJunit4ClassRunner.class,开启了Spring集成测试支持。

@ContextConfiguration指定了如何加载应用程序上下文。

SpringJunit4ClassRunner还能通过自动织入从应用程序上下文里向测试本身注入Bean。

testService()方法调用地址服务并验证了结果。

在Spring 4.2里,可以选择基于规则的SpringClassRule和SpringMethodRule来代替SpringJunit4ClassRunner.

虽然@ContextConfiguration在加载Spring应用上下文的过程种做了很多事情,但是它没能加载完整的Spring Boot 。

Spring Boot最总由SpringApplication加载的。

SpringApplication不仅加载应用程序上下文,还会开启日志、加载外部属性(application.properties或application.yml),以及其他特性,用@ContextConfiguration则得不到这些特性。

要在集成测试里或获取这些特性,可以把@ContextConfiguration替换为Spring Boot的@SpringApplicationConfiguration。

Web应用程序测试:

要测试,需要投入一些实际的的HTTP请求,确认它能正确地处理
那些请求。幸运的是,Spring Boot开发者有两个可选的方案能实现这类测试。
1.Spring Mock MVC:能在一个近似真实的模拟Servlet容器里测试控制器,而不用实际启动
应用服务器。
2.Web集成测试:在嵌入式Servlet容器(比如Tomcat或Jetty)里启动应用程序,在真正的应
用服务器里执行测试。

    模拟Spring MVC

Spring 的 Mock MVC框架模拟了SpringMVC的很多功能。

要在测试里设置Mock MVC,可以使用MockMvcBuilders,该类提供了两个静态方法。
1.standaloneSetup():构建一个Mock MVC,提供一个或多个手工创建并配置的控制器。
2.webAppContextSetup():使用Spring应用程序上下文来构建Mock MVC,该上下文里可以包含一个或多个配置好的控制器

两者的主要区别在于,standaloneSetup()希望你手工初始化并注入你要测试的控制器,而webAppContextSetup()则基于一个WebApplicationContext的实例,通常由Spring加载。
前者同单元测试更加接近,你可能只想让它专注于单一控制器的测试,而后者让Spring加载控制器及其依赖,以便进行完整的集成测试。
我们要用的是webAppContextSetup()。Spring完成了ReadingListController的初始化,并从Spring Boot自动配置的应用程序上下文里将其注入,我们直接对其进行测试。
webAppContextSetup()接受一个WebApplicationContext参数。因此,我们需要为测试类加上@WebAppConfiguration注解,使用@Autowired将WebApplicationContext作为实例变量注入测试类。

为集成测试控制器创建Mock MVC

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration( 
        classes = ReadingListApplication.class) 
@WebAppConfiguration //开启web上下文测试
public class MockMvcWebTests { 
    @Autowired 
    private WebApplicationContext webContext; //注入WebApplicationContext
    private MockMvc mockMvc; 
    @Before 
    public void setupMockMvc() { //设置MockMVC
        mockMvc = MockMvcBuilders
                 .webAppContextSetup(webContext) 
                 .build(); 
    } 
} 

@WebAppConfiguration注解声明,由SpringJUnit4ClassRunner创建的应用程序上下文应该是一个WebApplicationContext(相对于基本的非WebApplicationContext)。setupMockMvc()方法上添加了JUnit的@Before注解,表明它应该在测试方法之前执行。它将WebApplicationContext注入webAppContextSetup()方法,然后调用build()产生了一个MockMvc实例,该实例赋给了一个实例变量,供测试方法使用。
现在我们有了一个MockMvc,已经可以开始写测试方法了。我们先写个简单的测试方法,向/readingList发送一个HTTP GET请求,判断模型和视图是否满足我们的期望。下面的homePage()测试方法就是我们所需要的。

@Test 
public void homePage() throws Exception { 
    mockMvc.perform(MockMvcRequestBuilders.get("/readingList")) 
    .andExpect(MockMvcResultMatchers.status().isOk()) 
    .andExpect(MockMvcResultMatchers.view().name("readingList")) 
    .andExpect(MockMvcResultMatchers.model().attributeExists("books")) 
    .andExpect(MockMvcResultMatchers.model().attribute("books", 
     Matchers.is(Matchers.empty()))); 
} 

如你所见,我们在这个测试方法里使用了很多静态方法,包括Spring的MockMvcRequestBuilders和MockMvcResultMatchers里的静态方法,还有Hamcrest库的Matchers里的静态方法。在深入探讨这个测试方法前,先添加一些静态import,这样代码看起来更清爽一些:

import static org.hamcrest.Matchers.*; 
import static org.springframework.test.web.servlet.request. 
 ➥ MockMvcRequestBuilders.*; 
import static org.springframework.test.web.servlet.result. 
 ➥ MockMvcResultMatchers.*; 

有了这些静态import后,测试方法可以稍作调整:

@Test 
public void homePage() throws Exception { 
    mockMvc.perform(get("/readingList")) 
    .andExpect(status().isOk()) 
    .andExpect(view().name("readingList")) 
    .andExpect(model().attributeExists("books")) 
    .andExpect(model().attribute("books", is(empty()))); 
} 

此处完全不需要将应用程序部署到Web服务器上,它是运行在模拟的Spring MVC中的,刚好能通过MockMvc实例处理我们给它的HTTP请求。

再来看一个测试方法,发送一个HTTP POST请求提交一本新书。我们应该期待POST请求处理后重定向回/readingList,模型将包含新添加的图书。

public void postBook() throws Exception { 
mockMvc.perform(post("/readingList")  //执行post请求
    .contentType(MediaType.APPLICATION_FORM_URLENCODED) 
    .param("title", "BOOK TITLE") 
    .param("author", "BOOK AUTHOR") 
    .param("isbn", "1234567890") 
    .param("description", "DESCRIPTION")) 
    .andExpect(status().is3xxRedirection()) 
    .andExpect(header().string("Location", "/readingList")); 
   Book expectedBook = new Book(); //配置期望的图书
   expectedBook.setId(1L); 
   expectedBook.setReader("craig"); 
   expectedBook.setTitle("BOOK TITLE"); 
   expectedBook.setAuthor("BOOK AUTHOR"); 
   expectedBook.setIsbn("1234567890"); 
   expectedBook.setDescription("DESCRIPTION"); 
   mockMvc.perform(get("/readingList")) //执行get请求
    .andExpect(status().isOk()) 
    .andExpect(view().name("readingList")) 
    .andExpect(model().attributeExists("books")) 
    .andExpect(model().attribute("books", hasSize(1))) 
    .andExpect(model().attribute("books", 
    contains(samePropertyValuesAs(expectedBook)))); 
} 

在提交图书时,我们必须确保内容类型(通过MediaType.APPLICATION_FORM_URLENCODED)设置为application/x-www-form-urlencoded,这才是运行应用程序时浏览器会发送的内容类型。随后,要用MockMvcRequestBuilders的param方法设置表单域,模拟要提交的表单。一旦请求执行,我们要检查响应是否是一个到/readingList的重定向。
假定以上测试都通过,我们进入第二部分。首先设置一个Book对象,包含想要的值。我们用这个对象和首页获取的模型的值进行对比。
随后要对/readingList发起一个GET请求,大部分内容和我们之前测试主页时一样,只是之前模型中有一个空集合,而现在有一个集合项。这里要检查它的内容是否和我们创建的expectedBook一致。如此一来,我们的控制器看来保存了发送给它的图书,完成了工作。

猜你喜欢

转载自blog.csdn.net/qq_41723615/article/details/88376955