springboot2.1.3+Junit4 單元測試


引入依賴的包:

<dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>2.23.4</version>
      <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>

 

MockMvc + PowerMock + Mockito 來模擬post get請求,並mock掉service中存在doGet doPost外部系統的restful請求

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@prepareForTest({
        HttpUtil.class             // doGet doPost 使用util類,這里需要告訴UT
})
@PowerMockIgnore("javax.crypto.*")
@AutoConfigureMockMvc
@SpringBootTest
public class DemoSzlTest {
      @Autowired
      private WebApplicationContext webApplicationContext;
      private MockMvc mockMvc;
      
      @Before
      public void setUp() throws Exceptioin {
           MockitoAnnotations.initMocks(this); // 初始化裝載powerMockito
           mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
      }

      @Test
      @DirtiesContext
      public void testPostDemo() throws Exception {
            // mock HttpResponse 對象返回
            StringEntity se = new StringEntity("xxxxxx", "utf-8");
            HttpResponse resp = new BasicHttpResponse(new BasicStatusLine(HttpVersion.Http_1_1, HttpServletResponse.SC_OK, ""));
            resp.setEntity(se);
            PowerMockito.mockStatic(HttpUtil.class);
// 這里的 Mockito.anyString() 要和 實際方法參數保持一致
PowerMockite.when(HttpUtil.doGet(Mockito.anyString(), Mockito.anyMap(), Mockito.any())).thenReturn(resp); // 這里不 mock方法,直接走真實場景方法
PowerMockito.when(HttpUtil.toJson(Mockito.anyObject())).thenCallRealMethod();
// body contents String jsonBody = "{\"aaaa\":\"1111\"}"; String token = "xxxxx"; MvcResult resultMsg = mockMvc.perform( post("/xxx") .contentType(MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON_UTF8_VALUE) .header("xxxxx", token) .content(jsonBody) // request body ).andExpect(status().isCeated()) .andReturn(); }


    @Test
    @DirtiesContext
    public void contextLoads() throws Exception {
        // 這里定義的 header 值 必須要和service層使用的值保持一致,否則powerMock mock時會有問題
        // 其實也很好理解,既然要mock,那就做到真實一點,一切都保持一模一樣即可。
        Header[] header = {
                new BasicHeader("Authorization", "aaabbbccc"),
                new BasicHeader("Content-Type", "application/json")
            };
        
        // 手動創建HttpResponse,來模式http請求的返回值
        StringEntity se = new StringEntity("{"
                + "\"memo\":\"這個只是demo式樣\"}", "utf-8");
        HttpResponse resp = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
                HttpServletResponse.SC_OK, ""));
        resp.setEntity(se);
        
        // 和上面說明一致
        StringEntity se2 = new StringEntity("{"
                + "\"memo\":\"這個只是demo2式樣\"}", "utf-8");
        HttpResponse resp2 = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
                HttpServletResponse.SC_OK, ""));
        resp2.setEntity(se2);
        
        // 開始mock我們想要的靜態方法,此處是mock post請求並返回上面我們mock的值
        PowerMockito.mockStatic(HttpUtil.class);
        /**
         * 本人是為了達到一個service里連續調用2次post請求,但2次請求內容不同,返回結果也不同,進行mock
         * Mockito.eq("/aaa")               是mock值
         * Mockito.eq("aaabbb")             是mock值
         * Mockito.any(header.getClass())   是mock Header[]值
         */
        PowerMockito.when(HttpUtil.doPost(Mockito.eq("/aaa"), Mockito.eq("aaabbb"), Mockito.any(header.getClass()))).thenReturn(resp);
        PowerMockito.when(HttpUtil.doPost(Mockito.eq("/bbb"), Mockito.eq("cccddd"), Mockito.any(header.getClass()))).thenReturn(resp2);
        
        String jsonBody = "{\"aaaa\":\"1111\"}";
        String token = "xxxxx";
        MvcResult resultMsg = mockMvc.perform(
                 post("/testUTDemo")
                 .header("Authorization", token)
                 .content(jsonBody)    // request body
        ).andExpect(status().isCreated()).andReturn();
    } }

 

