Spring Boot 單元測試

weixin_34120274發表於2018-08-21

一、使用mock進行單元測試

Spring框架提供了MockMvc物件,可以在服務端完成對Controller的啟動。測試開始之前需要建立測試環境,setup方法被@Before修飾。通過MockMvcBuilders工具,使用WebApplicationContext物件作為引數,建立一個MockMvc物件。
示例:

@RunWith(SpringRunner.class)
//這裡的CodedictCustomApplication是springboot的啟動類名
@SpringBootTest(classes = CodedictCustomApplication.class)
public class CodeDictControllerTest {

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mvc;

  @Before
  public void setupMockMvc(){
    //初始化MockMvc物件
    mvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }

  /**
   * mockMvc.perform執行一個請求
   * MockMvcRequestBuilders.get(“/user/1”)構造一個請求,Post請求就用.post方法
   * contentType(MediaType.APPLICATION_JSON_UTF8)代表傳送端傳送的資料格式是application/json;charset=UTF-8
   * accept(MediaType.APPLICATION_JSON_UTF8)代表客戶端希望接受的資料型別為application/json;charset=UTF-8
   * ResultActions.andExpect新增執行完成後的斷言
   * ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看請求的狀態響應碼是否為200如果不是則拋異常,測試不通過
   * ResultActions.andExpect(MockMvcResultMatchers.jsonPath("$.showValue").value("證件型別")),這裡jsonPath用來獲取showValue欄位比對是否為證件型別,不是就測試不通過
   * ResultActions.andDo新增一個結果處理器,表示要對結果做點什麼事情,比如此處使用MockMvcResultHandlers.print()輸出整個響應結果資訊
   * @throws Exception
   */
  @Test
  public void getAllCatalog() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/getAllCatalog")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
    )
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andDo(MockMvcResultHandlers.print());

  }

  @Test
  public void getItem() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/getItem")
            .param("rid","ZJLX")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
    )
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andExpect(MockMvcResultMatchers.jsonPath("$.showValue").value("證件型別"))
    .andExpect(MockMvcResultMatchers.jsonPath("$.catalogId").value("#"))
    .andDo(MockMvcResultHandlers.print());
  }

  @Test
  // 單元測試的時候如果不想造成垃圾資料,可以開啟事物功能,記在方法或者類頭部新增@Transactional註解即可
  @Transactional
  public void modifyItem()throws Exception{
    mvc.perform(MockMvcRequestBuilders.get("/modifyItem")
            .param("rid","ZJLX")
            .param("code","ZJLX")
            .param("catalogId","#")
            .param("sortValue","1")
            .param("showValue","證件")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
    )
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andDo(MockMvcResultHandlers.print());

    mvc.perform(MockMvcRequestBuilders.get("/getItem")
            .param("rid","ZJLX")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
    )
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andExpect(MockMvcResultMatchers.jsonPath("$.showValue").value("證件"))
    .andExpect(MockMvcResultMatchers.jsonPath("$.sortValue").value("1"))
    .andDo(MockMvcResultHandlers.print());
  }
}

二、新斷言assertThat使用

JUnit 4.4 結合 Hamcrest 提供了一個全新的斷言語法——assertThat。程式設計師可以只使用 assertThat 一個斷言語句,結合 Hamcrest 提供的匹配符,就可以表達全部的測試思想,我們引入的版本是Junit4.12所以支援assertThat。
基本的語法:

assertThat( [value], [matcher statement] );
  • value:是接下來想要測試的變數值;
  • matcher statement:是使用 Hamcrest 匹配符來表達的對前面變數所期望的值的宣告,如果 value 值與 matcher statement 所表達的期望值相符,則測試成功,否則測試失敗。

示例:

@Test
  public void assertThatUse(){
    //1.使用匹配符 Matcher 和不使用之間的比較
    // 想判斷某個字串 s 是否含有子字串 "developer" 或 "Works" 中間的一個
    // JUnit 4.4 以前的版本:assertTrue(s.indexOf("developer")>-1||s.indexOf("Works")>-1 );
    // JUnit 4.4:
    assertThat("s",anyOf(containsString("developer"), containsString("Works")));
    // 匹配符 anyOf 表示任何一個條件滿足則成立,類似於邏輯或 "||", 匹配符 containsString 表示是否含有引數子
    // 字串,文章接下來會對匹配符進行具體介紹

    //2.Matcher 匹配符聯合使用
    // 聯合匹配符not和equalTo表示“不等於”
    assertThat( "s", not( equalTo( "developer" )));
    // 聯合匹配符not和containsString表示“不包含子字串”
    assertThat( "s", not( containsString( "Works")));
    // 聯合匹配符anyOf和containsString表示“包含任何一個子字串”
    assertThat("s", anyOf(containsString("developer"), containsString("Works")));


    //3.預設提供一些可讀的描述性錯誤資訊
    String s = "hello world!";
    assertThat( s, anyOf( containsString("developer"), containsString("Works") ) );
    // 如果出錯後,系統會自動丟擲以下提示資訊:
    // java.lang.AssertionError:
    // Expected: (a string containing "developer" or a string containing "Works")
    // but: was "s"
    // Expected :(a string containing "developer" or a string containing "Works")
  }

