JUnit 入門

JUNIT瞭解學習

轉自:關於Java單元測試,你須要知道的一切html

轉自: JUnit 入門教程java

 

JUnit高級用法之@RunWith

@RunWith

關於@RunWith註解,官方文檔是這麼描述的:spring

When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit.api

JUnit用例都是在Runner(運行器)來執行的。經過它,能夠爲這個測試類指定一個特定的Runner。那麼大多數時候咱們都沒有使用@RunWith這個註解,這是爲何呢?其實,JUnit中有一個默認的Runner,它的名字叫BlockJunit4ClassRunner,但這是在JUnit4.4以後才引入的,對於4.4以前版本的JUnit,它的名字叫Junit4ClassRunner。在新版本的源代碼中已經添加了註釋來講明這個問題:數組

/** * @deprecated Included for backwards compatibility with JUnit 4.4. Will be * removed in the next major release. Please use * {@link BlockJUnit4ClassRunner} in place of {@link JUnit4ClassRunner}. */ @Deprecated public class JUnit4ClassRunner extends Runner implements Filterable, Sortable { ...

寫過關於Spring項目的單元測試的同窗可能見過這樣的寫法,就是用JUnit加載Spring的配置文件以完成Context的初始化,而後從Context中取出Bean並完成測試:app

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) public class UserManagerTest {   @Autowired   ApplicationContext ctx;   @Test   public void testAddUser() {     try {       UserManager userManager = ctx.getBean(UserManager.class);       userManager.addUser();     } catch (Exception e) {       e.printStackTrace();     }   } }

注意這裏使用了@RunWith註解,代表這個類中的測試用例須要使用SpringJUnit4ClassRunner類來執行。框架

@RunWith(Suite.class)

其做用是使用JUnit執行一個測試套件。Suite類是JUnit自帶的,意爲套件,顧名思義,就是一套東西。經過它,能夠把多個相關的測試類看作一個測試套件一塊兒測試。看個例子:ide