Controller類:

package com.szl.demo.szldemo.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.szl.demo.szldemo.common.dto.UserDto;
import com.szl.demo.szldemo.service.UserService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class DemoController {
    @Autowired
    private UserService userService;
    
    @ResponseBody
    @RequestMapping(value = "/testUTDemo", method = RequestMethod.POST)
    public UserDto testUTDemo(HttpServletRequest request, HttpServletResponse response) {
        return userService.testDemoUnitTest();
    }
    
}

 

Service接口:

package com.szl.demo.szldemo.service;

import com.szl.demo.szldemo.common.dto.UserDto;

public interface UserService {

    UserDto testDemoUnitTest();

}

 

Service實現類:

package com.szl.demo.szldemo.service.impl;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.message.BasicHeader;
import org.springframework.stereotype.Service;
import com.szl.demo.szldemo.common.dto.UserDto;
import com.szl.demo.szldemo.common.util.HttpUtil;
import com.szl.demo.szldemo.service.UserService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service("userService")
public class UserServiceImpl implements UserService {

    public UserDto testDemoUnitTest() {
        try {
            Header[] header = {
                        new BasicHeader("Authorization", "aaabbbccc"), 
                        new BasicHeader("Content-Type", "application/json")
                    };
            
// 此處的代碼塊也是mock的重點 HttpResponse resp
= HttpUtil.doPost("/aaa", "aaabbb", header); HttpEntity entity = resp.getEntity(); String msg = IOUtils.toString(entity.getContent(), "UTF-8"); log.info("msg: " + msg);
// 同上 HttpResponse resp2
= HttpUtil.doPost("/bbb", "cccddd", header); HttpEntity entity2 = resp2.getEntity(); String msg2 = IOUtils.toString(entity2.getContent(), "UTF-8"); log.info("msg2: " + msg2); } catch (Exception e) { e.printStackTrace(); }
// 這里隨便賦值並返回,主要不是看這里的功能 UserDto dto
= new UserDto(); dto.setId(1001L); dto.setNickName("test"); dto.setUserId(10000001L); return dto; } }

 

HttpUtil.java類:

public class HttpUtil {
     public static HttpResponse doGet(String url, Map<String, String> params, Header[] headers) {
         .....
         這里省略
     }

public static JsonObject toJson(HttpResponse httpResponse) {
.....
這里省略

}
public static HttpResponse doPost(String url, String params, Header[] header) {
        // 這里只是為了演示,手動創建StringEntity並賦值,手動創建HttpResponse並返回
        HttpResponse resp = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, null));
        StringEntity se1 = new StringEntity("{"
                + "\"alg\":\"aqas\","
                + "\"id\":\"122011\"}", "utf-8");
        resp.setEntity(se1);
        return resp;
}
}

 

UserDto類: 

package com.szl.demo.szldemo.common.dto;

import java.io.Serializable;
import lombok.Data;

@Data
public class UserDto implements Serializable {
    private static final long serialVersionUID = -8858511759866491158L;
    
    private Long id;
    private Long userId;
    private String nickName;
    
}

 

 

好了,讓咋們來試試上面的UT跑出什么結果,如下圖:

 

OK, 大功告成,如有博友需要,供參考,需轉發,請注明引自鏈接,謝謝。

 

另附上額外功能,如果有朋友需要在UT里按順序執行,需要加注解:

@FixMethodOrder(xxx)

這里的xxx有三種模式:

- 默認(MethodSorters.DEFAULT)
- 按方法名(MethodSorters.NAME_ASCENDING)
- JVM(MethodSorters.JVM)

一開始本人使用JVM模式,雖然能達到預期,但運行不太穩定,本人還是推薦大家使用NAME_ASCENDING模式

 

如有朋友參考本人的筆記,有問題可以留言,轉載請注明原著,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM