Como executar métodos de teste em ordem específica no JUnit4?

413

Desejo executar métodos de teste anotados por @Testordem específica.

Por exemplo:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Quero garantir a execução test1()antes de test2()cada vez que corro MyTest, mas não consegui encontrar anotações como essa @Test(order=xx).

Eu acho que é um recurso muito importante para o JUnit, se o autor do JUnit não deseja o recurso de pedido , por quê?

卢 声 远 Shengyuan Lu
fonte
2
Eles me parecem executados na ordem em que aparecem no arquivo de origem.
Marquis of Lorne
84
Você nunca deve escrever testes que precisam ser executados em uma ordem especificada. Isso é realmente uma prática ruim. Todo teste deve ser capaz de executar de forma independente.
Apfelsaft 31/07
5
@EJP, isso era quase universal para java pré 7. Antes de 7, a maioria das JVMs fazia isso, mas nunca era garantido. As JVMs do Java 7 podem retornar os métodos em uma ordem não determinística.
Matthew Farwell
17
Gambiarra. Remova o @Test dos casos de teste, converta-os como funções privadas, crie um único caso de teste e chame as funções privadas em ordem.
Simon Guo
12
Remover o @Test dos casos de teste atrapalhará o relatório JUnit. A propósito, impor uma ordem específica é uma prática ruim para testes de unidade, mas não necessariamente uma prática ruim para testes de integração . A melhor opção (não ideal) é para anotar a classe com @FixMethodOrder(MethodSorters.NAME_ASCENDING), manter a @Testanotação de todos os métodos de ensaio e renomeá-los em ordem alfabética de acordo com a ordem desejada de execução, por exemplo t1_firstTest(), t2_secondTest(), etc.
MisterStrickland

Respostas:

238

Eu acho que é um recurso muito importante para o JUnit, se o autor do JUnit não deseja o recurso de pedido, por quê?

Não tenho certeza de que haja uma maneira limpa de fazer isso com o JUnit, pelo que sei, o JUnit assume que todos os testes podem ser executados em uma ordem arbitrária. Do FAQ:

Como uso um equipamento de teste?

(...) A ordem das chamadas de método de teste não é garantida , portanto testOneItemCollection () pode ser executado antes de testEmptyCollection (). (...)

Por que é tão? Bem, acredito que tornar a ordem dos testes dependente é uma prática que os autores não querem promover. Os testes devem ser independentes, não devem ser acoplados, e violar isso tornará as coisas mais difíceis de manter, prejudicará a capacidade de executar testes individualmente (obviamente) etc.

Dito isto, se você realmente quiser ir nessa direção, considere usar o TestNG, pois ele suporta a execução de métodos de testes em qualquer ordem arbitrária nativamente (e coisas como especificar que métodos dependem de grupos de métodos). Cedric Beust explica como fazer isso em ordem de execução de testes no testng .

Pascal Thivent
fonte
14
Você tem dois testes independentes ou apenas um e deve codificar como tal.
Jon Freedman
2
@ JonFreedman, como eu entendo a pergunta, não é um caso de testes serem interdependentes, apenas de ter uma especificação de coisas para testar e querer que os resultados apareçam nessa ordem.
Jon Brilhante
174
Compreendo que não imponha ordem para testes de unidade, no entanto, ao usar o JUnit para escrever testes de integração, seria bom poder especificar a ordem em que os testes são executados. Por exemplo, execute o teste de login primeiro.
Brian DiCasa
13
@BrianD. o login é provavelmente um "acessório" em vez de um teste que deve ser executado antes de todos os outros. Provavelmente vou escrever um BeforeClass que efetua login e depois escreve os testes para executar em qualquer ordem.
Marcospereira 22/10/12
47
A implicação "os testes devem ser independentes => os testes devem ser independentes da ORDER" não é verdadeira. Considere a classificação automatizada dos trabalhos de casa do aluno. Quero testar sua solução para entradas menores primeiro e depois para entradas maiores. Quando a solução está falhando para entradas menores (para limite de tempo / memória), por que os testes devem ser executados para entradas maiores?
mirelon
96

