- 論壇徽章:
- 1
|
1.jpg (28.33 KB, 下載次數(shù): 58)
下載附件
2016-06-07 11:50 上傳
JUnit對(duì)Java開(kāi)發(fā)者來(lái)說(shuō)一個(gè)在普遍使用的單元測(cè)試框架。但是,大多數(shù)使用者僅僅使用了它的一部分功能。本文介紹其中不常被用到的8個(gè)功能。比如臨時(shí)目錄創(chuàng)建及自動(dòng)清理,測(cè)試失敗時(shí)所有斷言的執(zhí)行,失控測(cè)試的強(qiáng)制停止等。
@Rule是JUnit4的新特性。利用@Rule注解我們可以擴(kuò)展JUnit的功能,在執(zhí)行case的時(shí)候加入測(cè)試者特有的操作,而不影響原有的case代碼:減小了特有操作和case原邏輯的耦合。譬如說(shuō)我們要重復(fù)測(cè)試某個(gè)test方法時(shí),當(dāng)然我們可以在@Test方法里面寫(xiě)循環(huán)。但是如果想把循環(huán)和測(cè)試邏輯分開(kāi)就可以利用@Rule。
@Rule只能注解在字段中,該字段必須是public的并且類(lèi)型必須實(shí)現(xiàn)了TestRule接口或MethodRule接口。JUnit 4.9還導(dǎo)入了@ClassRule注解。相對(duì)于@Rule注解來(lái)說(shuō),@ClassRule是一個(gè)類(lèi)級(jí)別的注解。就像@Before與@BeforeClass的區(qū)別。下面介紹JUnit中8個(gè)不常用到但很有用的功能。
通過(guò)@Rule注解可以生成臨時(shí)文件或臨時(shí)文件夾
有時(shí)候程序運(yùn)行時(shí)必須生成文件或文件夾,往往需要寫(xiě)代碼。JUnit4.7以后的版本中引入了TemporaryFolder。測(cè)試可以創(chuàng)建文件與目錄并在測(cè)試運(yùn)行結(jié)束后將其刪除。這對(duì)于那些與文件系統(tǒng)打交道而且獨(dú)立運(yùn)行的測(cè)試來(lái)說(shuō)很有用。這個(gè)功能使測(cè)試中文件夾的生成和管理變的非常簡(jiǎn)單。只要確保生成的文件夾或文件是名字不與既有的重復(fù)就行。并且,無(wú)論測(cè)試成功與否,任何在測(cè)試過(guò)程中添加到臨時(shí)目錄的文件或目錄都會(huì)在測(cè)試結(jié)束時(shí)刪除。
下面的例子中,JUnit生成的臨時(shí)目錄中生成一個(gè)sample.txt的文件,并且寫(xiě)入「JUnit Rocks!」。然后,讀取新生成文件的第一行并進(jìn)行比較。為使代碼簡(jiǎn)化,例子中文件處理使用了Apache Commons IO。
public class TemporaryFolderTests {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void fileCreatedAndWrittenSuccessfully() throws IOException {
File file = tempFolder.newFile("sample.txt");
FileUtils.writeStringToFile(file, "JUnit Rocks!");
String line = FileUtils.readFileToString(file);
assertThat(line, is("JUnit Rocks!"));
}
}
測(cè)試完后(無(wú)論測(cè)試成功與否)文件和目錄會(huì)被刪除。
取得當(dāng)前的測(cè)試方法名稱(chēng)
想要取得執(zhí)行中的測(cè)試方法名的時(shí)候,通過(guò)@Rule注解TestName類(lèi)的實(shí)例化對(duì)象可以取得。
public class TestNameTests {
@Rule
public TestName name = new TestName();
@Test
public void methodNameShouldBePrinted() {
System.out.println("Test method name:" + name.getMethodName());
}
}
通過(guò)getMethodName()方法可以得到當(dāng)前測(cè)試方法名稱(chēng)。
上面例子的執(zhí)行結(jié)果是:
Test method name: methodNameShouldBePrinted
在一個(gè)運(yùn)行測(cè)試方法過(guò)程中收集多個(gè)錯(cuò)誤信息
使用ErrorCollector類(lèi),可以在一個(gè)測(cè)試方法中收集多個(gè)測(cè)試錯(cuò)誤。也就是說(shuō),一個(gè)測(cè)試方法執(zhí)行中,不會(huì)在第一個(gè)確認(rèn)出錯(cuò)后就停止執(zhí)行。使用ErrorCollector可以在所有點(diǎn)確認(rèn)完后統(tǒng)一報(bào)出。
下在例子中有三個(gè)點(diǎn)要確認(rèn),它們都用ErrorCollector來(lái)確認(rèn)。
public class ErrorCollectorTests {
@Rule
public ErrorCollector collector = new ErrorCollector();
@Test
public void statementsCollectedSuccessfully() {
String s = null;
collector.checkThat ("Value should not be null", null, is(s));
s = "";
collector.checkThat( "Value should have the length of 1", s.length(), is(1));
s = "Junit!";
collector.checkThat( "Value should have the length of 10", s.length(), is(10));
}
}
第一個(gè)點(diǎn)確認(rèn)成功,后面兩個(gè)點(diǎn)都失敗的時(shí)候會(huì)報(bào)下面這樣的錯(cuò)。
java.lang.AssertionError:Value should have the length of 1
Expected: is <1>
but: was <0>
at org.hamcrest.MatcherAssert...
java.lang.AssertionError:Value should have the length of 10
Expected: is <10>
but: was <6>
at org.hamcrest.MatcherAssert...
一個(gè)測(cè)試方法用不同的參數(shù)來(lái)反復(fù)執(zhí)行
用不同的參數(shù)來(lái)測(cè)試一個(gè)方法是否正確是一個(gè)比較普遍的需求。JUnit4.0以后的版本中,通過(guò)@Parameters注解可以不重復(fù)寫(xiě)測(cè)試方法就能實(shí)現(xiàn)這一需求。
使用@Parameters注解時(shí)不能使用默認(rèn)Runner來(lái)運(yùn)行測(cè)試代碼,必須使用Parameterized.class測(cè)試類(lèi)。
通過(guò)下面的例子來(lái)說(shuō)明一下。
@RunWith(Parameterized.class)
public class FibonacciNumbersTests {
@Parameterized.Parameters
public static List data() {
return Arrays.asList(new Object[][]{
{0, 0}, {1, 1}, {2, 1},
{3, 2}, {4, 3}, {5, 5},
{6, 8}});
}
private int value;
private int expected;
public FibonacciNumbersTests( int input, int expected) {
value = input;
this.expected = expected;
}
@Test
public void fibonacciNumberCalc () {
assertEquals(expected, fib(value));
}
public static int fib(int n) {
if (n < 2) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
}
第一行指定@RunWith注解,JUnit用專(zhuān)門(mén)的Runner類(lèi)來(lái)運(yùn)行測(cè)試代碼。
List的各項(xiàng)目中第一個(gè)是input參數(shù),第二個(gè)是expected參數(shù)。測(cè)試方法通@Parameterized.Parameters注解,把List的各個(gè)項(xiàng)目做為測(cè)試類(lèi)的構(gòu)造函數(shù)的參數(shù)。
設(shè)定執(zhí)行最長(zhǎng)時(shí)間
通過(guò)@Test注解中的timeout屬性可以使執(zhí)行時(shí)間過(guò)長(zhǎng)的測(cè)試方法強(qiáng)制結(jié)束掉,同時(shí)測(cè)試結(jié)果也表示為測(cè)試失敗。timeout屬性中設(shè)定的時(shí)間是毫秒單位的。并且可以設(shè)定整個(gè)類(lèi)的執(zhí)行最長(zhǎng)時(shí)間。下面例子中設(shè)定了5秒最長(zhǎng)執(zhí)行時(shí)間。
public class LongRunningTests {
@Rule
public Timeout globalTimeout = Timeout.seconds(5);
@Test
public void whatWeDoInATestMethodEchoesInEternity() {
while(true);
}
}
執(zhí)行結(jié)果如下:
org.junit.runners.model.TestTimedOutException:
test timed out after 5 seconds
at tr.com.t2.labs.tdd.sample5.LongRunningTests…
用@Category把測(cè)試分組
JUnit 4.8以后的版本中,加入了分組測(cè)試的功能。下面例6的兩個(gè)測(cè)試方法中thisTestRunsSlowly()加上了@Category注解。
public class CategorizedTests {
@Test
@Category(SlowTests.class)
public void thisTestRunsSlowly() {
System.out.println("Slow test running");
}
@Test
public void thisTestRunsFast() {
System.out.println("Fast test running");
}
}
SlowTests.class的定義是下面這樣的。
public interface SlowTests {
}
在例7中用Categories.class的Runner來(lái)運(yùn)行分組后的測(cè)試代碼。指定類(lèi)的Runner沒(méi)有設(shè)定的話@Category注解是無(wú)效的。用@IncludeCategory注解來(lái)指定要執(zhí)行的接口。這樣只有指定的分組才會(huì)被執(zhí)行到。
@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses(CategorizedTests.class)
public class SlowTestsTestSuite {
}
自定義測(cè)試規(guī)則
通過(guò)實(shí)現(xiàn)TestRule接口可以簡(jiǎn)單的實(shí)現(xiàn)自定義測(cè)試規(guī)則。TestRule中只定義了apply()方法。在實(shí)現(xiàn)apply()方法時(shí)返回一個(gè)Statement類(lèi)型的返回值。Statement類(lèi)是定義了evaluate()方法的抽象類(lèi)。具體參照以下例子。
public class MyCustomRule implements TestRule {
private String label;
public MyCustomRule(String label) {
this.label = label;
}
@Override
public Statement apply(final Statement base, Description description){
return new Statement() {
@Override
public void evaluate() throws Throwable {
System.out.println(label + " before");
base.evaluate();
System.out.println(label + " after");
}
};
}
}
下面例9中引入了例8的自定義規(guī)則。
public class CustomRuleTests {
@Rule
public MyCustomRule myCustomRule = new MyCustomRule("custom");
@Test
public void myAwesomeMethodInvokedSuccessfully() {
System.out.println("Test worked OK");
}
}
執(zhí)行結(jié)果如下。
custom before
Test worked OK
custom after
使用RuleChain
RuleChain提供一種將多個(gè)TestRule串在一起執(zhí)行的機(jī)制。這在JUnit 4.10以后的版本中可以使用。需要根據(jù)特定順序執(zhí)行多個(gè)處理的時(shí)候,用RuleChain可以提高效率。
下面例10是在例9上寫(xiě)的一個(gè)例子。
public class RuleChainTests {
@Rule
public RuleChain chain = RuleChain.outerRule(
new MyCustomRule("outer")).around(new MyCustomRule("inner")
);
@Test
public void ruleChainWorkedOK() {
System.out.println("Test worked OK");
}
}
執(zhí)行如下:
outer before
inner before
Test worked OK
inner after
outer after
以上內(nèi)容是一位資深的碼農(nóng)大哥寫(xiě)的,不知道大家看了理解的怎樣?可以在評(píng)論區(qū)發(fā)布想法交流哦。
優(yōu)云軟件的碼農(nóng)都是能文能武!來(lái)認(rèn)識(shí)下作者
作者自我評(píng)價(jià):鮑震淵是一名工作10多年的老碼農(nóng)。2016年前專(zhuān)注于CRM系統(tǒng)的設(shè)計(jì)與開(kāi)發(fā)。
秉承devops的理念,從監(jiān)控、到應(yīng)用體驗(yàn),到自動(dòng)化持續(xù)交付,優(yōu)云軟件一切為了您做的更好!
將IT運(yùn)維化繁為簡(jiǎn),請(qǐng)?jiān)L問(wèn)優(yōu)云官網(wǎng):
chinaunix.jpg (16.06 KB, 下載次數(shù): 66)
下載附件
2016-06-07 11:53 上傳
|
|