Junit - execute o método de configuração uma vez

119

Eu montei uma classe com alguns testes e, em vez de usar @Before, gostaria de ter um método de instalação que seja executado apenas uma vez antes de todos os testes. Isso é possível com o Junit 4.8?

Bober02
fonte
1
Dê uma olhada no RunListener: stackoverflow.com/a/14773170/548473
Grigory Kislin 11/11

Respostas:

205

Embora eu concorde com @assylias que o uso @BeforeClassé uma solução clássica nem sempre é conveniente. O método anotado com @BeforeClassdeve ser estático. É muito inconveniente para alguns testes que precisam de instância de caso de teste. Por exemplo, testes baseados em primavera que costumam @Autowiredtrabalhar com serviços definidos no contexto da primavera.

Nesse caso, eu pessoalmente uso o setUp()método regular anotado com @Beforeanotação e gerencio meu sinalizador personalizado static(!) boolean:

private static boolean setUpIsDone = false;
.....
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}
AlexR
fonte
10
Adicionando ao comentário de Kenny Cason o motivo pelo qual ele deve ser estático. Ele deve ser estático porque o JUnit instancia uma nova instância da classe de teste para cada método @Test. A variável de instância será redefinida para seu valor padrão (false) para cada instância, se não for estática. Veja para obter mais informações: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz
2
Isso funciona, exceto no caso em que o setUp()método está em uma superclasse - postou uma resposta abaixo, tentando resolver isso.
Steve Chambers
4
Hesito em dizer isso a alguém com um representante de 84k, mas o BeforeClass na verdade não responde à pergunta: o BeforeClass é executado no início de cada classe de teste. Mas o OP pediu um que execute "apenas uma vez antes de todos os testes". Sua solução proposta poderia fazer isso, mas você teria que fazer com que todas as suas classes de teste estendessem uma classe "CommonTest" ...
mike roedor
1
@mikerodent, IMHO OP perguntou sobre todos os testes em seu caso de teste, nem todos os testes em geral. Portanto, seu comentário é menos relevante. Aliás, não se preocupe em dizer nada a qualquer pessoa, mesmo que sua reputação seja alta. Pelo menos é o que eu faço :). E minha reputação era significativamente menor em agosto de 2012, quando respondi à pergunta.
precisa saber é
Não funciona no meu caso, as variáveis ​​inicializadas na configuração são redefinidas após cada teste, portanto, é inútil iniciar apenas uma vez.
Aphax 24/05
89

Você pode usar a BeforeClassanotação :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}
assylias
fonte
12
Eu não posso usar isso, eu tenho e alguns métodos de configuração que são baseados em componentes não-estáticos, como getClass ()
Bober02
1
@ Bober02 BeforeClass precisa ser realmente estático. Se você não pode usar isso, a outra resposta fornece uma solução alternativa.
assylias
2
Claro que você não pode usar em TheClassYouWant.classvez da sua chamada getClass ()? Esta é Java real: String.class.getName().
stolsvik
1
@mikerodent Entendi a pergunta como "todos os testes da classe" - mas você está certo, pode não ser o que o OP queria.
Assilias 15/01/19
29

O JUnit 5 agora tem uma anotação @BeforeAll:

Indica que o método anotado deve ser executado antes de todos os métodos @Test na classe atual ou na hierarquia de classes; análogo ao @BeforeClass da JUnit 4. Tais métodos devem ser estáticos.

As anotações do ciclo de vida do JUnit 5 parecem ter finalmente acertado! Você pode adivinhar quais anotações estão disponíveis sem precisar procurar (por exemplo, @BeforeEach @AfterAll)

