我有一个集成测试套件。我有一个IntegrationTestBase
类用于扩展我的所有测试。此基类具有@Before
(public void setUp()
)和@After
(public void tearDown()
)方法来建立API和数据库连接。我一直在做的就是在每个测试用例中重写这两个方法并调用super.setUp()
和super.tearDown()
。但是,如果有人忘记调用超级或者将它们放在错误的位置并且抛出异常并且忘记在最后或者某事中调用super,这会导致问题。
我想要做的是在基类setUp
上制作tearDown
和final
方法,然后添加我们自己的注释@Before
和@After
方法。做一些初步测试似乎总是按此顺序调用:
Base @Before
Test @Before
Test
Test @After
Base @After
但我只是担心订单无法保证并且可能会导致问题。我环顾四周,没有看到任何关于这个问题的内容。有谁知道我能做到这一点而没有任何问题吗?
代码:
public class IntegrationTestBase {
@Before
public final void setUp() { *always called 1st?* }
@After
public final void tearDown() { *always called last?* }
}
public class MyTest extends IntegrationTestBase {
@Before
public final void before() { *always called 2nd?* }
@Test
public void test() { *always called 3rd?* }
@After
public final void after() { *always called 4th?* }
}
答案 0 :(得分:123)
是的,这种行为是有保证的:
超类的
@Before
方法将在当前类的方法之前运行,除非它们在当前类中被重写。没有定义其他排序。
超类中声明的
@After
方法将在当前类的方法之后运行,除非它们在当前类中被重写。
答案 1 :(得分:44)
之前困扰过我的一个潜在问题是:
我希望每个测试类中最多只有一个@Before
方法,因为不保证运行类中定义的@Before
方法的顺序。通常,我会调用这样一种方法setUpTest()
。
但是,虽然@Before
被记录为The @Before methods of superclasses will be run before those of the current class. No other ordering is defined.
,但这仅适用于标有@Before
的每个方法在类层次结构中都有唯一名称的情况。
例如,我有以下内容:
public class AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
我希望AbstractFooTest.setUpTest()
在FooTest.setUpTest()
之前运行,但只执行了FooTest.setupTest()
。根本没有调用AbstractFooTest.setUpTest()
。
必须按如下方式修改代码才能工作:
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
super.setUpTest();
...
}
}
答案 2 :(得分:19)
我认为根据@Before
和@After
的文档,正确的结论是给方法赋予唯一的名称。我在测试中使用以下模式:
public abstract class AbstractBaseTest {
@Before
public final void baseSetUp() { // or any other meaningful name
System.out.println("AbstractBaseTest.setUp");
}
@After
public final void baseTearDown() { // or any other meaningful name
System.out.println("AbstractBaseTest.tearDown");
}
}
和
public class Test extends AbstractBaseTest {
@Before
public void setUp() {
System.out.println("Test.setUp");
}
@After
public void tearDown() {
System.out.println("Test.tearDown");
}
@Test
public void test1() throws Exception {
System.out.println("test1");
}
@Test
public void test2() throws Exception {
System.out.println("test2");
}
}
给出结果
AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown
此方法的优点:AbstractBaseTest类的用户无法意外地覆盖setUp / tearDown方法。如果他们愿意,他们需要知道确切的名字,并且可以做到。
(次要)这种方法的缺点:用户无法看到在setUp / tearDown之前或之后发生了事情。他们需要知道这些东西是由抽象类提供的。但我认为这就是他们使用抽象类
的原因答案 3 :(得分:2)
如果你扭转局面,你可以声明你的基类抽象,并让后代声明在基类'带注释的setUp和tearDown方法中调用的setUp和tearDown方法(没有注释)。
答案 4 :(得分:2)
您可以使用@BeforeClass
注释来确保始终首先调用setup()
。同样,您可以使用@AfterClass
注释来确保始终最后调用tearDown()
。
通常不建议这样做,但它是supported。
这不完全是您想要的 - 但它实际上会在您的测试运行的整个过程中保持数据库连接打开,然后在结束时一劳永逸地关闭它。
答案 5 :(得分:2)
这不是标语问题的答案,但它是问题正文中提到的问题的答案。不要使用@Before或@After,而是使用@org.junit.Rule,因为它为您提供了更大的灵活性。如果您正在管理连接,ExternalResource(自4.7起)是您最感兴趣的规则。此外,如果您希望保证规则的执行顺序,请使用RuleChain(自4.10起)。我相信当问到这个问题时,所有这些都可用。下面的代码示例是从ExternalResource的javadocs中复制的。
public static class UsesExternalResource {
Server myServer= new Server();
@Rule
public ExternalResource resource= new ExternalResource() {
@Override
protected void before() throws Throwable {
myServer.connect();
};
@Override
protected void after() {
myServer.disconnect();
};
};
@Test
public void testFoo() {
new Client().run(myServer);
}
}