單元測試工具(連載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

