Ignorando Condicionalmente Testes no JUnit 4

365

OK, então a @Ignoreanotação é boa para marcar que um caso de teste não deve ser executado.

No entanto, às vezes eu quero ignorar um teste com base em informações de tempo de execução. Um exemplo pode ser se eu tiver um teste de simultaneidade que precise ser executado em uma máquina com um certo número de núcleos. Se esse teste fosse executado em uma máquina uniprocessadora, não acho que seria correto apenas passar no teste (já que não foi executado) e certamente não seria correto falhar no teste e interromper a compilação .

Então, eu quero poder ignorar testes em tempo de execução, pois esse parece ser o resultado certo (já que a estrutura de teste permitirá que a compilação passe, mas registre que os testes não foram executados). Tenho certeza de que a anotação não me dará essa flexibilidade e suspeito que precisarei criar manualmente o conjunto de testes para a classe em questão. No entanto, a documentação não menciona nada sobre isso e, analisando a API , também não está claro como isso seria feito programaticamente (ou seja, como eu crio programaticamente uma instância Testou similar equivalente à criada pela @Ignoreanotação?).

Se alguém fez algo semelhante no passado, ou tem uma idéia brilhante de como mais eu poderia fazer isso, ficaria feliz em ouvir sobre isso.

Andrzej Doyle
fonte

Respostas:

476

A maneira JUnit é fazer isso no tempo de execução org.junit.Assume.

 @Before
 public void beforeMethod() {
     org.junit.Assume.assumeTrue(someCondition());
     // rest of setup.
 }

Você pode fazer isso em um @Beforemétodo ou no próprio teste, mas não em um @Aftermétodo. Se você fizer isso no teste, seu @Beforemétodo será executado. Você também pode fazer isso dentro @BeforeClasspara impedir a inicialização da classe.

Uma falha de suposição faz com que o teste seja ignorado.

Edit: Para comparar com a @RunIfanotação de junit-ext , o código de exemplo ficaria assim:

@Test
public void calculateTotalSalary() {
    assumeThat(Database.connect(), is(notNull()));
    //test code below.
}

Sem mencionar que é muito mais fácil capturar e usar a conexão do Database.connect()método dessa maneira.

Yishai
fonte
11
@notnoop, essa não é minha observação. Eles são ignorados. O executor de teste da IDEA os relata dessa maneira, e uma olhada no código-fonte da JUnit mostra que ele relata o teste como ignorado.
Yishai
11
Para citar: "No futuro, isso pode mudar, e uma suposição falhada pode levar o teste a ser ignorado". Na verdade, mudou, a partir de 4.5 eu acredito. O javadoc atual diz: "O corredor JUnit padrão trata os testes com suposições falhadas como ignorados. Os corredores personalizados podem se comportar de maneira diferente." github.com/KentBeck/junit/blob/…
Yishai
4
O Eclipse 3.6 com Junit 4.8.1 relata suposições falsas como um teste de aprovação. O mesmo com a formiga 1.8.1.
Fijiaaron
8
O Eclipse relata suposições com falha porque a passagem é um bug: bugs.eclipse.org/bugs/show_bug.cgi?id=359944
Martin
11
@ JeffStorey, então você está procurando algumas coisas. Uma é a @BeforeClassanotação, onde você pode ter sua suposição falhada, o que ignorará toda a classe. Outra é @ClassRule(para o controle refinado, mas sobre toda a classe, uma vez).
Yishai 15/05
51

Você deve fazer o checkout do Junit-extprojeto. Eles possuem RunIfanotações que executam testes condicionais, como:

@Test
@RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
    //your code there
}

class DatabaseIsConnected implements Checker {
   public boolean satisify() {
        return Database.connect() != null;
   }
}

[Exemplo de código retirado do tutorial]

notnoop
fonte
3
Obrigado por esta resposta - uma sintaxe alternativa interessante para a funcionalidade, embora eu esteja indo Assumediretamente para não introduzir outra dependência.
Andrzej Doyle
3
Eu pessoalmente prefiro esta solução. Se você tiver muitos testes que devem ser executados com base nas mesmas condições, isso seria muito mais ideal do que ter que usar o Assume em todos os testes. Além disso, se isso puder ser usado no nível da classe e não no nível do método, será ainda mais ideal.
Richard
Eu preferiria, porque isso ajuda a executar o teste condicionalmente no tempo de execução. É adequado para a execução de vários testes de unidade e o requisito é executar os testes de unidade em um verificador específico. Fiquei realmente surpreso ao ver que o junit-ext não está disponível no repositório maven. Como conseguiríamos isso no projeto maven?
Shambhu # 21/14
4
Uma anotação como @RunIfsepara a condição quando um teste deve ser executado a partir do código de teste real, o que eu acho bom. O que eu não gosto é que isso requer um corredor de teste específico. Portanto, escrevi uma regra JUnit para ignorar condicionalmente os testes.
Rüdiger Herrmann
2
Depois de instalar o junit-ext jar (encontrado aqui code.google.com/p/junit-ext/downloads/… ) em nosso repositório local e implementar esta anotação @RunIf ... nada! É totalmente ignorado, e acho que o motivo pode ser que o junit-ext pareça depender do junit 4.5. Precisamos de 4.9+ devido ao teste de mola. Então ... não importa.
Marc
7

Na JUnit 4, outra opção para você pode ser criar uma anotação para indicar que o teste precisa atender aos seus critérios personalizados e, em seguida, estender o corredor padrão por conta própria e, usando a reflexão, baseie sua decisão nos critérios personalizados. Pode ser algo como isto:

public class CustomRunner extends BlockJUnit4ClassRunner {
    public CTRunner(Class<?> klass) throws initializationError {
        super(klass);
    }

    @Override
    protected boolean isIgnored(FrameworkMethod child) {
        if(shouldIgnore()) {
            return true;
        }
        return super.isIgnored(child);
    }

    private boolean shouldIgnore(class) {
        /* some custom criteria */
    }
}
Kyle Shrader
fonte
Embora isso pareça agradável e limpo, ele não funciona com as versões atuais se o JUnit4, pois BlockJUnit4ClassRunnernão oferece mais o isIgnoredmétodo.
Dave
-2

Uma observação rápida: Assume.assumeTrue(condition)ignora o restante das etapas, mas passa no teste. Para falhar no teste, use org.junit.Assert.fail()dentro da instrução condicional. Funciona da mesma forma, Assume.assumeTrue()mas falha no teste.

TIn TIn
fonte
5
Conforme observado nas respostas acima, uma suposição falhada não faz com que o teste seja aprovado, ele retorna um status separado. Alguns corredores podem relatar erroneamente isso como se fosse um passe, mas isso é um ponto fraco / falha no corredor de teste (e o corredor JUnit padrão exibe o teste como ignorado). E quanto à sua sentença final, falhar no teste não é especificamente o que eu quero (ed).
Andrzej Doyle
Ah ok. Os testes passaram na suposição com falha no meu caso, mas eu queria que eles fossem relatados como com falha (estava verificando uma exceção no Test Watcher). Forçar um fracasso me ajudou.
Tin Tin