Junit“之前/之后”的顺序是什么?

我有一个集成测试套件。我有一个 IntegrationTestBase类,可以扩展我所有的测试。此基类具有用于建立 API 和 DB 连接的 @Before(public void setUp())和 @After(public void tearDown())方法。我所做的只是在每个测试用例中重写这两个方法,并调用 super.setUp()super.tearDown()。然而,如果有人忘记调用 super 或将其放在错误的位置,并且抛出异常,而且他们忘记在 finally 或其他内容中调用 super,那么这可能会导致问题。

我想做的是在基类 final上创建 setUptearDown方法,然后添加我们自己的带注释的 @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?* }
}
124159 次浏览

如果反过来,您可以声明基类抽象,并让后代声明在基类的带注释的 setUp 和 tearDown 方法中调用的 setUp 和 tearDown 方法(不带注释)。

是的,这种行为是有保证的:

@Before :

超类的 @Before方法将在当前类的方法之前运行,除非它们在当前类中被重写。没有定义其他顺序。

@After :

在超类中声明的 @After方法将在当前类的方法之后运行,除非它们在当前类中被重写。

您可以使用 @BeforeClass注释来确保总是首先调用 setup()。类似地,您可以使用 @AfterClass注释来确保 tearDown()总是调用 last。

这通常是不推荐的,但它是 支持

这并不完全是您想要的-但是它实际上会在整个测试运行期间保持数据库连接打开,然后在最后彻底关闭它。

有一个潜在的问题曾经困扰过我:

我希望在每个测试类中最多有一个 @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();
...
}
}

我认为基于 @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之前或之后发生的事情。他们需要知道这些东西是由抽象类提供的。但我认为这就是他们使用抽象类的原因

这不是对标语问题的回答,而是对问题正文中提到的问题的回答。不要使用@Before 或@After,而是考虑使用 @ org. junit. Rule,因为它提供了更多的灵活性。如果您正在管理连接,那么 外部资源(从4.7开始)是您最感兴趣的规则。另外,如果希望保证规则的执行顺序,可以使用 规则链(从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);
}
}