单元测试:(三) powermock + spring mvc mock 对 controller 层自动化测试入门

《单元测试:(一)在已完成的 spring + mybatis 项目中加入 dbunit 完成 dao 层自动化测试》
《单元测试:(二) 使用 mockito 对 service 层自动化测试入门》
《单元测试:(三) powermock + spring mvc mock 对 controller 层自动化测试入门》

powermock 版本 1.7.0RC4,使用 powermock 的原因是因为能对静态方法进行验证。

一 在 pom.xml 中添加 powermock 需要的库

<!-- powermock 测试框架 -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>${powermock.version}</version>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>${powermock.version}</version>
</dependency>

二 在需要使用 powermock 测试的类上方使用对应的注解 @RunWith(PowerMockRunner.class)
三 在测试类中需要模拟的对象定义上方使用 @Mock 或者 @InjectMocks 注解,其中 @Mock 是对于测试类的依赖类的注解,被 @Mock 的对象所有方法都不真实调用;@InjectMocks 是对待测试类的注解,@InjectMocks 注解过的对象所有方法都会进入方法代码执行。
四 在测试类的启动方法中使用 spring mvc mock 初始化需要被测试的 controller 类实例,用来模拟 http 请求。测试类的启动方法就是在想要作为启动方法的的类上加 @Before 注解
五 在测试方法中使用 spring mvc mock 以及 powermock 进行 mock 对象初始化、被测试对象方法的调用验证、mock依赖对象方法的调用验证等,代码片段如下:
 

import net.ioooi.practice.base.domain.Admin;
import net.ioooi.practice.base.model.AdminShortListItem;
import net.ioooi.practice.base.service.AdminService;
import net.ioooi.practice.base.util.ValidationUtil;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.util.ArrayList;
import java.util.List;

import static junit.framework.TestCase.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Created by bel on 2017/4/18.
 */
@Ignore
public class AdminControllerTest {
    protected MockMvc mockMvc;

    @Mock
    private AdminService adminService;
    @InjectMocks
    private AdminController adminController;

    @Before
    public void setUp() {
        //mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); // 使用mockito的时候不能用这个,否则不会调用mockito虚拟的对象
        //MockitoAnnotations.initMocks(this); // 使用mockito的时候用的,使用powermock的时候去掉
        mockMvc = MockMvcBuilders.standaloneSetup(adminController).build(); // 使用mockito的时候用这个,否则不会调用mockito虚拟的对象
    }

    @Test
    public void testList() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/admin/list")) // 模拟发出 http 请求
                .andExpect(MockMvcResultMatchers.view().name("admin-list")) // 请求后期待得到 admin-list 路径
                .andExpect(MockMvcResultMatchers.status().isOk()); // 请求后期待返回正常
    }

    @Test
    public void testSearch() throws Exception {
        when(adminService.getAdminShortListCountBySearchKey(anyString())).thenReturn(1);
        AdminShortListItem item = new AdminShortListItem();
        item.setStatus(1);
        item.setId(1);
        item.setUserName("testsearch");
        item.setPhone("18900000000");
        List<AdminShortListItem> list = new ArrayList<AdminShortListItem>();
        list.add(item);
        when(adminService.getAdminShortListBySearchKey(anyString(), anyInt(), anyInt())).thenReturn(list);

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/admin/search")
                .contentType(MediaType.APPLICATION_JSON) // 设置 http 请求内容类型
                .characterEncoding("UTF-8") // 设置请求编码
                .accept(MediaType.APPLICATION_JSON) // 设置请求数据类型为 json
                .param("draw", "") // 设置请求传递的参数
                .param("start", "0")
                .param("length", "10")
                .param("search", "")
                .param("recordsTotal", "0"))
                .andExpect(MockMvcResultMatchers.status().isOk()) // 验证请求后服务端返回正常状态
                .andReturn();

        String expectedJsonResult = "{\"recordsFiltered\":1,\"data\":[{\"id\":1,\"userName\":\"testsearch\",\"phone\":\"18900000000\",\"status\":1}],\"draw\":\"\",\"recordsTotal\":1}";
        assertTrue(expectedJsonResult.equals(result.getResponse().getContentAsString()));

        verify(adminService, times(1)).getAdminShortListCountBySearchKey(anyString());
        verify(adminService, times(1)).getAdminShortListBySearchKey(anyString(), anyInt(), anyInt());
    }

    @Test
    public void testChangeStatus() throws Exception {
        when(adminService.changeUserStatusByUserIDs(any(Integer[].class), anyInt())).thenReturn(true);

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/admin/change_status")
                .contentType(MediaType.APPLICATION_JSON)
                .characterEncoding("UTF-8")
                .accept(MediaType.APPLICATION_JSON)
                .param("ids[]", "-1") // 传入数组的时候不能再加方括号,例如[1,2],需要去掉1,2
                .param("status", "0"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn();
        String expectedJsonResult = "{\"status\":true,\"message\":\"修改成功\",\"data\":null}";
        assertTrue(expectedJsonResult.equals(result.getResponse().getContentAsString()));
    }

    @Test
    @PrepareForTest({ ValidationUtil.class })
    public void testAdd() throws Exception {
        when(adminService.regist(any(Admin.class))).thenReturn(true);
        PowerMockito.mockStatic(ValidationUtil.class);
        PowerMockito.when(ValidationUtil.beanValidate(any(Admin.class))).thenReturn(""); // mock调用静态方法

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/admin/add")
                .contentType(MediaType.APPLICATION_JSON)
                .characterEncoding("UTF-8")
                .accept(MediaType.APPLICATION_JSON)
                .param("userName", "testadd")
                .param("password", "testadd")
                .param("email", "testadd@test.com")
                .param("phone", "18900000000"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn();
        String expectedJsonResult = "{\"status\":true,\"message\":\"添加成功\",\"data\":null}";
        assertTrue(expectedJsonResult.equals(result.getResponse().getContentAsString()));
        PowerMockito.verifyStatic(); // 本句和下一句联合起来对静态方法进行调用
        ValidationUtil.beanValidate((any(Admin.class)));
    }

   @Test
   @PrepareForTest({ ValidationUtil.class })
    public void testModify() throws Exception {
        Admin admin = new Admin();
        when(adminService.getAdminById(anyInt())).thenReturn(admin);
        when(adminService.modify(admin)).thenReturn(true);

        PowerMockito.mockStatic(ValidationUtil.class);
        PowerMockito.when(ValidationUtil.beanValidate(any(Admin.class))).thenReturn(""); // mock调用静态方法

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/admin/modify")
                .contentType(MediaType.APPLICATION_JSON)
                .characterEncoding("UTF-8")
                .accept(MediaType.APPLICATION_JSON)
                .param("id", "1")
                .param("email", "testadd@test.com")
                .param("phone", "18900000000"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn();
        String expectedJsonResult = "{\"status\":true,\"message\":\"修改成功\",\"data\":null}";
        assertTrue(expectedJsonResult.equals(result.getResponse().getContentAsString()));

       verify(adminService).getAdminById(anyInt());
       verify(adminService).modify(admin);

       PowerMockito.verifyStatic(); // 本句和下一句联合起来对静态方法进行调用
       ValidationUtil.beanValidate((any(Admin.class)));
    }
}

 

转载于:https://my.oschina.net/ioooi/blog/1526359

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值