Eu tenho alguns métodos que devem chamar System.exit()
certas entradas. Infelizmente, testar esses casos faz com que o JUnit seja encerrado! Colocar as chamadas de método em um novo Thread não parece ajudar, pois System.exit()
finaliza a JVM, não apenas o segmento atual. Existem padrões comuns para lidar com isso? Por exemplo, posso substituir um stub porSystem.exit()
?
[EDIT] A classe em questão é na verdade uma ferramenta de linha de comando que estou tentando testar dentro do JUnit. Talvez o JUnit simplesmente não seja a ferramenta certa para o trabalho? Sugestões para ferramentas complementares de teste de regressão são bem-vindas (de preferência algo que se integre bem ao JUnit e EclEmma).
java
multithreading
unit-testing
junit
testability
Chris Conway
fonte
fonte
System.exit()
é ruim - seu programa deve falhar rapidamente. Lançar uma exceção simplesmente prolonga um aplicativo em um estado inválido em situações em que um desenvolvedor deseja sair e gera erros falsos.Respostas:
De fato, Derkeiler.com sugere:
System.exit()
?System.exit()
realmente sair da JVM:Atualização em dezembro de 2012:
Will propõe nos comentários, usando as Regras do sistema , uma coleção de regras JUnit (4.9+) para testar o código usado
java.lang.System
.Isso foi mencionado inicialmente por Stefan Birkner em sua resposta em dezembro de 2011.
Por exemplo:
fonte
System.exit
sempre que um argumento inválido da linha de comando é fornecido.A biblioteca System Rules possui uma regra JUnit chamada ExpectedSystemExit. Com esta regra, você pode testar o código, que chama System.exit (...):
Divulgação completa: sou o autor dessa biblioteca.
fonte
ExpectedSystemRule
é bom, é claro; o problema é que ela requer uma biblioteca extra de terceiros que fornece muito pouco em termos de utilidade no mundo real e é específica da JUnit.exit.checkAssertionAfterwards()
.Que tal injetar um "ExitManager" nesses métodos:
O código de produção usa o ExitManagerImpl e o código de teste usa ExitManagerMock e pode verificar se exit () foi chamado e com qual código de saída.
fonte
Na verdade, você pode zombar ou apagar o
System.exit
método em um teste JUnit.Por exemplo, usando o JMockit, você pode escrever (existem outras maneiras também):
EDIT: Teste alternativo (usando a API JMockit mais recente) que não permite que nenhum código seja executado após uma chamada para
System.exit(n)
:fonte
exit
chamada (não que realmente tenha sido um problema, IMO).Runtime
pode ser ridicularizada " pode ser ridicularizada, mas não para,System.exit
pelo menos no meu cenário, na execução de testes em Gradle.Um truque que usamos em nossa base de código foi fazer com que a chamada para System.exit () fosse encapsulada em um impl Runnable, que o método em questão usou por padrão. Para teste de unidade, definimos um Runnable simulado diferente. Algo assim:
... e o método de teste JUnit ...
fonte
Crie uma classe com capacidade de simulação que envolva System.exit ()
Eu concordo com EricSchaefer . Mas se você usar uma boa estrutura de zombaria como o Mockito , basta uma classe simples e concreta, sem necessidade de uma interface e duas implementações.
Parando a execução de teste em System.exit ()
Problema:
Uma zombaria
Sytem.exit()
não terminará a execução. Isso é ruim se você quiser testar issothing2
se não é executado.Solução:
Você deve refatorar esse código conforme sugerido por martin :
E faça
System.exit(status)
na função de chamada. Isso força você a ter todos os seusSystem.exit()
s em um só lugar ou perto delemain()
. Isso é mais limpo do que chamarSystem.exit()
profundamente dentro de sua lógica.Código
Embrulho:
A Principal:
Teste:
fonte
Gosto de algumas das respostas já dadas, mas queria demonstrar uma técnica diferente que geralmente é útil ao testar o código legado. Código fornecido como:
Você pode fazer uma refatoração segura para criar um método que agrupe a chamada System.exit:
Em seguida, você pode criar um falso para o seu teste que substitui a saída:
Essa é uma técnica genérica para substituir o comportamento por casos de teste, e eu a uso o tempo todo ao refatorar o código legado. Geralmente não é onde eu vou deixar as coisas, mas uma etapa intermediária para testar o código existente.
fonte
Uma rápida olhada na API, mostra que o System.exit pode gerar uma exceção esp. se um gerenciador de segurança proíbe o desligamento da VM. Talvez uma solução seria instalar um gerente desse tipo.
fonte
Você pode usar o java SecurityManager para impedir que o encadeamento atual desligue a Java VM. O código a seguir deve fazer o que você deseja:
fonte
Para que a resposta do VonC seja executada no JUnit 4, modifiquei o código da seguinte maneira
fonte
Você pode testar System.exit (..) substituindo a instância do Runtime. Por exemplo, com TestNG + Mockito:
fonte
Existem ambientes em que o código de saída retornado é usado pelo programa de chamada (como ERRORLEVEL no MS Batch). Temos testes em torno dos métodos principais que fazem isso em nosso código, e nossa abordagem foi usar uma substituição semelhante do SecurityManager, como usado em outros testes aqui.
Ontem à noite, montei um pequeno JAR usando as anotações do Junit @Rule para ocultar o código do gerenciador de segurança, além de adicionar expectativas com base no código de retorno esperado. http://code.google.com/p/junitsystemrules/
fonte
A maioria das soluções
System.exit()
é chamadoSecurityManager
Portanto, a maioria das soluções não é adequada para situações em que:
System.exit()
assertAll()
, por exemplo.Não fiquei satisfeito com as restrições impostas pelas soluções existentes apresentadas nas outras respostas e, portanto, criei algo sozinho.
A classe a seguir fornece um método
assertExits(int expectedStatus, Executable executable)
que afirma queSystem.exit()
é chamado com umstatus
valor especificado e o teste pode continuar após ele. Funciona da mesma maneira que o JUnit 5assertThrows
. Ele também respeita um gerenciador de segurança existente.Há um problema restante: quando o código em teste instala um novo gerenciador de segurança que substitui completamente o gerenciador de segurança definido pelo teste. Todas as outras
SecurityManager
soluções baseadas em mim conhecidas sofrem o mesmo problema.Você pode usar a classe assim:
O código pode ser facilmente portado para JUnit 4, TestNG ou qualquer outra estrutura, se necessário. O único elemento específico da estrutura está falhando no teste. Isso pode ser facilmente alterado para algo independente de estrutura (exceto um Junit 4
Rule
Há espaço para melhorias, por exemplo, sobrecarregando
assertExits()
com mensagens personalizáveis.fonte
Use
Runtime.exec(String command)
para iniciar a JVM em um processo separado.fonte
Há um pequeno problema com a
SecurityManager
solução. Alguns métodos, comoJFrame.exitOnClose
, também chamamSecurityManager.checkExit
. No meu aplicativo, eu não queria que a chamada falhasse, então useifonte
Chamar System.exit () é uma prática ruim, a menos que seja feita dentro de um main (). Esses métodos devem gerar uma exceção que, em última análise, é capturada pelo seu main (), que chama System.exit com o código apropriado.
fonte