Em que ordem os Junit @ Before / @ After são chamados?


133

Eu tenho um conjunto de testes de integração. Eu tenho uma IntegrationTestBaseaula para todos os meus testes estenderem. Essa classe base possui um método @Before( public void setUp()) e @After( public void tearDown()) para estabelecer conexões API e DB. O que venho fazendo é substituir esses dois métodos em cada caso de teste e chamar super.setUp()e super.tearDown(). No entanto, isso pode causar problemas se alguém esquecer de chamar o super ou colocá-lo no lugar errado e uma exceção for lançada e eles esquecerem de chamar super no finalmente ou algo assim.

O que eu quero fazer é criar os métodos setUpe tearDownna classe base finale, em seguida, basta adicionar nossos próprios métodos @Beforee anotados @After. Fazendo alguns testes iniciais, parece sempre chamar nesta ordem:

Base @Before
Test @Before
Test
Test @After
Base @After

mas estou um pouco preocupado com o fato de o pedido não ser garantido e de causar problemas. Olhei em volta e não vi nada sobre o assunto. Alguém sabe se eu posso fazer isso e não tenho problemas?

Código:

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
O MyTestdesaparecido é um extends?
— aioobe

@aioobe: not anymore :)
— Joel

Respostas:


135

Sim, esse comportamento é garantido:

@Before:

Os @Beforemétodos das superclasses serão executados antes dos da classe atual, a menos que sejam substituídos na classe atual. Nenhuma outra ordem está definida.

@After:

Os @Aftermétodos declarados nas superclasses serão executados após os da classe atual, a menos que sejam substituídos na classe atual.


15
Para ficar claro, a ordem de execução de todos os @Beforemétodos não é garantida. Se houver 10 @Beforemétodos, cada um deles pode ser executado em qualquer ordem; imediatamente antes de qualquer outro método.
— Swati

5
Então, em vez de citar a documentação um tanto ambígua, você pode explicá-la com suas próprias palavras? Os métodos @Beforee são @Afterexecutados antes de qualquer outro método de classe (uma vez por método) ou imediatamente antes e depois de todo o conjunto de métodos de classe (uma vez por classe)?
— BT

5
Veja a captura importante declarada por John Q Citizen: "isso se aplica apenas se cada método marcado com @Antes tiver um nome exclusivo na hierarquia de classes" É muito importante lembrar!
— precisa saber é o seguinte

Eu tive um conflito de nome usando o mesmo nome de método em um método @Before (d) em uma classe e outro método em sua superclasse, on junit-4.12.
— Stephane

Esta regra também se aplica aos métodos @BeforeExample do ConcordionRunner?
— Adrian Pronk

51

Uma pegadinha em potencial que já me mordeu:

Eu gosto de ter no máximo um @Beforemétodo em cada classe de teste, porque a ordem de execução dos @Beforemétodos definidos em uma classe não é garantida. Normalmente, chamarei esse método setUpTest().

Mas, embora @Beforeesteja documentado como The @Before methods of superclasses will be run before those of the current class. No other ordering is defined., isso só se aplica se cada método marcado com @Beforetiver um nome exclusivo na hierarquia de classes.

Por exemplo, eu tive o seguinte:

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

Eu esperava AbstractFooTest.setUpTest()correr antes FooTest.setUpTest(), mas apenas FooTest.setupTest()foi executado. AbstractFooTest.setUpTest()não foi chamado.

O código deve ser modificado da seguinte maneira para funcionar:

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

Por que não mudar apenas o nome do método @Before na classe base? Isso vai poupar você de ter de chamada para super em todas as crianças ... de qualquer maneira boa captura com os mesmos nomes emitir
— Lawrence Tierney

24
Apenas uma observação para tornar as coisas mais seguras: Para evitar conflitos de nome, você pode criar os @Before/ @Aftermétodos na classe base final, para que o compilador se queixe se você (acidentalmente) tentar substituí-los na subclasse.
— Stefan Winkler

4
O método pai com o mesmo nome que não está sendo executado não soa como um comportamento JUnit. Parece como funciona a substituição básica no POO. O método pai basicamente não existe no tempo de execução. A criança a substitui para todos os efeitos. É assim que o Java funciona.
— Brandon

1
Outra dica é que as classes pai precisam ser públicas, caso contrário, seus @Beforemétodos marcados serão ignorados se uma subclasse também tiver um @Beforemétodo.
— Rusins ​​29/07/19

21

Penso que, com base na documentação @Beforee @Afterna conclusão correta, é dar nomes únicos aos métodos. Eu uso o seguinte padrão nos meus testes:

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");
  }
}

e

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");
  }
}

dar como resultado

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

Vantagem dessa abordagem: os usuários da classe AbstractBaseTest não podem substituir os métodos setUp / tearDown por acidente. Se quiserem, precisam saber o nome exato e podem fazê-lo.

(Menor) desvantagem dessa abordagem: os usuários não conseguem ver que há coisas acontecendo antes ou depois de sua configuração / desmontagem. Eles precisam saber que essas coisas são fornecidas pela classe abstrata. Mas suponho que essa seja a razão pela qual eles usam a classe abstrata


2
bom exemplo - seria ainda mais ilustrativo se você tivesse dois métodos @Test, portanto, é possível observar que setUp e tearDown envolvem cada método de teste.
— Mark

Acho que essa é a base para a melhor resposta ao OP, mas você deve preencher sua resposta de forma independente. Você poderia aumentar seu exemplo para abranger as alternativas sugeridas por outras pessoas e explicar por que sua proposta é superior?
— Wachr 18/08

2

Se você inverter as coisas, poderá declarar o abstrato da classe base e os descendentes declararão os métodos setUp e tearDown (sem anotações) que são chamados nos métodos anotados setUp e tearDown da classe base.


1
não é uma má idéia, mas eu não quero fazer cumprir um contrato sobre os testes que não precisa de sua própria configuração / tearDown
— Joel

2

Você pode usar a @BeforeClassanotação para garantir que setup()sempre seja chamado primeiro. Da mesma forma, você pode usar a @AfterClassanotação para garantir que tearDown()sempre seja chamado por último.

Isso geralmente não é recomendado, mas é suportado .

Não é exatamente o que você deseja - mas essencialmente manterá sua conexão com o banco de dados aberta o tempo todo em que seus testes estiverem sendo executados e depois fechá-la de uma vez por todas no final.


2
Na verdade, se você fosse fazer isso, eu recomendo a criação de um método setupDB()e closeDB()e marcando-as com @BeforeClasse @AfterClasse substituir o seu / antes depois métodos com setup()etearDown()
— Swati

Métodos anotados @BeforeClasse @AfterClassprecisam ser estáticos. E o caso, quando queremos usar variáveis ​​de instância dentro desses métodos?
— Pratik Singhal

Um aviso ao usar @BeforeClasscom o Powermock: ele funciona apenas na primeira execução de teste. Veja esta edição: github.com/powermock/powermock/issues/398
— Dagmar

2

Esta não é uma resposta para a pergunta do slogan, mas é uma resposta para os problemas mencionados no corpo da pergunta. Em vez de usar @Before ou @After, analise o uso de @ org.junit.Rule, pois oferece mais flexibilidade. ExternalResource (a partir de 4.7) é a regra na qual você estará mais interessado se estiver gerenciando conexões. Além disso, se você deseja garantir a ordem de execução de suas regras, use um RuleChain (a partir da versão 4.10). Acredito que todos eles estavam disponíveis quando essa pergunta foi feita. O exemplo de código abaixo é copiado dos javadocs do ExternalResource.

 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);
     }
 }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.