SpringBoot2 整合測試元件,七種測試手段對比

一、背景描述

在版本開發中,時間段大致的劃分為:需求,開發,測試;

需求階段:理解需求做好介面設計;

開發階段:完成功能開發和對接;

測試上線:自測,提測,修復,上線;

實際上開發階段兩個核心的工作,開發和流程自測,自測的根本目的是為自己提前解決可能出現的問題;如果缺少自測和提測兩個關鍵步驟,那麼問題就會被傳遞給更多的使用者,產生更多的資源消耗;

自測是於開發而言,提測是對專業的測試人員而言,如果儘可能在自測階段就發現問題,並解決問題,那麼一個問題就不會影響到團隊協作上的更多人員,

如果一個簡單的問題上升到團隊協作層面,很可能會導致問題本身被放大

工欲善其事必先利其器,開發如果要做好自測流程,學會使用工具提高效率是十分關鍵的,自測的關鍵在於發現問題和解決問題,所以選擇好用和高效的工具可以極大的降低自測的時間消耗。

下面圍繞幾個自己開發過程中常用的測試工具和手段,做簡單的總結,

不在於對比方式的好壞,存在即合理,在不同場景中對合理手段的選擇,快速解決問題才是根本目的

二、PostMan工具

PostMan很常用的介面測試工具,開發過程中快速測試介面,功能強大並且簡單方便,不但可以單個介面測試,也可以對介面分塊管理批次執行:

SpringBoot2 整合測試元件,七種測試手段對比

整體來說工具比較好用,適應於開發階段的介面快速測試,或者在解決問題的過程中單個介面的測試,同時對測試引數有儲存和記憶能力,這也是受歡迎的一大原因。

但是該工具不適應於複雜的流程化測試,例如需要根據上次介面的響應報文做分別處理,或者下次請求需要填充某個介面響應的資料。

三、Swagger文件

Swagger管理介面文件,是當下服務中很常用的元件,透過對介面和物件的簡單註釋,快速生成介面描述資訊,並且可以對介面傳送請求,協助除錯,該文件在前後端聯調中極大的提高效率。

介面文件的管理本身是一件麻煩事,介面通常會根據業務不斷的調整,如果單獨維護一份介面文件,需要付出很多時間成本,並且容易出問題,利用swagger就可以避免這個問題。

藉助swagger註解標記物件

@TableName(“jt_activity”)@ApiModel(value=“活動PO物件”, description=“活動資訊表【jt_activity】”)public class Activity {    @ApiModelProperty(value = “主鍵ID”)    @TableId(type = IdType。AUTO)    private Integer id;    @ApiModelProperty(value = “活動主題”)    private String activityTitle;    @ApiModelProperty(value = “聯絡號碼”)    private String contactPhone;    @ApiModelProperty(value = “1線上、2線下”)    private Integer isOnline;    @ApiModelProperty(value = “舉辦地址”)    private String address;    @ApiModelProperty(value = “主辦單位”)    private String organizer;    @ApiModelProperty(value = “建立時間”)    private Date createTime;}

藉助swagger註解標記介面

@Api(tags = “活動主體介面”)@RestControllerpublic class ActivityWeb {    @Resource    private ActivityService activityService ;    @ApiOperation(“新增活動”)    @PostMapping(“/activity”)    public Integer save (@RequestBody Activity activity){        activityService。save(activity) ;        return activity。getId() ;    }    @ApiOperation(“主鍵查詢”)    @GetMapping(“/activity/{id}”)    public Activity getById (@PathVariable(“id”) Integer id){        return activityService。getById(id) ;    }    @ApiOperation(“修改活動”)    @PutMapping(“/activity”)    public Boolean updateById (@RequestBody Activity activity){        return activityService。updateById(activity) ;    }}

SpringBoot2 整合測試元件,七種測試手段對比

通常來說,基於swagger註解標記介面類和方法上的入參和關鍵返參物件即可,這樣可以避免再單獨維護介面文件。

