143

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

我想要做的是在基类上创建setUpand方法,然后添加我们自己的注释和方法。做一些初始测试,它似乎总是按以下顺序调用:tearDownfinal@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?* }
}
4

6 回答 6

145

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

@Before

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

@After

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

于 2011-05-20T19:24:06.557 回答
57

一个潜在的陷阱,以前曾咬过我:

我喜欢@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();
    ...
  }
}
于 2013-06-20T00:51:58.643 回答
25

我认为基于文档的@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。他们需要知道这些东西是由抽象类提供的。但我认为这就是他们使用抽象类的原因

于 2013-07-09T13:58:14.547 回答
2

如果你扭转局面,你可以声明你的基类抽象,并让后代声明在基类的带注释的 setUp 和 tearDown 方法中调用的 setUp 和 tearDown 方法(没有注释)。

于 2011-05-20T19:21:54.910 回答
2

您可以使用@BeforeClass注释来确保setup()始终首先调用它。同样,您可以使用@AfterClass注释来确保tearDown()始终最后调用它。

通常不建议这样做,但支持

这并不完全是您想要的 - 但它本质上会在您的测试运行的整个过程中保持您的数据库连接打开,然后在最后一劳永逸地关闭它。

于 2011-05-20T19:26:41.087 回答
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);
     }
 }
于 2015-11-02T01:39:17.110 回答