單元測試工具(連載3)

  • 2019 年 12 月 12 日
  • 筆記

1.4 對JUnit 4的高級操作

1.@BeforeClass和 @AfterClass

有一個類是負責對大文件(超過500MB)進行讀寫,它的每一個方法都是對文件進行操作。換句話說,調用每個方法前,都要打開一個大文件,並讀入文件內容,這絕對是一個非常耗時的操作。如果使用@Before和@After,那麼每次測試都要讀取一次文件,效率極其低下。

@BeforeClass和@AfterClass兩個標識來幫助實現這個功能。從名字上就可以看出,用這兩個Fixture標註的函數,只在測試用例初始化時執行 @BeforeClass方法,當所有測試執行完畢後,執行@AfterClass方法進行收尾工作。這裡要注意,每個測試類只能有一個方法被標註為@BeforeClass或@AfterClass,並且該方法必須是public和static。

2.防止超時

比如,程式里存在死循環,如何處理?如在「簡單計算器」產品程式碼中,有語句:

publicvoid squareRoot(int n) {

for(; ;) ; // Bug : 死循環

}

要實現這一功能,給@Test標註加一個參數即可:

@Test(timeout=1000)//1000ms

publicvoid testSquareRoot() {

calculator.squareRoot(4);

assertEquals(2,calculator.getResult());

}

也就是說,測試用例等待時間1000ms(即1s),如果1s內沒有反應,就認為測試失敗。

3.Runner(運行器)

當測試程式碼提交給JUnit 4框架後,JUnit 4框架通過Runner如何來運行測試程式碼。如果不設置,就認為是默認的,但是也可以設置:

import org.junit.internal.runners.TestClassRunner;

import org.junit.runner.RunWith;

// 使用了系統默認的TestClassRunner,與下面程式碼完全一樣

public classCalculatorTest {

...

}

@RunWith(BlockJUnit4ClassRunner.class)

public class CalculatorTest {

...

}

JUnit 4的Runner主要有BlockJUnit4ClassRunner、ParentRunner、Statement、TestRule、Description、RunNotifier、Enclosed和InvokeMethod。其中BlockJUnit4ClassRunner.class是默認的Runner。

lEnclosed:是實現內部類中測試類的運行器。

lParentRunner:是JUnit 4測試執行器的基類,它提供了一個測試器所需要的大部分功能。繼承它的類需要實現:

protectedabstract List<T> getChildren();

protectedabstract Description describeChild(T child);

protectedabstract void runChild(T child,RunNotifier notifier);

lParameterized:則可以設置參數化測試用例。

lJUnit38ClassRunner:是為了向後兼容JUnit 3而定義的運行器。

lStatement:在運行時,執行test case前可以插入一些用戶動作,它就是描述這些動作的一個類。繼承這個類要實現:

/**

*Run the action,throwing a {@code Throwable} ifanything goes wrong.

*/

publicabstract void evaluate() throws Throwable;

這個方法會先後在ParentRunner.run()和ParentRunner.runLeaf()這兩個方法裡面調用。另外,我們可以自定義一個Statement,並且實現evaluate()方法。

lTestRule:TestRule可以描述一個或多個測試方法如何運行和報告資訊的介面。在TestRule中可以額外加入一些check,我們可以讓一個test case失敗/成功,也可以加入一些setup和cleanup要做的事,也可以加入一些log之類的報告資訊。總之,跑test case之前的任何事,都可以在裡面做。需要實現apply()方法。

/**

* Modifies the method-running{@link Statement} to implement this

* test-running rule.

* @param base The {@linkStatement} to be modified

* @param description A {@linkDescription} of the test implemented in {@code base}

* @return a new statement,which may be the same as {@codebase},

* a wrapper around {@code base},or a completely new Statement.

*/

Statement apply(Statementbase,Description description);

lDescription:存儲著當前單個或多個test case的描述資訊。這些資訊跟邏輯無關,比如原數據資訊等。實例化Description用Description.createTestDescription()方法。

lRunNotifier:運行時通知器。執行Runner.run(RunNotifierrunNotifier)方法時,需要傳一個RunNotifier進去,這個RunNotifier是事件的管理器,它能幫助我們監控測試執行的情況。

lInvokeMethod:最終執行test case裡面的測試方法通過這個類來做,這個類會間接調用Method.invoke()方法通知編譯器執行@test方法。

l……

4.參數化測試

案例2:計算一個數的平方。

測試「計算一個數的平方」這個函數,暫且分3類:正數、0、負數。測試程式碼如下:

import org.junit.AfterClass;

import org.junit.Before;

import org.junit.BeforeClass;

import org.junit.Test;

import static org.junit.Assert. * ;

publicclass AdvancedTest {

privatestatic Calculator calculator=newCalculator();

@Before

publicvoid clearCalculator() {

calculator.clear();

}

@Test

public void square1() {

calculator.square(2);

assertEquals(4,calculator.getResult());

}

@Test

Public void square2() {

calculator.square(0);

assertEquals(0,calculator.getResult());

}

@Test

Public void square3() {

calculator.square(-3);

assertEquals(9,calculator.getResult());

}

}

如果用參數化實現程式碼,就簡化成如下形式:

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.junit.runners.Parameterized;

import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;

import java.util.Collection;

@RunWith(Parameterized.class )

public class SquareTest{

private static Calculator calculator = new Calculator();

private int param;

private int result;

@Parameters

public static Collection<Object[]>data {

return Arrays.asList( new Object[][] {

{2,4},

{0,0},

{-3,9},

} );

}

// 構造函數,對變數進行初始化

public void SquareTest(int param,int result) {

this .param=param;

this .result =result;

}

@Test

Public void square(){

calculator.square(param);

assertEquals(resul,calculator.getResult());

}

}

5.批量測試

對於批量測試,就是把所有的測試類打成一個包一起運行,其程式碼如下:

import org.junit.runner.RunWith;

import org.junit.runners.Suite;

@RunWith(Suite.class)

@Suite.SuiteClasses({

CalculatorTest.class,

SquareTest.class

} )

public class AllCalculatorTests{

}

星雲測試

http://www.teststars.cc

奇林軟體

http://www.kylinpet.com

聯合通測

http://www.quicktesting.net

Tags: