四、单元测试

笔记源于书籍:SpringBoot+Vue全栈开发实战-王松

一、前言

​ 如果在需要测试的地方都是创建一个controller1进行测试,这样效率较低,在SpringBoot中可以使用单元测试对每一个环节的代码进行测试,SpringBoot简化Spring的测试,只需要少量代码即可搭建测试环节,可以对Controller、Service或者Dao层的代码进行测试。

二、基本用法

​ 使用IDEA创建SpringBoot项目时,默认已经添加了spring-boot-starter-test依赖

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

并且有了初始的测试类如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01ApplicationTest{
    @Test
    public void contextLoads()
}

注释:

  • SpringRunner是SpringFrameWork中测试类SpringJUnit4ClassRunner的别名
    Junit基本注解介绍
  • @RunWith注解:该注解将JUnit执行类修改为SpringRunner,
  • @BeforeClass 在所有测试方法前执行一次,一般在其中写上整体初始化的代码
  • @AfterClass 在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码
  • @Before 在每个测试方法前执行,一般用来初始化方法
  • @After 在每个测试方法后执行,在方法执行完成后要做的事情
  • @Test(timeout = 1000) 测试方法执行超过1000毫秒后算超时,测试将失败
  • @Test(expected = Exception.class) 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败
  • @Test 编写一般测试用例
  • @Ignore(“not ready yet”) 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类。

三、Service层测试

service层的测试就是常规测试,如下有一个HelloService:

  • Service层:
@Service
public class HelloService{
    public String sayHello(String name)
        return "hello " + name;
}
  • 测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01ApplicationTest{
    
    // 注入Service
    @AutoWired
    HelloService helloService;
    
    // 测试方法
    public void testHelloService(){
        String result = helloService.sayHello("Jack");
        
        // 判断结果
        Assert.assertThat(result, Matchers.is("hello Jack"));
    }
}

四、Controller层测试

​ controller层往往需要使用到一些不易获取的对象,这时使用Mock测试,采用虚拟的对象进行测试.

​ Spring中的MockMvc提供了对Http请求的模拟,开发者可以在不依赖网络环境的情况下实现对Controller的测试。可以不必启动工程就可以测试这些接口

  • Controller层:
@RestController
public class HelloController{
    
    @GetMapping("/hello")
    piblic String hello(String name){
        return "hello " + name;
    }
    
    @PostMapping("/book")
    public String addBook(@RequestBody Book book){
        return book.toString();
    }
}
  • Controller层涉及的Entity类Book:
public class Book{
    private Integer id;
    private String name;
    private String author;
    // 省略getter、setter、toString
}
  • 要对Controller层进行测试,则要借助于MockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
public class ControllerApplicationTest{
    
    @AutoWired
    HelloService helloService;
    
    @AutoWired
    WebApplicationContext wac;// 用来模拟ServletContext环境
    
    MockMvc mockMvc;// 声明一个MockMvc对象
   
    @Before
    public void before(){// 每个方法执行前进行MockMvc的初始化操作
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    
    @Test
    public void testHello() throws Exception{
        MvcResult mvcResult = mockMvc.perform(
        	MockMvcRequestBuilders
        		.get("hello")
            	.contentType(MediaType.APPLIACTION_FORM_URLENCODED)
            	.param("name", "Michael")// 设置参数
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andDo(MockMvcResultHandlers.print())// 将请求详细信息打印到控制台
            .andReturn();// 返回相应的MvcResult
         System.out.println(mvcResult.getResponse().getContentAsString());// 将返回的MvcResult打印出来
    }
           
    @Test
    public void testBook() throws Exception{
        ObjectMapper om = new ObjectMapper();
        Book book = new Book();
        book.setAuthor("罗贯中");
        book.setName("三国演义");
        book.setId(1);
        String s = om.writeValueAsString(book);// 将book对象转为一段Json
        MvcResult mvcResult = mockMvc
            .perform(MockMvcRequestBuilder
                    .post("book")
                    .contentType(MediaType.APPLICATION_JSON)// 传递Json数据
                    .content(s))// 设置content为book的json格式
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andReturn();
        System.out.println(mvcResult.getResponse().getContentAsString());
    }
}
  • 除了使用MockMvc测试方法之外,SpringBoot还提供了TestRestTemplate,开发者使用@SpringBootTest时,TestRestTemplate自动可用,可以直接注入。

注意:使用TestRestTemplate需要将SpringBootTest注解中webEnvironment属性的默认值由WebEnvironment.MOVK修改为WebEnvironment.DEFINED_PORT或者WebEnvironment.RANDOM_PORT,这两种都是真实的Servlet环境而不是模拟的Servlet环境。

测试:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class ControllerApplicationTest{
    @AutoWired
    TestRestTemplate restTemplate;
    
    @Test
    public void testHello(){
        ResponseEntity<String> hello = restTemplate.getForEntity("?hello?name={0}",String.class, "Michael");
        System.out.println(hello.getBody());
    }
}

关于restTemplate的详细使用方法参考可另一篇文章: https://blog.csdn.net/the_bud/article/details/105573026

  • Mock测试

假设当前服务主要是提供用户方面的功能, 有时候还希望查看用户购买了哪些产品以及产品的 详情,而产品的详情是产品微服务提供的。这时,当前服务就希望通过一个产品服务接口 ( ProductService), 基于 REST 风格调用产品微服务来获取产品的信息。然而当前的产品微服务还未 能提供相关的功能,因此当前的测试不能继续进行。

这时, Mock 测试的理念就到来了 。 Mock 测试是在测试过程中,对于某些不容易构造或者不容 易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。 简单地说,如果产品服务接口 ( ProductService)的 getProduct 方法当前无法调度产品微服务,那么 Mock 测试就可以给一个虚拟的 产品 ,让当前测试能够继续。下面举例说明。

这里假设需要获取一个产品的信息,然后产品微服务还没有能提供相关的功能。这时希望能够 构建一个虚拟的产品结果来让其他的测试流程能够继续下去。 下面用如代码清单 16-9 所示的代码来 模拟这个场景。

@MockBean
private ProductService productService = null ; 
@Test 
public void testGetProduct () { 
    //构建虚拟对象 
    Product mockProduct =new Product (); 
    mockProduct.setId ( lL); 
    mockProduct.setProductName ("product name " + l);
    mockProduct.setNote("note"+1) ; 
    //指定 Mock Bean 方法和参数 
    BDDMockito.given(this.productService.getProduct(lL)) 
        //指定返回的虚拟对象 
        .willReturn(mockProduct); 
    //进行 Mock 测试 
    Product product= productService .getProduct (lL) ; 
    Assert.assertTrue(product.getid( ) == lL); 
}

代码中注解@MockBean 代表对哪个 Spring Bean 使用 Mock测试。所以在测试方法 testGetProduct 中,先是构建虚拟对象,因为按假设 ProductService 并不能提供服务,所以就只能先模拟产 品 ( mockProduct)。模拟对象后,使用 Spring Boot 引入的 Mockito 来指定 Mock Bean、方法和参数,并 指定返回的虚拟对象。这样当进行测试产品服务(ProductService)时,在调用 getProduct 方法且参 数为 l 的情况下,它就会返回之前构建的虚拟对象。图 16-6 所示是我测试的结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6sOWYstr-1587087989255)(C:\Users\USER\AppData\Roaming\Typora\typora-user-images\1585294027441.png)]
从图 16-6 可以看到, 产品服务将返回模拟构建的虚拟对象。这样在一些难以模拟和构建的场景 下就可以使用模拟对象进行后续的测试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值