Passo 5: Repetir
3.3 Teste do comportamento Excepcional
3.3.3 Framework
Esta seção tem por objetivo apresentar a ferramenta desenvolvida com o objetivo de
dar suporte a abordagem proposta. Para isto, foi desenvolvida uma extensão do JUnit
framework chamada JUnitE.
O JUnitE permite a definição de fluxos excepcionais esperados em termos do local
onde a exceção é lançada, os locais intermediários onde passam antes de atingir o seu
tratador e o local onde a exceção é tratada. Também permite a verificação em tempo
de excecução os resultados obtidos nos testes do comportamento excepcional de uma
aplicação. A extensão também permite a verificação de transformações de exceções
mesmo que não haja o encapsulamento da exceção original na nova exceção.
A figura
3.11
mostra a estrutura do JUnitE. A classe principal é a JunitETestCase
nesta, estão concentradas as adaptações para fornecer meios para a verificação dos tes-
tes excepcionais. Para a criação de um teste excepcional o caso de teste deve estender
diretamente de JunitETestCase. O aspecto ExceptionLogAspect é responsável por moni-
3.3. TESTE DO COMPORTAMENTO EXCEPCIONAL
Figura 3.11 Visão jUnite tool
torar e a classe FileWriterUtil de salvar o log de execução dos fluxos excepcionais para a
posterior verificação. As anotações @PartialSequencialMode e @FullSequencialMode
indicam o tipo de verificação desejada no teste excepcional seguindo o modelo da seção
3.3.1onde @PartialSequencialMode indica a possibilidade de omissão de elementos do
fluxo excepcional e @FullSequencialMode indica que tanto o fluxo excepcional definido
quanto o obtido pela execução caso de testes devem ser iguais contrário, o teste deverá
falha. Caso um caso de teste não seja anotado por nenhuma destas duas, o modo de
verificação parcial é utilizado por padrão. As anotações @ForceException e @Force-
ExceptionOnContructor indicam que um teste excepcional deve forcar o lançamento de
uma exceção e devem ser usadas em conjunto com a anotação @Test do JUnit. Através
do pré-processamento na classe AspectGenerator estas anotações são utilizadas para a
3.3. TESTE DO COMPORTAMENTO EXCEPCIONAL
geração de aspectos que forçam o lançamento de uma exceção de acordo com os parâ-
metros fornecidos.
A Figura
3.12
podemos observar um exemplo de teste excepcional escrito para o
JUnitE. Neste exemplo, a definição do fluxo excepcional esperado é definido como um
array de string (linhas 13-18) que segue algumas convenções para indicar o tipo de
exceção (linha 14), o local onde a exceção é levantada (linha 15), locais intermediários
por onde a exceção é propagada (linha 16) e o local onde é tratada (linha 17). A or-
dem dos elementos nesta matriz indica a ordenação deste fluxo, desde o local onde a
exceção é levantada, até o local do tratamento desta exceção. Os métodos exception,
raiseSite, intermediateSite e handlingSite fazem parte da sintaxe para
a definição do fluxo excepcional. O método exception indica o tipo da exceção es-
perada, raiseSite indica o local onde a exceção é lançada, intermediateSite
é utilizado para indicar os locais intermediários e handlingSite para indicar o local
onde a exceção é capturada. Após definido o fluxo excepcional, o array definido deve
ser passado como parâmetro ao método setExceptionPath(String[]) antes da
execução do caso de teste.
O método testEHFlowDAOServlet (linha 10) implementa o caso de teste uti-
lizando a anotação @Test do JUnit (linha 9). Como em qualquer abordagem de teste
funcional, o teste é responsável por desencadear a condição de erro que resultará na exce-
ção esperada, no nosso exemplo a chamada ao método myServlet.service (linha
25).
Em alguns casos torna-se necessário redefinir o comportamento do resultado de um
teste. Por exemplo, podemos ter uma determinada situação em que o testador descobre
um erro devido à ocorrência de uma exceção não tratada que ocasiona a interrupção de
forma não esperada da execução do sistema. Neste caso ele poderá especificar o fluxo
excepcional indesejado para que, ao longo da evolução do sistema, seja possível verificar
se este fluxo inesperado ainda ocorre. Para isso deve-se especificar um teste com o fluxo
excepcional não esperado e o método result deverá ser redefinido da seguinte forma:
//Método da superclasse, o override é opcional.
//Use para modificar o resultado do teste.
public void result() {
//Ex: se uma exceção especificada ocorrer,
//o teste não deverá passar
assertTrue(!verifyResults());
}
3.3. TESTE DO COMPORTAMENTO EXCEPCIONAL
1 - public class MyTestCase extends JuntETestCase {
2-
3-
//This annotation forces an exception
4-
//to be trigged
5 -
@ForceException(exception="java.io.IOException",
6 -
method = "MyDAO.insertData",
7 -
methodReturnType = "void",
8 -
methodParType = "java.lang.String")
9 -
@Test
10-
public void testEHFlowDAOServlet() {
11-
12-
//Specifies the desired exception path
13-
String[] trace = new String[]{
14-
exception("java.io.IOException"),
15-
raiseSite("MyDAO.insertData "),
16-
intermediateSite("myFacade.insertData"),
17-
handlingSite("myServlet.service")
18-
};
19-
20-
//Sets the exception path
21-
super.setExceptionPath(trace);
22-
23-
//Calls the element that should handle
24-
//the exception
25-
myServlet.service();
26-
}
27-}
Figura 3.12 Exemplo de caso de teste
a execução de um caso de teste.
O JUnitE também permite a geração de um arquivo de log após a execução de testes.
Abaixo temos um exemplo ilustrativo do arquivo gerado:
1 - #pkg.MyException
2 - pkg.raisingSite
3 - pkg.intermediateSite1
4 - pkg.intermediateSite2
5 - @pkg.catchingSite
Neste arquivo, o símbolo # representa o tipo de exceção (linha 1), na linha subse-
quente (linha 2) temos o local onde a exceção foi levantada, neste caso
pkg.raisingSite. Sempre a linha após a exceção define o local onde a exceção
3.3. TESTE DO COMPORTAMENTO EXCEPCIONAL
foi levantada. Nas linhas 3 e 4 temos a definição dos locais intermediários por onde
a exceção trafega. Por fim, temos na linha 5 o local onde a exceção é tratada definida
pelo símbolo @. Caso este símbolo não apareça em um fluxo excepcional significa que
a exceção não foi capturada ocasionando o término da execução do caso de teste. Em
um mesmo arquivo de log podemos ter vários blocos similares ao exemplo. Isto indica
a presença de múltiplos fluxos excepcionais durante a execução do teste. Este arquivo
contém todas as informações do fluxo de exceção produzido pela execução do caso de
teste. Esta informação é importante pois com ela é possível acompanhar as mudanças
entre as versões de um sistema.
Figura 3.13 Resultado de um caso de teste.
Porém a verificação manual do log é opcional para a abordagem proposta. Para um
teste passar não é necessária avaliação manual deste arquivo. Está avaliação é realizada
automaticamente pelo JUnitE e a apresentação do resultado do teste é similar ao JUnit
o qual representa um teste que passou através de uma barra verde e barra vermelha para
testes que falharam. Na figura
3.13podemos observar o resultado de um teste no JUnitE.
Uma falha ocorre quando o fluxo excepcional, que é registrado no log de execução,
não for igual ao fluxo excepcional especificado. Esta verificação é realizada automatica-
mente comparando-os durante a execução do caso de teste.
O JUnitE também permite a criação de casos de teste que verifiquem o remapea-
mento de exceções de um tipo para outro como no exemplo da Figura
3.14. Esta figura
mostra a execução dos métodos a, b e c nesta ordem. O método c lança uma exceção
3.3. TESTE DO COMPORTAMENTO EXCEPCIONAL
Figura 3.14 Transformação de tipo uma exceção.
do tipo IOException e a propaga até o método b. O método b captura essa exceção
e a remapeia em uma exceção do tipo MyException que é posteriormente capturada
pelo método a. Infelizmente o JUnit não suporta este tipo de definição.
No documento
Uma Abordagem Leve para Testar o Comportamento Excepcional
(páginas 60-65)