Se você se livrar da sua instância existente do Junit e fizer o download do JUnit 4.11 ou superior no caminho de construção, o código a seguir executará os métodos de teste na ordem de seus nomes, classificados em ordem crescente:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}
Aniket Kulkarni
fonte
8
Na verdade, tentamos um método semelhante test_001_login (), por exemplo, mas, embora funcione principalmente para preservar a ordem, não é garantido - temos várias instâncias por execução de teste em que 004, 005 e 006 são executadas após 007. Isso faz com que você diz "WTF!" e corre para o StackOverflow para obter respostas.
Max P Magee
Impressionante - disponível no JUnit 4.12
DtechNet
1
nos meus testes: testAcase - funcionou, test_A_case / testA_case - não!
Rodislav Moldovan
6
Eu tentei esse parâmetro de anotação "MethodSorters.JVM", por exemplo, "@FixMethodOrder (MethodSorters.JVM)". Da API: JVM - Deixa os métodos de teste na ordem retornada pela JVM. Funciona muito bem para o meu que eu estou fazendo (CRUD), executa os métodos de ensaio na ordem em que são escritos em um.
Edvinauskas
1
Essa anotação é de fato uma resposta, mas tem a ressalva de que não está definida (em 4 de junho de 4.12) @Inheritede, portanto, se torna ineficaz na minha AbstractTestCaseclasse pai.
AbVog 3/09/18
49

Se o pedido for importante, faça você mesmo.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

Em particular, você deve listar algumas ou todas as permutações de pedidos possíveis para testar, se necessário.

Por exemplo,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

Ou, um teste completo de todas as permutações:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Aqui permute()está uma função simples que itera todas as permutas possíveis em uma coleção de array.

Xiè Jìléi
fonte
Mas e se testes em arquivos diferentes?
Oleg Abrazhaev
3
No primeiro bloco de código, test2é executado test1 novamente . Junit ainda pode ser executado test2antes test1. Provavelmente, isso não é o que você pretendia e não é uma resposta válida para a pergunta.
toolforger
47

A migração para o TestNG parece a melhor maneira, mas não vejo uma solução clara aqui para o jUnit. Aqui está a solução / formatação mais legível que encontrei para o jUnit:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Isso garante que os métodos stage2 sejam chamados após os stage1 e antes dos stage3.

joro
fonte
5
Esta abordagem é bom, mas seria válido mencionar que se você tiver mais de 10 testes que costuma funcionar bem, a menos que você adicionar um 0prefixo, por exemplovoid stage01_prepareAndTest(){ }
EliuX
Se você tiver mais de 10 etapas (não testes) - Sim. Prefiro limitar o número de etapas e ter mais testes em cada etapa, quando isso for possível.
Joro
18

Esse é um dos principais problemas que eu enfrentei quando trabalhei no Junit e criei a seguinte solução, que funciona bem para mim:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

Também crie uma interface como abaixo:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Agora, suponha que você tenha a classe A em que você escreveu vários casos de teste como abaixo:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

Portanto, a execução começará no método chamado "method ()". Obrigado!

Aman Goel
fonte
Usando outro JUnit Runner com o PowerMock Desde a versão 1.6.0, o PowerMock oferece suporte para delegar a execução de teste a outro runner da JUnit sem usar uma regra da JUnit. Isso deixa a execução de teste real para outro corredor de sua escolha. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

Atualmente, o JUnit permite que métodos de teste executem a ordenação usando anotações de classe:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

Por padrão, os métodos de teste são executados em ordem alfabética. Portanto, para definir métodos específicos, você pode nomeá-los como:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Você pode encontrar exemplos aqui .

Zon
fonte
6

Veja um relatório JUnit. O JUnit já está organizado por pacote. Cada pacote possui (ou pode ter) classes TestSuite, cada uma das quais, por sua vez, executa vários TestCases. Cada TestCase pode ter vários métodos de teste do formulário public void test*(), cada um dos quais realmente se tornará uma instância da classe TestCase à qual eles pertencem. Cada método de teste (instância TestCase) possui um nome e um critério de aprovação / reprovação.

