zoukankan      html  css  js  c++  java
  • Spring boot web 单元测试程序

    一,测试Web应用程序

    要恰当地测试一个Web应用程序,需要投入一些实际的HTTP请求,确认它能正确地处理 那些请求。Spring Boot开发者有两个可选的方案能实现这类测试:

    • Spring Mock MVC:能在一个近似真实的模拟Servlet容器里测试控制器,而不用实际启动 应用服务器。
    • Web集成测试:在嵌入式Servlet容器(比如Tomcat或Jetty)里启动应用程序,在真正的应 用服务器里执行测试。

    1. 模拟Spring MVC

    要在测试里设置Mock MVC,可以使用MockMvcBuilders,该类提供了两个静态方法:

    • standaloneSetup():构建一个Mock MVC,提供一个或多个手工创建并配置的控制器。
    • webAppContextSetup():使用Spring应用程序上下文来构建Mock MVC,该上下文里 可以包含一个或多个配置好的控制器。

    两个方法区别:

    • standaloneSetup():手工初始化并注入要测试的控制器,
    • webAppContextSetup():基于一个WebApplicationContext的实例,通常由Spring加载。

    前者同单元测试更加接近,你可能只想让它专注于单一控制器的测试,而后者让Spring加载控制 器及其依赖,以便进行完整的集成测试。

    /* 代码清单4-2 为集成测试控制器创建Mock MVC */
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(
    classes = ReadingListApplication.class)         //开启Web上下文
    @WebAppConfiguration
        public class MockMvcWebTests {
        @Autowired
        private WebApplicationContext webContext;   //注入WebApplicationContext
        private MockMvc mockMvc;
        @Before
        public void setupMockMvc() {
            mockMvc = MockMvcBuilders
            .webAppContextSetup(webContext)        //设置MockMvc
            .build();
        }
    }
    /* 向/readingList发起一个GET请求 */
    @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())));
    }
    /* 向/readingList发起一个POST请求 */
    @Test
    public void postBook() throws Exception {
        mockMvc.perform(post("/readingList")
            .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"));
    /* 验证刚刚的POST请求 */
    //配置期望的图书
    Book expectedBook = new Book();   
    expectedBook.setId(1L);
    expectedBook.setReader("craig");
    expectedBook.setTitle("BOOK TITLE");
    expectedBook.setAuthor("BOOK AUTHOR");
    expectedBook.setIsbn("1234567890");
    expectedBook.setDescription("DESCRIPTION");
    //执行GET请求
    mockMvc.perform(get("/readingList"))
        .andExpect(status().isOk())
        .andExpect(view().name("readingList"))
        .andExpect(model().attributeExists("books"))
        .andExpect(model().attribute("books", hasSize(1)))
        .andExpect(model().attribute("books",
        contains(samePropertyValuesAs(expectedBook))));
    }

    2. 测试Web安全

    使用Spring Security

    1. 添加依赖

      <dependency>
          <groupId>org.springframework.security</groupId>
          <artifactId>spring-security-test</artifactId>
          <scope>test</scope>
      </dependency>
    2. 在创建MockMvc实例时运用Spring Security的配置器

      @Before
      public void setupMockMvc() {
          mockMvc = MockMvcBuilders
          .webAppContextSetup(webContext)
          .apply(springSecurity())
          .build();
      }
    3. 使用(具体的安全配置取决于你如何配置Spring Security(或者Spring Boot如何自动配置Spring Security)。)

      场景代码:

      1)请求未经身份验证

      /* 请求未经身份验证,重定向回登录界面 */
      @Test
      public void homePage_unauthenticatedUser() throws Exception {
          mockMvc.perform(get("/"))
          .andExpect(status().is3xxRedirection())
          .andExpect(header().string("Location",
          "http://localhost/login"));
      }

      2)请求经过身份验证 Spring Security提供了两个注解:

      • @WithMockUser:用给定的值创建了一个UserDetails对象,指定用户名、密码和授权。
      • @WithUserDetails:使用事先配置好的UserDetailsService来加载UserDetails对象,根据给定的用户名查找 并返回一个Reader对象。
      /* 经过身份验证的请求,使用@WithMockUser */
      @Test
      @WithMockUser(username="craig",
         password="password",
         roles="READER")
         public void homePage_authenticatedUser() throws Exception {
         ...
      }
      /* 经过身份验证的请求,使用@WithUserDetails */
      @Test
      @WithUserDetails("craig")
      public void homePage_authenticatedUser() throws Exception {
         Reader expectedReader = new Reader();
         expectedReader.setUsername("craig");
         expectedReader.setPassword("password");
         expectedReader.setFullname("Craig Walls");
         mockMvc.perform(get("/"))
             .andExpect(status().isOk())
             .andExpect(view().name("readingList"))
             .andExpect(model().attribute("reader",
             samePropertyValuesAs(expectedReader)))
             .andExpect(model().attribute("books", hasSize(0)))
      }

      此处没有启动Servlet容器来运行这些测试, Spring的Mock MVC取代了实际的Servlet 容器。它比直接调用控制器方法要好,但它并没有真的在Web浏 览器里执行应用程序,验证呈现出的视图。

    三、测试运行中的应用程序

    Spring Boot支持用嵌入式Servlet容器来启动应用程序。

    Spring Boot 的 @WebIntegrationTest 注解就是这么做的。 在测试类上添加@WebIntegrationTest注解,可以声明你不仅希望Spring Boot为测试创建应用程序上下文,还要启 动一个嵌入式的Servlet容器。一旦应用程序运行在嵌入式容器里,你就可以发起真实的HTTP请 求,断言结果了。

    /* 代码清单4-5 在服务器里启动应用程序,以Spring的RestTemplate对应用程序发起HTTP请求 */
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(
    classes=ReadingListApplication.class)
    @WebIntegrationTest
    public class SimpleWebTest {
        @Test(expected=HttpClientErrorException.class)
        public void pageNotFound() {
            try {
            RestTemplate rest = new RestTemplate();
            rest.getForObject(
                "http://localhost:8080/bogusPage", String.class);
            fail("Should result in HTTP 404");
            } catch (HttpClientErrorException e) {
            assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode());
            throw e;
            }
        }   
    }

    1. 用随机端口启动服务器

    @WebIntegrationTest(value={"server.port=0"}) 或者 @WebIntegrationTest("server.port=0") 或者 @WebIntegrationTest(randomPort=true)

    使用端口

    @Value("${local.server.port}")
    private int port;
    rest.getForObject(
    "http://localhost:{port}/bogusPage", String.class, port);

    2. 使用Selenium测试HTML页面

    1. 添加org.seleniumhq.selenium依赖

    2. 代码里使用

      1> 配置

      /* 在Spring Boot里使用Selenium测试的模板 */
       @RunWith(SpringJUnit4ClassRunner.class)
       @SpringApplicationConfiguration(
       classes=ReadingListApplication.class)
       @WebIntegrationTest(randomPort=true)
           public class ServerWebTests {
           private static FirefoxDriver browser;
           @Value("${local.server.port}")
           private int port;
           
           //配置Firefox驱动
           @BeforeClass
           public static void openBrowser() {
               browser = new FirefoxDriver();
               browser.manage().timeouts()
               .implicitlyWait(10, TimeUnit.SECONDS);
           }
           
           //关闭浏览器
           @AfterClass
           public static void closeBrowser() {
               browser.quit();
           }
       }

      2> 测试

      用Selenium测试阅读列表应用程序
       @Test
       public void addBookToEmptyList() {
           String baseUrl = "http://localhost:" + port;
           browser.get(baseUrl);
           assertEquals("You have no books in your book list", 
                           browser.findElementByTagName("div").getText());
                          
           //填充并发送表单                
           browser.findElementByName("title").sendKeys("BOOK TITLE");
           browser.findElementByName("author").sendKeys("BOOK AUTHOR");
           browser.findElementByName("isbn").sendKeys("1234567890");
           browser.findElementByName("description").sendKeys("DESCRIPTION");
           browser.findElementByTagName("form").submit();
           
           //判断列表中是否包含新书
           WebElement dl = browser.findElementByCssSelector("dt.bookHeadline");
           assertEquals("BOOK TITLE by BOOK AUTHOR (ISBN: 1234567890)", dl.getText());
           WebElement dt = browser.findElementByCssSelector("dd.bookDescription");
           assertEquals("DESCRIPTION", dt.getText());
       }

    备注:书上版本比较老,下面补充下新版本的测试方法。 例子摘自segmentfault_chenatu的文章 (仅适用spring-boot 1.4版本以后的写法):

    直接调用接口函数进行测试:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ApiTest {
        @Autowired
        MessageApi messageApi;
        ...

    测试controller:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class ControllerTest {
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void testControllerMethods() {
            MvcResult result = mockMvc.perform(get("/get-receive-message-abstracts").param("siteId", "webtrn").param("uid", "lucy")
                        .param("limit", "100")).andExpect(status().isOk())
                        .andExpect(jsonPath("$", hasSize(10))).andExpect(jsonPath("$[9].title", is("hello0"))).andReturn();
        }
  • 相关阅读:
    python易混易乱(2)
    python易混易乱(1)
    #1062 – Duplicate entry ‘1’ for key ‘PRIMARY’
    关于 flask 实现数据库迁移以后 如何根据创建的模型类添加新的表?
    Linux同步互斥(Peterson算法,生产者消费者模型)
    正则表达式(Python)
    进程间通信
    CSS常见简写规则整理
    Django Model
    Django杂记
  • 原文地址:https://www.cnblogs.com/Zhaoyongshui/p/9002825.html
Copyright © 2011-2022 走看看