基于Mockito的多层模拟单元测试 小结

1.首先,我们先来了解下Mock是个什么东西吧。Mockito教程

2.基本上一些简单unit-test的mock用法可以参照上面那个教程了。我要总结的主要是分为两个方面:

1) 通过Mock来访问Controller;

参考:基于RESTful风格的SpringMVC的测试 , Testing Spring MVC Controllers

2) 通过Mock来访问Controller,并且模拟Dao层数据入库功能。也就是在不改变原有代码的基础上,处理get请求且取消数据入库。

参考:多层mock的单元测试

3.前提:我们的项目是MVC的架构,假设分为controller, service, dao。

1) 假设controller代码如下:

package com.bas.controller;

import com.bas.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @Autowired
    private TestService service;

    @RequestMapping(value = "/test/abc", method = RequestMethod.GET)
    public Boolean cfginstFileParse() {        
        return service.parse("abc.xml");
    }

}

2) 假设service代码如下:

package com.bas.serviceImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao handler;
    @Value("${size}")
    private Integer size;


    @Override
    public  Boolean parse(String xpath){
        
        return  handler.parseXml();

    }
}

3) 假设dao代码如下:

package com.bas.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


@Service
public class TestDaoImpl implements TestDao {

    @Override
    public  Boolean parseXml(String xpath){
        
        return  true;

    }
}

4) MockUtil工具类:

package com.bas.common.util;

import com.bas.dao;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.Mockito.doNothing;


public class MockUtil {

    private volatile static MockUtil mockUtil;
    private static MockMvc mvc; // process request controller

    public MockUtil() {}

    public static MockUtil builder(WebApplicationContext wac) {
        if(mockUtil == null) {
            synchronized (MockUtil.class){
                if(mockUtil == null) {
                    mockUtil = new MockUtil();
                    mvc = MockMvcBuilders.webAppContextSetup(wac).build();
                }
            }
        }
        return mockUtil;
    }

    /**
     * Process the get request.
     * @param url controller restful api
     * @return return the result from controller
     * @throws Exception
     */
    public static MvcResult getPerformResult(String url) throws Exception {
        return mvc.perform(MockMvcRequestBuilders
                .get(url))
                .andExpect(status().isOk())
                .andReturn();
    }

    /**
     * Process the get request for parsing.
     * @param controller the app entrance we want to test
     * @param handler the object what is mocked , so it would not run
     * @param url controller restful api
     * @return return the result from controller
     * @throws Exception
     */
    public static MvcResult parsePerformResult(Object controller, Dao handler, String url) throws Exception {
        // Setup Spring test in standalone mode
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

        // stub the method handle , to do nothing when is's called.
        doNothing().when(handler).handle(new HashMap<String, List<Object>>());

        return mockMvc.perform(MockMvcRequestBuilders
                .get(url))
                .andExpect(status().isOk())
                .andReturn();
    }

}

4.通过Mock来访问Controller

package com.bas.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {

    @Autowired
    private WebApplicationContext wac;
    private MockMvc mvc;

    @Before
    public void before() {
        mvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void unitTest() {
        try {
            MvcResult result = MockUtil.builder(wac).getPerformResult("/test/abc");
            System.out.println(result.getResponse().getContentAsString());
        } catch (Exception e) {
		
        }
    }

}

5.通过Mock来访问Controller、dao层模拟的多层Mock

package com.bas.test;

import com.bas.TestController;
import com.bas.TestServiceImpl;
import com.bas.TestDaoImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {
	
	/* start to mock parse from controller api */
	@Before
	public void before() {
		// initialize mock object , without it would cause in failed mock
		MockitoAnnotations.initMocks(this);
	}

	@Autowired
	private TestController testController;
	@InjectMocks
	@Autowired
	TestServiceImpl mockService;
	@Mock
	TestDaoImpl mockHandler;

	@Test
	public void mockControllerTest() {
		try {
            // ReflectionTestUtils.setField(mockService, "size", 1);
			// run controller , mock service and handler
			MvcResult result = MockUtil.parsePerformResult(testController, mockHandler, "/test/abc");
			System.out.println(result.getResponse().getContentAsString());
		}catch (Exception e) {
		
		}
	}
}

注:

1.@InjectMocks, @Autowired 注入的是TestServiceImpl而不是TestService,因为Mock会创建一个实例而interface是不可以的;

2.MockitoAnnotations.initMocks(this); 如果这一句漏掉了会导致dao真实的执行而模拟失败;

3.细心的可以看到TestServiceImpl的@Value("${size}"),这个如果在service用到的话需要在test这样注入:ReflectionTestUtils.setField(mockService, "size", 1)。

附:

1.可模拟的数据类型 教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值