Swagger介面文件在開發的過程中更多是扮演文件的角色,真正使用swagger去除錯的介面也常是一些增刪改查的簡單介面,這個工具也同樣不適應於複雜流程的測試。

四、TestRestTemplate類

SpringBoot測試包中整合的測試API,需要依賴測試包,可以訪問控制層介面,非常方便的完成互動過程:

Jar包依賴

    org。springframework。boot    spring-boot-starter-test    test

使用案例

@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。RANDOM_PORT)public class ActivityTest01 {    protected static Logger logger = LoggerFactory。getLogger(ActivityTest01。class) ;    @Resource    private TestRestTemplate restTemplate;    private Activity activity = null ;    @Before    public void before (){        activity = restTemplate。getForObject(“/activity/{id}”, Activity。class,1);        logger。info(“\n”+JSONUtil。toJsonPrettyStr(activity));    }    @Test    public void updateById (){        if (activity != null){            activity。setCreateTime(new Date());            activity。setOrganizer(“One商家”);            restTemplate。put(“/activity”,activity);        }    }    @After    public void after (){        activity = restTemplate。getForObject(“/activity/{id}”, Activity。class,1);        logger。info(“\n”+JSONUtil。toJsonPrettyStr(activity));        activity = null ;    }}

在TestRestTemplate原始碼中可以發現,基於RestTemplate做封裝,很多功能的實現都是呼叫RestTemplate方法。

用寫程式碼的方式去實現介面測試,靈活度非常高,可以根據流程做定製開發,很適應於中等複雜的場景測試,這裡為什麼這樣描述,下面對比Http請求再細說。

五、Http請求模式

透過模擬介面的Http請求實現的方式,目前來說個人感覺靈活的最高的方式,先看簡單的案例:

