junit測試延伸--方法的重複測試
在實際編碼測試中,我們有的時候需要對一個方法進行多次測試,那麼怎麼辦呢?這個問題和測試套件解決的方案一樣,我們總不能不停的去右鍵run as,那怎麼辦呢?還好偉大的junit幫我們想到了。
OK,現在我們開始來寫一個例子:
測試原始碼:
測試程式碼:package org.linkinpark.junit.testjunit; /** * @建立作者: LinkinPark * @建立時間: 2016年2月5日 * @功能描述: 寫一個測試原始碼 */ public class Linkin { public String test(String str) { return str + "。。。"; } }
現在我們執行一下,然後我們看下junit控制檯什麼情況?package org.linkinpark.junit.testjunit; import org.junit.Assert; import junit.extensions.RepeatedTest; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; public class LinkinTest extends TestCase { public LinkinTest() { super(); } public LinkinTest(String name) { super(name); } public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new RepeatedTest(new LinkinTest("test"), 20)); return suite; } public void test() throws Exception { Linkin test = new Linkin(); String str = test.test("LinkinPark"); Assert.assertEquals("LinkinPark。。。", str); } }
OK,測試通過,我們也看到了,我們寫的這個test測試方法執行20次,現在我們既然會使用junit來進行方法的重複測試了。那麼junit到底是怎麼實現的呢?我在前面的junit原始碼解析整理中沒有整理到這部分。
那我們現在一起來看一下:
上面的測試程式碼使用junit38測試套件,就是自定義了Suite()方法,我不知道junit4X系列中這個重複測試能不能用,個人覺得好像是不行。先言歸正傳吧。在我們自己定義的Suite類中,我們呼叫addTest()方法來新增用例,注意這裡新增的是RepeatedTest類,那我們現在就來看看這個類的原始碼:
RepeatedTest類的父類TestDecorator原始碼如下:
package org.linkinpark.commons.extensions;
import org.linkinpark.commons.framework.Test;
import org.linkinpark.commons.framework.TestResult;
import org.linkinpark.junit.Assert;
/**
* @建立作者: LinkinPark
* @建立時間: 2016年2月5日
* @功能描述: 測試裝飾器父類
*/
public class TestDecorator extends Assert implements Test
{
protected Test fTest; // 封裝一個測試用例
public TestDecorator(Test test)
{
fTest = test;
}
/**
* The basic run behaviour.
*/
public void basicRun(TestResult result)
{
fTest.run(result);
}
public int countTestCases()
{
return fTest.countTestCases();
}
public void run(TestResult result)
{
basicRun(result);
}
@Override
public String toString()
{
return fTest.toString();
}
public Test getTest()
{
return fTest;
}
}
RepeatedTest原始碼如下:package org.linkinpark.commons.extensions;
import org.linkinpark.commons.framework.Test;
import org.linkinpark.commons.framework.TestResult;
/**
* @建立作者: LinkinPark
* @建立時間: 2016年2月5日
* @功能描述: 重複測試Test容器
*/
public class RepeatedTest extends TestDecorator
{
private int fTimesRepeat;
public RepeatedTest(Test test, int repeat)
{
super(test);
if (repeat < 0)
{
throw new IllegalArgumentException("Repetition count must be >= 0");
}
fTimesRepeat = repeat;
}
@Override
public int countTestCases()
{
return super.countTestCases() * fTimesRepeat;
}
@Override
public void run(TestResult result)
{
for (int i = 0; i < fTimesRepeat; i++)
{
if (result.shouldStop())
{
break;
}
super.run(result);
}
}
@Override
public String toString()
{
return super.toString() + "(repeated)";
}
}
2篇原始碼分析一下:
在TestDecorator父類中,封裝一個測試用例Test。在前面的一系列junit原始碼分析中,我們知道了,一個測試用例的執行,實際是TestCase類中的run()方法。其實這裡的這個TestDecorator可以理解為類似於Suite,他是一個測試用例的容器。
由於該類同樣實現了Test介面,所以執行測試也同樣是run()方法,那麼我們看到了該類的run()方法實際上呼叫的也是Test介面的run()方法,這麼說大家明白了,其實這裡只是在TestCase類上面嫁入一層,其實也是介面卡模式。
然後現在我們來看下RepeatedTest類,該子類重寫了父類的run()方法,只不過加了一個for迴圈來重複呼叫父類執行測試的方法,這樣子就實現了多次執行測試的目的。原始碼不難,我們在研究junit的原始碼過程中不得不佩服junit的設計。這塊也是我之所以
要去看一些開源框架原始碼的目的,之後我會自己寫一套自己的web框架,到時候也會把這些好的思想加入進去,使自己的程式碼真正的高內聚,低耦合。
OK,在看前面的原始碼過程中,我還發現了一個類,TestSetup,同樣也是TestDecorator的子類。大概的看了下原始碼,也明白了該類的使用。
package org.linkinpark.commons.extensions;
import org.linkinpark.commons.framework.Protectable;
import org.linkinpark.commons.framework.Test;
import org.linkinpark.commons.framework.TestResult;
/**
* A Decorator to set up and tear down additional fixture state. Subclass
* TestSetup and insert it into your tests when you want to set up additional
* state once before the tests are run.
*/
public class TestSetup extends TestDecorator
{
public TestSetup(Test test)
{
super(test);
}
@Override
public void run(final TestResult result)
{
Protectable p = new Protectable()
{
public void protect() throws Exception
{
setUp();
basicRun(result);
tearDown();
}
};
result.runProtected(this, p);
}
/**
* Sets up the fixture. Override to set up additional fixture state.
*/
protected void setUp() throws Exception
{
}
/**
* Tears down the fixture. Override to tear down the additional fixture
* state.
*/
protected void tearDown() throws Exception
{
}
}
明顯的如果我們在測試類中存在繼承關係的時候,我們當然可以直接重寫setUp()和tearDown()方法來執行測試前後的初始化,同時我們也可以使用裝飾器來封裝一次測試用例,同樣的效果。我看過junit原始碼中自帶的測試類,框架主要用這個類來做一些異常和失敗的統計。OK,我們學段程式碼來試一下好了。
演示程式碼如下:
package org.linkinpark.commons.textui;
import org.linkinpark.commons.framework.TestCase;
import org.linkinpark.junit.Assert;
public class LinkinTest extends TestCase
{
public LinkinTest()
{
}
public LinkinTest(String methodName)
{
super(methodName);
}
public void setUp()
{
System.err.println("這裡是父類定義的setUp()");
}
public void testLinkin4Normal()
{
final String str = "林肯:這裡是自己的被測試的正確程式碼";
Assert.assertEquals(str, str);
}
}
然後我們派生一個子類,主要在子類執行main方法時候不要直接執行,寫一個suite靜態方法來傳入一個TestSetup型別的Test,然後我們來看下效果。
package org.linkinpark.commons.textui;
import org.linkinpark.commons.extensions.TestSetup;
import org.linkinpark.commons.framework.Test;
import org.linkinpark.commons.framework.TestSuite;
public class SonLinkinTest extends LinkinTest
{
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new TestSetup(new LinkinTest()));
return suite;
}
public void tearDown()
{
System.err.println("這裡是子類定義的tearDown()");
}
public static void main(String[] args)
{
TestRunner.run(SonLinkinTest.class);
}
}
最後我們來看下控制檯的輸出:
###########開始迭代執行整套測試,互相獨立###########
第一步:框架開始列印日誌====
~~~~~~~~~~~~~~~~~~~~~~~
第二步:框架開始執行測試====
這裡是父類定義的setUp()
框架開始執行測試,執行的方法是-->public void org.linkinpark.commons.textui.LinkinTest.testLinkin4Normal()
框架結束執行測試,執行的方法是-->public void org.linkinpark.commons.textui.LinkinTest.testLinkin4Normal()
這裡是子類定義的tearDown()
第三步:框架結束執行測試====
~~~~~~~~~~~~~~~~~~~~~~~
第四步:框架開始統計時間====
耗時:0.023秒
第五步:框架開始統計結果====
結果:OK,木問題!
統計:一共執行了1個測試用例
第六步:框架結束整個測試====