O que meu gerenciamento exige é o conceito de itens individuais do TestStep , cada um deles relatando seus próprios critérios de aprovação / reprovação. A falha de qualquer etapa do teste não deve impedir a execução das etapas subseqüentes.

No passado, os desenvolvedores de testes em minha posição organizavam as classes TestCase em pacotes que correspondiam às partes do produto em teste, criavam uma classe TestCase para cada teste e tornavam cada método de teste uma "etapa" separada no teste, complete com seus próprios critérios de aprovação / reprovação na saída JUnit. Cada TestCase é um "teste" independente, mas os métodos individuais, ou "etapas" de teste no TestCase, devem ocorrer em uma ordem específica.

Os métodos do TestCase foram as etapas do TestCase, e os projetistas de testes obtiveram um critério de aprovação / reprovação separado por etapa do teste. Agora as etapas do teste são confusas e os testes (é claro) falham.

Por exemplo:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Cada método de teste afirma e reporta seus próprios critérios de aprovação / reprovação separados. Colocar isso em "um grande método de teste" para fins de pedido perde a granularidade dos critérios de aprovação / reprovação de cada "etapa" no relatório de resumo da JUnit. ... e isso incomoda meus gerentes. Atualmente, eles estão exigindo outra alternativa.

Alguém pode explicar como uma JUnit com ordenação de método de teste codificado suportaria critérios de aprovação / reprovação separados de cada etapa de teste seqüencial, como exemplificado acima e exigido por minha gerência?

Independentemente da documentação, vejo isso como uma regressão séria na estrutura do JUnit que está dificultando a vida de muitos desenvolvedores de teste.

Desenvolvedor Anônimo
fonte
6

Atualização do JUnit 5 (e minha opinião)

Eu acho que é um recurso muito importante para o JUnit, se o autor do JUnit não deseja o recurso de pedido, por quê?

Por padrão, as bibliotecas de teste de unidade não tentam executar testes na ordem que ocorre no arquivo de origem.
JUnit 5 como JUnit 4 funcionam dessa maneira. Por quê ? Porque se a ordem importa, significa que alguns testes são acoplados entre eles e isso é indesejável para testes de unidade .
Portanto, o @Nestedrecurso introduzido pelo JUnit 5 segue a mesma abordagem padrão.

Mas para testes de integração, a ordem do método de teste pode ser importante, pois um método de teste pode alterar o estado do aplicativo da maneira esperada por outro método de teste. Por exemplo, quando você escreve um teste de integração para um processamento de check-out de e-shop, o primeiro método de teste a ser executado é o registro de um cliente, o segundo é a adição de itens na cesta e o último é o checkout. Se o executor do teste não respeitar essa ordem, o cenário de teste será defeituoso e falhará.
Portanto, no JUnit 5 (da versão 5.4), você tem a mesma possibilidade de definir a ordem de execução, anotando a classe de teste @TestMethodOrder(OrderAnnotation.class)e especificando a ordem @Order(numericOrderValue)para os métodos relevantes para a ordem.

Por exemplo :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

    @Order(3)
    @Test
    void checkoutOrder() {
        System.out.println("checkoutOrder");
    }

    @Order(2)
    @Test
    void addItemsInBasket() {
        System.out.println("addItemsInBasket");
    }

    @Order(1)
    @Test
    void createUserAndLogin() {
        System.out.println("createUserAndLogin");
    }
}

Resultado :

createUserAndLogin

addItemsInBasket

checkoutOrder

A propósito, especificar @TestMethodOrder(OrderAnnotation.class)não parece necessário (pelo menos na versão 5.4.0 que testei).

Nota lateral
Sobre a pergunta: o JUnit 5 é a melhor opção para escrever testes de integração? Eu não acho que ela deva ser a primeira ferramenta a ser considerada (pepino e companhia podem trazer valor e recursos mais específicos para testes de integração), mas em alguns casos de teste de integração, a estrutura JUnit é suficiente. Portanto, é uma boa notícia que o recurso existe.