@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。DEFINED_PORT)public class ActivityTest03 {    protected static Logger logger = LoggerFactory。getLogger(ActivityTest03。class) ;    protected static String REQ_URL = “服務地址+埠”;    @Test    public void testHttp (){        // 查詢        String getRes = HttpUtil。get(REQ_URL+“activity/1”);        logger。info(“\n {} ”,JSONUtil。toJsonPrettyStr(getRes));        Activity activity = JSONUtil。toBean(getRes, Activity。class) ;        // 新增        activity。setId(null);        activity。setOrganizer(“Http商家”);        String saveRes = HttpUtil。post(REQ_URL+“/activity”,JSONUtil。toJsonStr(activity));        logger。info(“\n {} ”,saveRes);        // 更新        activity。setId(Integer。parseInt(saveRes));        activity。setOrganizer(“Put商家”);        String putRes = HttpRequest。put(REQ_URL+“/activity”)                。body(JSONUtil。toJsonStr(activity))。execute()。body();        logger。info(“\n {} ”,putRes);    }}

這種方式對於複雜的業務流程來說非常好用,當然這裡不排除個人習慣,在測試複雜流程的時候,一個簡單方案:

使用者資訊:模擬http中token資料;

業務流程:透過資料獲取包裝引數模型;

獨立服務管理,模擬併發場景;

根據執行過程生成分析資料結果;

對於複雜業務流程的測試,每個節點的模擬都具有一定的難度,通常在完整的流程中涉及到的服務和庫表都是多個,並且請求鏈路複雜,基於一個靈活的自動化流程,去測試完整的鏈路,可以對效率有極大的提升。

六、Service層測試

針對服務層的測試手段,其本意在於業務實現的邏輯測試:

@RunWith(SpringRunner。class)@SpringBootTest(classes = Application。class)public class ActivityTest04 {    protected static Logger logger = LoggerFactory。getLogger(ActivityTest04。class) ;    @Autowired    private ActivityService activityService ;    @Test    public void testService (){        // 查詢        Activity activity = activityService。getById(1) ;        // 新增        activity。setId(null);        activityService。save(activity) ;        // 修改        activity。setOrganizer(“Ser商家”);        activityService。updateById(activity) ;        // 刪除        activityService。removeById(activity。getId()) ;    }}

該測試在實際的開發過程也並不常用,偶爾在於某個業務方法實現難度很大,用來針對性測試。

七、MockMvc方式

MockMvc同樣是SpringBoot整合測試包提供的測試方式,透過物件的模擬,驗證介面是否符合預期:

@AutoConfigureMockMvc@RunWith(SpringRunner。class)@SpringBootTest(webEnvironment = SpringBootTest。WebEnvironment。MOCK)public class ActivityTest02 {    protected static Logger logger = LoggerFactory。getLogger(ActivityTest02。class) ;    @Resource    private MockMvc mockMvc ;    private Activity activity = null ;    @Before    public void before () throws Exception {        ResultActions resultAction = mockMvc。perform(MockMvcRequestBuilders。get(“/activity/{id}”,1)) ;        MvcResult mvcResult = resultAction。andReturn() ;        String result = mvcResult。getResponse()。getContentAsString();        activity = JSONUtil。toBean(result,Activity。class) ;    }    @Test    public void updateById () throws Exception {        activity。setId(null);        activity。setCreateTime(new Date());        activity。setOrganizer(“One商家”);        ResultActions resultAction = mockMvc。perform(MockMvcRequestBuilders。post(“/activity”)                                            。contentType(MediaType。APPLICATION_JSON)                                            。content(JSONUtil。toJsonStr(activity))) ;        MvcResult mvcResult = resultAction。andReturn() ;        String result = mvcResult。getResponse()。getContentAsString();        activity。setId(Integer。parseInt(result));        logger。info(“result : {} ”,result);    }    @After    public void after () throws Exception {        activity。setCreateTime(new Date());        activity。setOrganizer(“Update商家”);        ResultActions resultAction = mockMvc。perform(MockMvcRequestBuilders。put(“/activity”)                。contentType(MediaType。APPLICATION_JSON)                。content(JSONUtil。toJsonStr(activity))) ;        MvcResult mvcResult = resultAction。andReturn() ;        String result = mvcResult。getResponse()。getContentAsString();        logger。info(“result : {} ”,result);    }}

對於這種Mock型別的測試,非常專業,通常個人使用極少,暫時沒有Get到其精髓思想。

八、Mockito測試

Mock屬於非常專業和標準的測試手段,需要依賴powermock包:

    org。powermock    powermock-core    test    org。powermock    powermock-api-mockito2    test    org。powermock    powermock-module-junit4    test

簡單使用案例:

@RunWith(PowerMockRunner。class)@SpringBootTestpublic class ActivityTest05 {    @Test    public void testMock (){        Set mockSet = PowerMockito。mock(Set。class);        PowerMockito。when(mockSet。size())。thenReturn(10);        int actual = mockSet。size();        int expected = 15 ;        Assert。assertEquals(“返回值不符合預期”,expected, actual);    }    @Test    public void testTitle (){        String expectTitle = “Mock主題” ;        Activity activity = PowerMockito。mock(Activity。class);        PowerMockito。when(activity。getMockTitle())。thenReturn(expectTitle);        String actualTitle = activity。getMockTitle();        Assert。assertNotEquals(“主題相符”, expectTitle, actualTitle);    }}

可以透過Mock方式,快速模擬出複雜的物件結構,以便構建測試方法,由於使用很少,同樣個人暫時沒Get到點。

推薦閱讀

GitHub原始碼和分類管理,持續更新

SpringBoot2 整合ElasticJob框架,定製化管理流程

SpringBoot2 整合 Zookeeper元件,管理架構中服務協調

SpringBoot2 整合 Drools規則引擎,實現高效的業務規則

SpringBoot2 整合MinIO中介軟體,實現檔案便捷管理

SpringBoot2 配置多資料來源,整合MybatisPlus增強外掛

SpringBoot2 整合測試元件,七種測試手段對比