如何在测试套件中定义JUnit方法规则?
我有一个类JUnit测试类的JUnit套件。 如果在该测试方法上存在某个注释,我想在套件上定义一个规则,以便在运行每个unit testing之前和之后对数据库执行某些操作。
我已经能够在套件和测试类中创建一个@ClassRule,它将在每个类之前执行此操作(这不够好)并且我已经能够使用测试类本身定义测试规则,但是这样是重复的,似乎不是很干。
是否可以在套件中定义每个测试方法规则,还是必须将它们添加到每个测试中?
编辑:为了澄清,我想在套件中声明代码,该套件将在测试类中的测试方法之间运行(即“围绕”)。
这可以做到,但需要一些工作。 您还需要定义自己的Suite runner和您自己的Test runner,然后在测试运行器中覆盖runChild()。 使用以下Suite和Test类:
@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"); } }
请注意,我使用@Deprecated
注释了test1()
。 当您在测试中使用@Deprecated
注释时,您希望做一些不同的事情,因此我们需要扩展Suite以使用自定义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 getRunners(Class>[] classes) throws InitializationError { List runners = new LinkedList (); for (Class> klazz : classes) { runners.add(new MyRunner(klazz)); } return runners; } }
JUnit为它将运行的每个测试创建一个Runner
。 通常,Suite只会创建默认的BlockJUnit4ClassRunner
,我们在这里所做的只是覆盖Suite的构造函数,它从SuiteClass
注释中读取类,我们正在创建自己的MyRunner
, 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的其他方法。 有关详细信息,请参阅Suite的各种构造函数旁边的注释。
您可以使用添加到Suite的RunListener 。 它不会为您提供规则可以执行的所有操作,但它将为您提供一个应该具有注释的Description类。 至少,我不认为JUnit只会将其过滤到其理解的注释。
JUnit的开发人员刚刚讨论了在这里向套件添加RunListener的机制。
就其本身而言,向使用@RunWith(Suite.class)
注释的类添加规则将@RunWith(Suite.class)
。 我相信这是因为Suite
是一个ParentRunner
而不是像BlockJUnit4ClassRunner
这样的Runner
,它会尝试在它运行的类上BlockJUnit4ClassRunner
规则。 要运行它的子Runners
,它会告诉子Runners
运行。 那些Runner
可能通过对这些类应用规则来构建测试,但Suite
runner不会采取任何特殊操作将规则从其自身应用到其子Runner
构建的测试。
您是否尝试过使用“测试类层次结构”? 我经常使用抽象测试类来共享测试或夹具。 例如,我的所有数据库测试都是初始化嵌入式数据源。 我首先创建一个抽象的“DbTestCase”类来处理init逻辑。 然后所有子类都将有益于测试和固定装置。
但是,当我的测试用例需要许多我无法存储在单个层次结构中的测试/夹具逻辑时,有时会遇到问题。 在这种情况下,只有方面编程才能解决问题。 通过任何必需类可以实现的特定注释/接口标记测试/夹具逻辑。
您可以选择使用自定义运行器来处理“方面”,该运行器将根据测试类的注释/接口“注入”测试/夹具逻辑。
您可以使用TestNG对测试进行分组 。 您可以配置TestNG以运行某些逻辑@BeforeGroup和@AfterGroup 。