場景:在項目開發中要測試springboot工程中一個幾個dao和service的功能是否正常,初期是在web工程中進行要素的錄入,工做量太大。使用該單元測試大大減少了工做強度。java
Junit這種老技術,如今又拿出來講,不爲別的,某種程度上來講,更是爲了要說明它在項目中的重要性。
憑本人的感受和經驗來講,在項目中徹底按標準都寫Junit用例覆蓋大部分業務代碼的,應該不會超過一半。web
恰好前段時間寫了一些關於SpringBoot的帖子,正好如今把Junit再拿出來從幾個方面再說一下,也算是給一些新手參考了。spring
那麼先簡單說一下爲何要寫測試用例
1. 能夠避免測試點的遺漏,爲了更好的進行測試,能夠提升測試效率
2. 能夠自動測試,能夠在項目打包前進行測試校驗
3. 能夠及時發現由於修改代碼致使新的問題的出現,並及時解決數組
那麼本文從如下幾點來講明怎麼使用Junit,Junit4比3要方便不少,細節你們能夠本身瞭解下,主要就是版本4中對方法命名格式再也不有要求,再也不須要繼承TestCase,一切都基於註解實現。springboot
建立一個普通的Java類,在Junit4中再也不須要繼承TestCase類了。
由於咱們是Web項目,因此在建立的Java類中添加註解:框架
@RunWith(SpringJUnit4ClassRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持! @SpringApplicationConfiguration(classes = SpringBootSampleApplication.class) // 指定咱們SpringBoot工程的Application啓動類 @WebAppConfiguration // 因爲是Web項目,Junit須要模擬ServletContext,所以咱們須要給咱們的測試類加上@WebAppConfiguration。
接下來就能夠編寫測試方法了,測試方法使用@Test註解標註便可。
在該類中咱們能夠像日常開發同樣,直接@Autowired來注入咱們要測試的類實例。
下面是完整代碼:oop
package org.springboot.sample; import static org.junit.Assert.assertArrayEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.springboot.sample.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; /** * * @author 單紅宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2016年2月23日 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringBootSampleApplication.class) @WebAppConfiguration public class StudentTest { @Autowired private StudentService studentService; @Test public void likeName() { assertArrayEquals( new Object[]{ studentService.likeName("小明2").size() > 0, studentService.likeName("壞").size() > 0, studentService.likeName("莉莉").size() > 0 }, new Object[]{ true, false, true } ); // assertTrue(studentService.likeName("小明2").size() > 0); } }
接下來,你須要新增無數個測試類,編寫無數個測試方法來保障咱們開發完的程序的有效性。post
//在全部測試方法前執行一次,通常在其中寫上總體初始化的代碼
@BeforeClass單元測試
//在全部測試方法後執行一次,通常在其中寫上銷燬和釋放資源的代碼
@AfterClass測試
//在每一個測試方法前執行,通常用來初始化方法(好比咱們在測試別的方法時,類中與其餘測試方法共享的值已經被改變,爲了保證測試結果的有效性,咱們會在@Before註解的方法中重置數據)
@Before
//在每一個測試方法後執行,在方法執行完成後要作的事情
@After
// 測試方法執行超過1000毫秒後算超時,測試將失敗
@Test(timeout = 1000)
// 測試方法指望獲得的異常類,若是方法執行沒有拋出指定的異常,則測試失敗
@Test(expected = Exception.class)
// 執行測試時將忽略掉此方法,若是用於修飾類,則忽略整個類
@Ignore("not ready yet")
@Test
@RunWith
在JUnit中有不少個Runner,他們負責調用你的測試代碼,每個Runner都有各自的特殊功能,你要根據須要選擇不一樣的Runner來運行你的測試代碼。
若是咱們只是簡單的作普通Java測試,不涉及Spring Web項目,你能夠省略@RunWith註解,這樣系統會自動使用默認Runner來運行你的代碼。
Junit爲咱們提供的參數化測試須要使用 @RunWith(Parameterized.class)
然而由於Junit 使用@RunWith指定一個Runner,在咱們更多狀況下須要使用@RunWith(SpringJUnit4ClassRunner.class)來測試咱們的Spring工程方法,因此咱們使用assertArrayEquals 來對方法進行多種可能性測試即可。
下面是關於參數化測試的一個簡單例子:
package org.springboot.sample; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ParameterTest { private String name; private boolean result; /** * 該構造方法的參數與下面@Parameters註解的方法中的Object數組中值的順序對應 * @param name * @param result */ public ParameterTest(String name, boolean result) { super(); this.name = name; this.result = result; } @Test public void test() { assertTrue(name.contains("小") == result); } /** * 該方法返回Collection * * @return * @author SHANHY * @create 2016年2月26日 */ @Parameters public static Collection<?> data(){ // Object 數組中值的順序注意要和上面的構造方法ParameterTest的參數對應 return Arrays.asList(new Object[][]{ {"小明2", true}, {"壞", false}, {"莉莉", false}, }); } }
正常狀況下咱們寫了5個測試類,咱們須要一個一個執行。
打包測試,就是新增一個類,而後將咱們寫好的其餘測試類配置在一塊兒,而後直接運行這個類就達到同時運行其餘幾個測試的目的。
代碼以下:
@RunWith(Suite.class) @SuiteClasses({ATest.class, BTest.class, CTest.class}) public class ABCSuite { // 類中不須要編寫代碼 }
咱們能夠直接使用這個來測試咱們的Rest API,若是內部單元測試要求不是很嚴格,咱們保證對外的API進行徹底測試便可,由於API會調用內部的不少方法,姑且把它當作是整合測試吧。
下面是一個簡單的例子
package org.springboot.sample; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; /** * * @author 單紅宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2016年2月23日 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringBootSampleApplication.class) //@WebAppConfiguration // 使用@WebIntegrationTest註解須要將@WebAppConfiguration註釋掉 @WebIntegrationTest("server.port:0")// 使用0表示端口號隨機,也能夠具體指定如8888這樣的固定端口 public class HelloControllerTest { private String dateReg; private Pattern pattern; private RestTemplate template = new TestRestTemplate(); @Value("${local.server.port}")// 注入端口號 private int port; @Test public void test3(){ String url = "http://localhost:"+port+"/myspringboot/hello/info"; MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>(); map.add("name", "Tom"); map.add("name1", "Lily"); String result = template.postForObject(url, map, String.class); System.out.println(result); assertNotNull(result); assertThat(result, Matchers.containsString("Tom")); } }
使用 OutputCapture 來捕獲指定方法開始執行之後的全部輸出,包括System.out輸出和Log日誌。
OutputCapture 須要使用@Rule註解,而且實例化的對象須要使用public修飾,以下代碼:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringBootSampleApplication.class) //@WebAppConfiguration // 使用@WebIntegrationTest註解須要將@WebAppConfiguration註釋掉 @WebIntegrationTest("server.port:0")// 使用0表示端口號隨機,也能夠具體指定如8888這樣的固定端口 public class HelloControllerTest { @Value("${local.server.port}")// 注入端口號 private int port; private static final Logger logger = LoggerFactory.getLogger(StudentController.class); @Rule // 這裏注意,使用@Rule註解必需要用public public OutputCapture capture = new OutputCapture(); @Test public void test4(){ System.out.println("HelloWorld"); logger.info("logo日誌也會被capture捕獲測試輸出"); assertThat(capture.toString(), Matchers.containsString("World")); } }
關於Assert類中的一些斷言方法,都很簡單,本文再也不贅述。
可是在新版的Junit中,assertEquals 方法已經被廢棄,它建議咱們使用assertArrayEquals,旨在讓咱們測試一個方法的時候多傳幾種參數進行多種可能性測試。