其它使用示例:

字元相關匹配符
/**equalTo匹配符斷言被測的testedValue等於expectedValue,
* equalTo可以斷言數值之間,字串之間和物件之間是否相等,相當於Object的equals方法
*/
assertThat(testedValue, equalTo(expectedValue));
/**equalToIgnoringCase匹配符斷言被測的字串testedString
*在忽略大小寫的情況下等於expectedString
*/
assertThat(testedString, equalToIgnoringCase(expectedString));
/**equalToIgnoringWhiteSpace匹配符斷言被測的字串testedString
*在忽略頭尾的任意個空格的情況下等於expectedString,
*注意:字串中的空格不能被忽略
*/
assertThat(testedString, equalToIgnoringWhiteSpace(expectedString);
/**containsString匹配符斷言被測的字串testedString包含子字串subString**/
assertThat(testedString, containsString(subString) );
/**endsWith匹配符斷言被測的字串testedString以子字串suffix結尾*/
assertThat(testedString, endsWith(suffix));
/**startsWith匹配符斷言被測的字串testedString以子字串prefix開始*/
assertThat(testedString, startsWith(prefix));
一般匹配符
/**nullValue()匹配符斷言被測object的值為null*/
assertThat(object,nullValue());
/**notNullValue()匹配符斷言被測object的值不為null*/
assertThat(object,notNullValue());
/**is匹配符斷言被測的object等於後面給出匹配表示式*/
assertThat(testedString, is(equalTo(expectedValue)));
/**is匹配符簡寫應用之一,is(equalTo(x))的簡寫,斷言testedValue等於expectedValue*/
assertThat(testedValue, is(expectedValue));
/**is匹配符簡寫應用之二,is(instanceOf(SomeClass.class))的簡寫,
*斷言testedObject為Cheddar的例項
*/
assertThat(testedObject, is(Cheddar.class));
/**not匹配符和is匹配符正好相反,斷言被測的object不等於後面給出的object*/
assertThat(testedString, not(expectedString));
/**allOf匹配符斷言符合所有條件,相當於“與”(&&)*/
assertThat(testedNumber, allOf( greaterThan(8), lessThan(16) ) );
/**anyOf匹配符斷言符合條件之一,相當於“或”(||)*/
assertThat(testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
數值相關匹配符
/**closeTo匹配符斷言被測的浮點型數testedDouble在20.0¡À0.5範圍之內*/
assertThat(testedDouble, closeTo( 20.0, 0.5 ));
/**greaterThan匹配符斷言被測的數值testedNumber大於16.0*/
assertThat(testedNumber, greaterThan(16.0));
/** lessThan匹配符斷言被測的數值testedNumber小於16.0*/
assertThat(testedNumber, lessThan (16.0));
/** greaterThanOrEqualTo匹配符斷言被測的數值testedNumber大於等於16.0*/
assertThat(testedNumber, greaterThanOrEqualTo (16.0));
/** lessThanOrEqualTo匹配符斷言被測的testedNumber小於等於16.0*/
assertThat(testedNumber, lessThanOrEqualTo (16.0));
集合相關匹配符
/**hasEntry匹配符斷言被測的Map物件mapObject含有一個鍵值為"key"對應元素值為"value"的Entry項*/
assertThat(mapObject, hasEntry("key", "value" ) );
/**hasItem匹配符表明被測的迭代物件iterableObject含有元素element項則測試通過*/
assertThat(iterableObject, hasItem (element));
/** hasKey匹配符斷言被測的Map物件mapObject含有鍵值“key”*/
assertThat(mapObject, hasKey ("key"));
/** hasValue匹配符斷言被測的Map物件mapObject含有元素值value*/
assertThat(mapObject, hasValue(value));

相關文章