欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Springboot---mockMVC进行Controller单元测试

程序员文章站 2022-04-30 09:43:22
...

知识准备

1、注解含义

@RunWith(SpringRunner.class):表示使用Spring Test组件进行单元测试,其中SpringRunner继承类SpringJUnit4ClassRunner。 
@WebAppConfiguration:测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的;value指定web应用的根; 
@AutoConfigureMockMvc:注入一个MockMvc实例;

注入web环境的ApplicationContext容器:mvc = MockMvcBuilders.webAppContextSetup(context).build(); 
2、fastjson转换--对象字段值为null的json化

        //方式一:
        /**
         * 自定义jason转换字段过路规则
         */
        ValueFilter filter = new ValueFilter() {
            @Override
            public Object process(Object object, String name, Object value) {
                if (value == null)
                    return "";//如果字段的值为空则用空字符串代替
                return value;
            }
        };
        JSONObject.toJSONString(lisList,filter )
 
        // 方式二:       
        // 如果lisList字段的值为空,则用null表示
        JSONObject.toJSONString(lisList,SerializerFeature.WriteMapNullValue)

单元测试

依赖

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

代码 

1、jsonPath("$.result")的规则项目地址:https://github.com/json-path/JsonPath

2、使用ObjectMapper等同于对Girl对象直接赋值再转换为json格式。

@RunWith(SpringRunner.class)
@SpringBootTest// 此处若不指定,则默认为src/main/java/下面的启动类
public class TermControllerTest {

    private MockMvc mockMvc;

    private static final String PREFIX_URI = "XXXXXXX";

    /**
     * 注入WebApplicationContext
     */
    @Autowired
    private WebApplicationContext webApplicationContext;

    /**
     * 模拟MVC对象,通过MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build()初始化
     */
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(
                this.webApplicationContext).build();
    }

    /**
     * 获取所有字典类别,类型为basicitems
     * 
     * @throws Exception
     */
    @Test
    public void loadTermsByTypes() throws Exception {

        String uri = PREFIX_URI + "/basic/getList" + "?types=changestatus";
        
        // 构造返回值对象
        Response<List<List<Map<String, String>>>> fpoResponse = new FpoResponse<>();
        Response.setCode("xxx");
        ... ...
        
        // 模拟GET请求
        MvcResult mvcResult = this.mockMvc
                .perform(MockMvcRequestBuilders.get(uri))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(// 校验自定义的状态码
                        MockMvcResultMatchers.jsonPath("$.code").value(
                                "xxxxxxx"))
                .andExpect(
                        MockMvcResultMatchers.content().contentType(
                                MediaType.APPLICATION_JSON_UTF8))
                .andExpect(
                        MockMvcResultMatchers.jsonPath("$.result").value(// 获取所有结果
                                JSONObject.toJSONString(lisList,// 指定空字符串也进行转换
                                        SerializerFeature.WriteMapNullValue)))
                .andReturn();// 获取返回结果
        /**
         * 自定义jason转换字段过路规则
         */
        ValueFilter filter = new ValueFilter() {
            @Override
            public Object process(Object object, String name, Object value) {
                if (value == null)
                    return "";
                return value;
            }
        };
        
        // 打印返回结果
        System.out.println(JSONObject.toJSONString(Response,
                SerializerFeature.WriteMapNullValue));
        System.out.println(mvcResult.getResponse().getContentAsString());
    }


    @Test
    public void addGirl() throws Exception {
//        Girl girl = new Girl();
//        girl.setCupSize("B");
//        girl.setAge(19);
//        girl.setMoney(22.22);

        //ObjectMapper 是一个可以重复使用的对象
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = "{\"cupSize\":\"B\", \"age\":19, \"money\":22.22}";
        //将JSON字符串值转换成 Girl对象里的属性值
        Girl girl = mapper.readValue(jsonString, Girl.class);
        mvc.perform(MockMvcRequestBuilders.post("/girlsss")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
        /* 使用writeValueAsString() 方法来获取对象的JSON字符串表示 */
                .content(mapper.writeValueAsString(girl)))
                .andExpect(MockMvcResultMatchers.status().isOk())
                              .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8))
                 .andExpect(MockMvcResultMatchers.jsonPath(
                        "$.cupSize").value("B"))
                .andExpect(MockMvcResultMatchers.jsonPath(
                        "$.age").value(19))
                .andExpect(MockMvcResultMatchers.jsonPath(
                        "$.money").value(22.22));
    }
}

单元测试事务

junit 单元测试事务会自动回滚。通过@Rollback(true)注解来实现,默认是true,事务会回滚,可以不写。false时事务不会回滚,数据会写到数据库中。

@Transactional也去掉再运行测试,结果在数据库中查询到了被插入的数据

结论:在JUnit4的测试方法中打事务注解@Transactional,默认会按照@Rollback(true)来进行处理,无论如何都会回滚,打不打注解@Rollback@Rollback(true)已经不重要了。而在@Transactional的基础上加上@Rollback(false)之后,效果就好像是单单这个测试函数这一层没有打事务似的,而不会传播到嵌套的service层的事务内,即如果在service层的事务中,插入数据后又发生异常,最终在service层里还是会进行rollback,数据并不会插入到数据库中。

Case 1: 插入成功后会回滚
        输出:insert :1 rows!
             Rolled back transaction after test execution for test context......
    @Test
    @Transactional
    public void testTx() throws Exception{
        User user = new User();
        user.setUsername("zhuqiuhui-testTx");
        user.setAddress("河南省上蔡县-testTx");
        user.setBirthday(new Date());
        user.setSex("男-testTx");
        int num = iUserService.insertUser(user);
        assert(num==1);
        System.out.println("insert :" + num + " rows!");
    }
  
Case 2: 正常插入,此过程若(1)处抛出异常(不论Exception、RuntimeException或者FileNotFoundException等)均正常插入,事务并不回滚
    @Test
    @Transactional
    @Rollback(false)
    public void testTx() throws Exception{
        User user = new User();
        user.setUsername("zhuqiuhui-testTx");
        user.setAddress("河南省上蔡县-testTx");
        user.setBirthday(new Date());
        user.setSex("男-testTx");
        int num = iUserService.insertUser(user);--------(1)
        assert(num==1);
        System.out.println("insert :" + num + " rows!");
    }