Brian
fonte
6
Ele tem o mesmo problema @BeforeClass, precisa ser static. A solução da IMO @ AlexR é melhor.
Zengr 13/12/16
O @zengr tende a concordar com você: como eu disse ao AlexR, a solução dele exige que todas as classes de teste subclasses de uma classe CommonTest se ela for executada apenas uma vez. Mas é tão simples quanto pode ser, e IMHO você provavelmente não deve usar uma solução fornecida pela estrutura "sofisticada" quando um mecanismo simples estiver disponível na linguagem. A menos que haja uma boa razão, é claro. Além disso, o uso de uma coisa simples como a dele, com um bom nome de tipo "faz o que diz na lata", ajuda na legibilidade.
mike roedor
Dito isto, novamente IMHO, parece haver muito mais justificativa para ter uma anotação "AfterAll": seria muito difícil e artificial conceber um mecanismo para detectar quando todos os testes foram feitos. Por outro lado, é claro, os puristas provavelmente dirão que você nunca deve fazer uma "limpeza final", ou seja, que cada "tearDown" deve deixar todos os recursos em um estado primitivo ... e provavelmente eles estão certos!
mike roedor
Isso funciona com o Maven, onde existem vários módulos, cada um com seus testes?
Mark Boon
@ Mike Roedor, no meu caso, a instalação e remoção de arquivos de teste no sistema de arquivos antes / depois de cada teste parece estar causando conflitos nos arquivos. Por enquanto, cheguei independentemente à solução da AlexR para configurar uma vez. Eu tenho dois sinalizadores estáticos, já configurados e sujos. setup () chama cleanup () se um estado sujo for detectado inicialmente ou se uma falha na configuração levar a um estado sujo. Para limpar depois de executar os testes, eu os executo novamente. Confuso, nem ideal, nem no nosso processo de construção. Ainda procurando uma maneira melhor (jUnit 4.12).
Rebeccah
9

Quando setUp()está em uma superclasse da classe de teste (por exemplo, AbstractTestBaseabaixo), a resposta aceita pode ser modificada da seguinte maneira:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Isso deve funcionar para um único setUp()método não estático , mas não consigo produzir um equivalente tearDown()sem entrar em um mundo de reflexão complexa ... Bounty aponta para quem pode!

Steve Chambers
fonte
3

Editar: Acabei de descobrir, durante a depuração, que a classe é instanciada antes de cada teste também. Acho que a anotação @BeforeClass é a melhor aqui.

Você também pode configurar o construtor, afinal a classe de teste é uma classe. Não tenho certeza se é uma prática ruim porque quase todos os outros métodos são anotados, mas funciona. Você pode criar um construtor assim:

public UT () {
    // initialize once here
}
@Test
// Some test here...

O ctor será chamado antes dos testes porque eles não são estáticos.


fonte
0

Experimente esta solução: https://stackoverflow.com/a/46274919/907576 :

com @BeforeAllMethods/ @AfterAllMethodsanotação, você pode executar qualquer método na classe Test em um contexto de instância, onde todos os valores injetados estão disponíveis.

radistao
fonte
Confia em uma biblioteca de terceiros.
Andrew Andrew
0

Minha solução suja é:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Eu o uso como base para todos os meus testCases.

Obi Two
fonte
classe pública TestCaseExtended estende TestCase {private estático boolean isInitialized = false; estático privado TestCaseExtended caseExtended; private int serId; @Override public void setUp () lança Exception {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Two
0

Se você não deseja forçar uma declaração de uma variável que é definida e verificada em cada subteste, a adição a um SuperTeste pode fazer:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}
mmm
fonte
0

Eu resolvi esse problema assim:

Adicione à sua classe abstrata Base (quero dizer classe abstrata onde você inicializa seu driver no método setUpDriver () ) esta parte do código:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

E agora, se suas classes de teste se estenderem da classe abstrata Base -, o método setUpDriver () será executado antes do primeiro @Test apenas uma vez por execução.

Sergii
fonte
0

Use o método @PostConstruct do Spring para executar todo o trabalho de inicialização e esse método é executado antes que qualquer um dos @Test seja executado

Abhishek Chatterjee
fonte