JUnit 入門


JUNIT了解學習

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

轉自: JUnit 入門教程

 

JUnit高級用法之@RunWith

@RunWith

關於@RunWith注解,官方文檔是這么描述的:

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.

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並完成測試:

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自帶的,意為套件,顧名思義,就是一套東西。通過它,可以把多個相關的測試類看做一個測試套件一起測試。看個例子:

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”、10、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.

 


免責聲明!

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



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