davidxxx
fonte
4

Não tenho certeza se concordo. Se eu quiser testar o 'Upload de arquivo' e depois o teste 'Dados inseridos pelo upload de arquivo', por que não gostaria que eles fossem independentes um do outro? Perfeitamente razoável, acho que posso executá-los separadamente, em vez de ter os dois em um caso de teste de Golias.

Mattk
fonte
3

O que você deseja é perfeitamente razoável quando os casos de teste estão sendo executados como um conjunto.

Infelizmente, não há tempo para dar uma solução completa no momento, mas veja a classe:

org.junit.runners.Suite

O que permite chamar casos de teste (de qualquer classe de teste) em uma ordem específica.

Eles podem ser usados ​​para criar testes funcionais, de integração ou de sistema.

Isso deixa os testes de unidade como estão, sem ordem específica (conforme recomendado), independentemente de você ser executado dessa maneira ou não e depois reutilizar os testes como parte de uma imagem maior.

Reutilizamos / herdamos o mesmo código para testes de unidade, integração e sistema, às vezes orientados a dados, às vezes orientados por confirmação e às vezes executados como um conjunto.

CharlieS
fonte
2
Você não teve tempo de responder a essa pergunta desde 2014? ;)
Charlie
2

Veja minha solução aqui: "Junit e java 7."

Neste artigo, descrevo como executar testes junit em ordem - "como no seu código-fonte". Os testes serão executados, na ordem em que seus métodos de teste aparecerem no arquivo de classe.

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Mas, como disse Pascal Thivent, essa não é uma boa prática.

kornero
fonte
Eu já tinha visto a sua postagem no blog (em russo!), Mas isso é muito complicado.
Nicolas Barbulesco 04/04
1
@NicolasBarbulesco Eu tenho dois blogs (rus e eng). É muito complicado, porque você não deve criar testes com dependência da ordem de execução. Minha solução é uma solução alternativa, mas a solução real - é remover essa dependência.
Kornero 22/05
1
Esta postagem não contém uma resposta real. Por favor, considere adicionar pelo menos a explicação básica, além do link.
localidade padrão
0

Li algumas respostas e concordo que não é uma prática recomendada, mas a maneira mais fácil de solicitar seus testes - e a maneira como o JUnit executa testes por padrão é pelo nome alfabético crescente.

Então, apenas nomeie seus testes na ordem alfabética desejada. Observe também que o nome do teste deve começar com a palavra teste. Cuidado com os números

test12 será executado antes do test2

tão:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
fonte
0

Confira este: https://github.com/TransparentMarket/junit . Ele executa o teste na ordem em que são especificados (definidos no arquivo de classe compilado). Também possui um conjunto AllTests para executar testes definidos primeiro pelo subpacote. Usando a implementação AllTests, pode-se estender a solução também filtrando propriedades (costumávamos usar anotações @Fast, mas essas ainda não foram publicadas).

Martin Kersten
fonte
0

Aqui está uma extensão do JUnit que pode produzir o comportamento desejado: https://github.com/aafuks/aaf-junit

Sei que isso é contrário aos autores da filosofia JUnit, mas ao usar o JUnit em ambientes que não são estritamente testes de unidade (como praticado em Java), isso pode ser muito útil.

shlomi33
fonte
0

Acabei aqui pensando que meus testes não foram executados em ordem, mas a verdade é que a bagunça estava nos meus trabalhos assíncronos. Ao trabalhar com simultaneidade, você também deve executar verificações de simultaneidade entre seus testes. No meu caso, trabalhos e testes compartilham um semáforo, portanto, os próximos testes são interrompidos até que o trabalho em execução libere o bloqueio.

Sei que isso não está totalmente relacionado a esta pergunta, mas talvez possa ajudar a direcionar o problema correto

McCoy
fonte
0

você pode usar um destes códigos:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
Arezoo Bagherzadi
fonte