1. 程式人生 > >junit測試延伸--方法的重複測試

junit測試延伸--方法的重複測試

在實際編碼測試中,我們有的時候需要對一個方法進行多次測試,那麼怎麼辦呢?這個問題和測試套件解決的方案一樣,我們總不能不停的去右鍵run as,那怎麼辦呢?還好偉大的junit幫我們想到了。

OK,現在我們開始來寫一個例子:

測試原始碼:

package org.linkinpark.junit.testjunit;

/**
 * @建立作者: LinkinPark
 * @建立時間: 2016年2月5日
 * @功能描述: 寫一個測試原始碼
 */
public class Linkin
{
	public String test(String str)
	{
		return str + "。。。";
	}

}
測試程式碼:
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);
	}

}
現在我們執行一下,然後我們看下junit控制檯什麼情況?

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個測試用例
第六步:框架結束整個測試====