Junit @ Before / @ After叫什么顺序?


133

我有一个集成测试套件。我有一IntegrationTestBase堂课可以扩展我的所有测试。该基类具有@Beforepublic void setUp())和@Afterpublic void tearDown())方法来建立API和数据库连接。我一直在做的只是在每个测试用例中覆盖这两个方法,并调用super.setUp()super.tearDown()。但是,如果有人忘记了调用super或将它们放置在错误的位置并抛出异常,而他们却忘记了在final之类的地方调用super,则会导致问题。

我想做的是在基类上创建setUptearDown方法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?* }
}

1
MyTest缺少的extends吗?
aioobe 2013年

@aioobe:不再:)
Joel

Answers:


135

是的,可以保证此行为:

@Before

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

@After

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


15
需要明确的@Before是,不能保证所有方法的执行顺序。如果有10 @Before种方法,则每种方法都可以按任意顺序执行。在任何其他方法之前。
斯瓦蒂

5
因此,除了引用有些模棱两可的文档外,您还可以用自己的话来解释它吗?是@Before@After方法运行之前(每班一次)其它类方法(每一次法),或只是类方法的整个套件的前后?
BT

5
请参阅John Q Citizen所说的重要内容:“仅当用@Before标记的每个方法在类层次结构中具有唯一的名称时,此方法才适用”。记住这一点非常重要!
2014年

我在类中的@Before(d)方法和其父类中的另一个方法on上使用相同的方法名称时发生名称冲突junit-4.12
Stephane

此规则也适用于ConcordionRunner的@BeforeExample方法吗?
Adrian Pronk

51

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

我喜欢@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方法的名称?这样一来,您就不必再召唤所有孩子中的超级孩子了……无论如何,同名同堂的好
搭档

24
只是为了使事情更安全起见:为了避免名称冲突,您可以在基类中使用@Before/ @After方法final,因此,如果您(偶然)尝试在子类中重写它们,则编译器将抱怨。
Stefan Winkler

4
没有运行相同名称的父方法听起来不像JUnit行为。这听起来像是基本的优先于OOP。父方法在运行时基本上不存在。孩子会出于所有意图和目的替换它。这就是Java的工作方式。
布兰登

1
另一个陷阱是,父类必须是公共的,否则,@Before如果子类也具有方法,则将忽略其标记的@Before方法。
鲁辛

21

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


2
一个很好的例子-如果您有两个@Test方法,则将更具说明性,因此可以看出,setUp和tearDown包装了每个测试方法。
2015年

我认为这是对OP的最佳答案的基础,但是您应该填写独立答案。您是否可以扩大您的示例以涵盖其他人提出的替代方案,并解释您的提议为何优越?
wachr

2

如果您解决了所有问题,则可以声明基类抽象,并让后代声明在基类的带注释的setUp和tearDown方法中调用的setUp和tearDown方法(不带注释)。


1
这不是一个坏主意,但我不想对不需要自己的setUp / tearDown的测试强制执行合同
Joel

2

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

通常不建议这样做,但是它是受支持的

这并不是您真正想要的-但实际上它将使您的数据库连接在测试运行的整个过程中保持打开状态,然后一劳永逸地关闭它。


2
其实,如果你做到这一点,我建议建立一个方法setupDB(),并closeDB()与它们标记@BeforeClass@AfterClass与方法后更换您的前/ setup()tearDown()
斯瓦特

用注释的方法,@BeforeClass并且@AfterClass必须是静态的。当我们想在这些方法中使用实例变量时,情况又如何呢?
Pratik Singhal '18

@BeforeClass与Powermock一起使用时的警告:它仅适用于第一次测试运行。看到这个问题:github.com/powermock/powermock/issues/398
Dagmar

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);
     }
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.