import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ TestA.class, TestB.class, /*Any test class you want to run*/}) public class TestSuite { // Please note this case won't run. It will only run cases which // are configured in @Suite.SuiteClasses @Test public void testPrint() { System.out.println("Hello"); } }

@RunWith指定了Suite類,說明這個TestSuite類是一個套件。經過@Suite.SuiteClasses指定了要執行的測試類(這些類中的全部用例都會執行)。函數

須要注意的是,這個TestSuite類自己用例則不會執行了(如上面的testPrint()方法)。單元測試

@RunWith(Parameterized.class)

Parameterized類也是JUnit自帶的,用於使用多個參數組合屢次執行同一個測試用例。看下面的例子:

import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.List; 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 TestParameterized { private int expected; private int first; private int second; public TestParameterized(int expected, int firstNumber, int secondNumber) { this.expected = expected; this.first = firstNumber; this.second = secondNumber; } /** * Note: @Parameters annotated method must be public static, * otherwise an Exception will thrown. */ @Parameters public static List<Integer[]> parameters() { return Arrays.asList(new Integer[][]{{3, 1, 2}, {5, 2, 3}, {7, 3, 4}, {9, 4, 5}}); } @Test public void testAdd() { String format = "Using parameters: expect=%d, first=%d, second=%d"; System.out.println(String.format(format, expected, first, second)); Feature feature = new Feature(); assertEquals(expected, feature.add(first, second)); } @Test public void testPrint() { String format = "Print ----------: expect=%d, first=%d, second=%d"; System.out.println(String.format(format, expected, first, second)); } } class Feature { public int add(int i1, int i2) { return i1 + i2; } }

執行結果以下:

能夠看到,雖然TestParameterized類中只有兩個測試用例testAdd和testPrint,可是結果輸出了8行,即每一個用例都執行了4遍。

使用Parameterized註解須要注意幾點:

  • 該方法要有構造函數
  • 有一個public static的方法被@Parameters標註,而且該方法只能返回Iterable類型或數組類型的數據(源代碼是以下處理的)
if (parameters instanceof Iterable) { return (Iterable<Object>) parameters; } else if (parameters instanceof Object[]) { return Arrays.asList((Object[]) parameters); } else { throw parametersMethodReturnedWrongType(); }

由於上面的方式使用了構造方法來初始化數據,其實也可使用字段注入來代替構造方法,只需稍加改變TestParameterized類便可:

  1. 用Parameter參數來修飾屬性。注意:索引從0開始
  2. 屬性要用public修飾
@Parameter(0) public int expected; @Parameter(1) public int first; @Parameter(2) public int second;

@RunWith(Categories.class)

顧名思義,執行一個「類別」。和Suite相似,只是Suite是執行指定類中的全部用例,而Categories執行的範圍更小,是在Suite的基礎上只執行指定的「類別」的用例。這就須要事先在各個測試用例上用@Category標註該用例屬於那些「類別」,以後即可以經過類別來選擇執行某些用例。看例子:

/*-----TestA.java-----*/ import org.junit.Test; import org.junit.experimental.categories.Category; class Feature1 {} class Feature2 {} public class TestA { @Test @Category(Feature1.class) public void testAdd() { System.out.println("A.testAdd"); } @Test @Category(Feature2.class) public void testAdd2() { System.out.println("A.testAdd2"); } @Test @Category({Feature1.class, Feature2.class}) public void testAdd3() { System.out.println("A.testAdd3"); } } /*-----TestCategory.java-----*/ import org.junit.experimental.categories.Categories; import org.junit.experimental.categories.Categories.ExcludeCategory; import org.junit.experimental.categories.Categories.IncludeCategory; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Categories.class) @IncludeCategory(Feature1.class) @ExcludeCategory(Feature2.class) @Suite.SuiteClasses({ TestA.class, /*Any test class you want to run*/}) public class TestCategory { // Do nothing }

其中,Feature1和Feature2表明兩個不一樣的「類型」,TestA類中經過@Category標註了各個用例(能夠爲一個用例指定多個Category,例如上方的testAdd3方法)。@IncludeCategory指明瞭須要執行的類型,而@ExcludeCategory指明瞭不但願執行的類型,這個註解對於過濾相似testAdd3這樣有多個類型的用例頗有效。如下是執行結果:

能夠看到,只有標註了Feature1的用例執行了,並且帶有Feature2的則被過濾掉了,因此只剩下testAdd這一個用例了。

@RunWith(Theories.class)

提供一組參數的排列組合值做爲待測方法的輸入參數。同時注意到在使用Theories這個Runner的時候,咱們的待測方法能夠擁有輸入參數,而這在其它的Runner中的測試方法是不行的。下面是一個例子:

import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; @RunWith(Theories.class) public class TestTheories { @DataPoint public static String nameValue1 = "Tony"; @DataPoint public static String nameValue2 = "Jim"; @DataPoint public static int ageValue1 = 10; @DataPoint public static int ageValue2 = 20; @Theory public void testMethod(String name, int age){ System.out.println(String.format("%s's age is %s", name, age)); } }

上面的代碼的意思是,將」Tony」、」Jim」、十、20四個參數以類型合法的排列組合傳給待測方法。所以輸出的結果必然也有2x2=4種。下面是執行結果:

不過,爲了簡單,咱們除了可使用@DataPoint註解來提供參數以外,還能夠經過@DataPoints註解來提供參數,參照上述代碼,只須要將@DataPoint註解標註的四個字段參數替換爲以下的兩個便可:

@DataPoints public static String[] names = {"Tony", "Jim"}; @DataPoints public static int[] ageValue1 = {10, 20};

總結

介紹了這麼幾種Runner,如今回過頭來看看一開始提到的SpringJUnit4ClassRunner,其實這個類繼承與JUnit默認的運行器BlockJUnit4ClassRunner,來看看源代碼中的聲明(官方文檔):

public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {

繼承的好處就是能夠徹底保留默認的功能,而且提供了一套支持Spring上下文的框架,正如官方文檔所說:

SpringJUnit4ClassRunner is a custom extension of JUnit's BlockJUnit4ClassRunner which provides functionality of the Spring TestContext Framework to standard JUnit tests by means of the TestContextManagerand associated support classes and annotations.

相關文章
相關標籤/搜索