我有一個類是JUnit測試類的JUnit套件。我想在套件上定義規則,以在每個單元測試運行之前和之後對數據庫做些什麼如果該測試方法存在某個註釋。如何在測試套件中定義JUnit方法規則?
我已經能夠在每個和每個類(這不夠好)之前這樣做的套件和測試類中創建@ClassRule,並且我已經能夠定義測試規則測試類本身,但這是重複的,看起來不太乾燥。
是否可以在套件中定義每個測試方法規則,還是必須將它們添加到每個測試?爲了澄清,我想聲明一個套件中的代碼,它將在測試類中的測試方法之間運行(即「圍繞」)。
我有一個類是JUnit測試類的JUnit套件。我想在套件上定義規則,以在每個單元測試運行之前和之後對數據庫做些什麼如果該測試方法存在某個註釋。如何在測試套件中定義JUnit方法規則?
我已經能夠在每個和每個類(這不夠好)之前這樣做的套件和測試類中創建@ClassRule,並且我已經能夠定義測試規則測試類本身,但這是重複的,看起來不太乾燥。
是否可以在套件中定義每個測試方法規則,還是必須將它們添加到每個測試?爲了澄清,我想聲明一個套件中的代碼,它將在測試類中的測試方法之間運行(即「圍繞」)。
這可以完成,但它需要一點工作。您還需要定義自己的套件運行器和您自己的測試運行器,然後在測試運行器中重寫runChild()。使用下面的套房和測試類:
@RunWith(MySuite.class)
@SuiteClasses({Class1Test.class})
public class AllTests {
}
public class Class1Test {
@Deprecated @Test public void test1() {
System.out.println("" + this.getClass().getName() + " test1");
}
@Test public void test2() {
System.out.println("" + this.getClass().getName() + " test2");
}
}
請注意,我註釋test1()
與@Deprecated
。你想,當你有上試@Deprecated
註釋做不同的事情,所以我們需要擴展套件使用自定義Runner
:
public class MySuite extends Suite {
// copied from Suite
private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
Suite.SuiteClasses annotation = klass.getAnnotation(Suite.SuiteClasses.class);
if (annotation == null) {
throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
}
return annotation.value();
}
// copied from Suite
public MySuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
super(null, getRunners(getAnnotatedClasses(klass)));
}
public static List<Runner> getRunners(Class<?>[] classes) throws InitializationError {
List<Runner> runners = new LinkedList<Runner>();
for (Class<?> klazz : classes) {
runners.add(new MyRunner(klazz));
}
return runners;
}
}
的JUnit會爲每個測試將運行一個Runner
。通常情況下,Suite會創建默認的BlockJUnit4ClassRunner
,我們這裏所做的全部重寫了Suite的構造函數,它從SuiteClass
註釋中讀取類,我們正在創建自己的運行程序MyRunner
。這是我們的MyRunner類:
public class MyRunner extends BlockJUnit4ClassRunner {
public MyRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description= describeChild(method);
if (method.getAnnotation(Ignore.class) != null) {
notifier.fireTestIgnored(description);
} else {
if (description.getAnnotation(Deprecated.class) != null) {
System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
}
runLeaf(methodBlock(method), description, notifier);
}
}
}
這其中大部分是從BlockJUnit4ClassRunner
複製。我已經添加了位:
if (description.getAnnotation(Deprecated.class) != null) {
System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
}
,我們測試了@Deprecated
註釋的方法的存在,並做一些事情,如果它的存在。剩下的部分作爲讀者的練習。當我運行上面的套件時,我得到輸出:
name=test1 annotations=[@java.lang.Deprecated(), @org.junit.Test(expected=class org.junit.Test$None, timeout=0)]
uk.co.farwell.junit.run.Class1Test test1
uk.co.farwell.junit.run.Class1Test test2
請注意,Suite有多個構造函數,具體取決於它如何被調用。上述工作適用於Eclipse,但我還沒有測試過運行該套件的其他方式。有關更多信息,請參閱Suite的各種構造函數旁邊的註釋。
您可以用TestNG group tests。你可以配置TestNG來運行一些邏輯@BeforeGroup and @AfterGroup。
您可以使用添加到套件的RunListener。它不會給你規則可以做的所有事情,但它會爲你提供一個Description類,它應該有可用的註釋。至少,我不認爲JUnit只將它過濾到其理解的註釋中。
JUnit的開發人員剛剛討論了將RunListener添加到套件的機制here。
就其本身而言,向@RunWith(Suite.class)
註解的類添加規則將無濟於事。我認爲這是因爲Suite
是ParentRunner<Runner>
而不是Runner
,比如BlockJUnit4ClassRunner
,它會試圖在它運行的類上刮取規則。爲了運行它的孩子,它告訴孩子Runners
運行。這些Runner
可能已經通過對這些類別應用規則來構建他們的測試,但Suite
跑步者不會採取任何特殊行動來將自身的規則應用於其子女Runner
構建的測試。
您是否嘗試過使用「測試類層次結構」?我經常使用抽象測試類來分享測試或夾具。例如,我所有的數據庫測試都在初始化嵌入式數據源。我首先創建一個處理init邏輯的抽象「DbTestCase」類。那麼所有的子類都將有利於測試和固定裝置。
但是,有時我遇到問題,當我的測試用例需要很多測試/夾具邏輯時,我無法將其存儲在單個層次結構中。在這種情況下,只有方面編程才能解決問題。通過特定的註釋/接口標記測試/夾具邏輯,任何必需的類都可以實現。
你可以選擇考慮使用自定義運行器來處理「aspect」,該運行器將根據被測試類的註釋/接口「注入」測試/夾具邏輯。
雖然層次理念是健全的,但方面就像黑魔法或芥末:他們應該謹慎使用,即使只是謹慎使用。 – ArtB
我不認爲這是我正在尋找。我想要在每個測試之間運行的東西。 – ArtB
但是,如果您有一組測試,應該可以使用BeforeMethod和AfterMethod。 (我猜) – ollins
也許,無論如何,我需要使用JUnit和TestNG不是一個選項。 – ArtB