Universidade Federal do Rio Grande do Norte
Centro de Ciências Exatas e da Terra
Departamento de Informática e Matemática Aplicada
Programa de Pós-Graduação em Sistemas e Computação
UMA ABORDAGEM PARA VERIFICAÇÃO DO
COMPORTAMENTO EXCEPCIONAL A PARTIR DE
REGRAS DE DESIGN E TESTES
RICARDO JOSÉ SALES JÚNIOR
Dissertação apresentada ao Programa de Pós-Graduação em Sistemas e
Computação do Departamento de Informática e Matemática Aplicada da
Universidade Federal do Rio Grande do Norte como requisito parcial para a
obtenção do grau de Mestre em Sistemas e Computação.
Natal/RN
FEVEREIRO - 2013
Universidade Federal do Rio Grande do Norte
Centro de Ciências Exatas e da Terra
Departamento de Informática e Matemática Aplicada
Programa de Pós-Graduação em Sistemas e Computação
UMA ABORDAGEM PARA VERIFICAÇÃO DO
COMPORTAMENTO EXCEPCIONAL A PARTIR DE
REGRAS DE DESIGN E TESTES
Ricardo José Sales Júnior
Dissertação apresentada ao Programa de
Pós-Graduação
Computação
em
do
Sistemas
e
Departamento
de
Informática e Matemática Aplicada da
Universidade Federal do Rio Grande do
Norte como requisito parcial para a
obtenção do grau de Mestre em Sistemas
e Computação.
Profª. Drª. Roberta de Souza Coelho
Orientadora
Natal/RN
2013
Catalogação da Publicação na Fonte. UFRN / SISBI / Biblioteca Setorial
Especializada do Centro de Ciências Exatas e da Terra – CCET.
Sales Júnior, Ricardo José.
Uma abordagem para verificação do comportamento excepcional a partir de
regras de design e testes / Ricardo José Sales Júnior. – Natal, RN, 2013.
133 f. : il.
Orientadora: Profa. Dra. Roberta de Souza Coelho.
Dissertação (Mestrado) – Universidade Federal do Rio Grande do Norte. Centro
de Ciências Exatas e da Terra. Departamento de Informática e Matemática Aplicada.
Programa de Pós-Graduação em Sistemas e Computação.
1. Software – Teste - Dissertação. 2. Tratamento de exceções – Dissertação. 3.
Comportamento excepcional – Dissertação. 4. Regras de design – Dissertação. I.
Coelho, Roberta de Souza. II. Título.
RN/UF/BSE-CCET
CDU 004.4
Dedico essa dissertação a Jeová Deus, a
minha Esposa Raquel, meus pais e amigos que
me deram toda a ajuda que precisei.
Agradecimentos
Em primeiro lugar, agradeço a Jeová Deus por me dar a força necessária para
conciliar as minhas atividades como trabalhador, marido, trabalhador e aluno de
mestrado. Agradeço a minha esposa pelo amor, bondade, paciência e abnegação, as
horas que ela abdicou de estar comigo para que eu pudesse encerrar mais esta etapa.
Agradeço aos meus pais que estiveram comigo por toda a minha vida e cujo exemplo
e dedicação me educaram mais do que qualquer escola. À minha orientadora mais
uma vez agradeço a paciência, as horas de sono perdidas, o cuidado e a preocupação
na vida acadêmica e pessoal. Aos meus irmãos e amigos sou grato pelo apoio. Aos
meus chefes no Ministério Público do Rio Grande do Norte e colegas de equipe,
agradeço o suporte para a realização da minha pesquisa. Aos professores Eduardo
Aranha e Gibeon Soares as sugestões e apoio ao experimento. Também agradeço
aos colegas Israel Garcia, Rafael Di Bernardo e Luciano Carvalho pelas trocas de
figurinhas relacionadas com as nossas pesquisas.
Resumo
Verificar a conformidade entre a implementação de um sistema e suas regras de
design é uma atividade importante para tentar garantir que não ocorra a degradação
entre os padrões arquiteturais definidos para o sistema e o que realmente está
implementado no código-fonte. Especialmente no caso de sistemas dos quais se
exige um alto nível de confiabilidade é importante definir regras de design (design
rules) específicas para o comportamento excepcional. Tais regras descrevem como
as exceções devem fluir através do sistema, definindo quais são os elementos
responsáveis por capturar as exceções lançadas por outros elementos do sistema.
Entretanto, as abordagens atuais para verificar automaticamente regras de design
não proveem mecanismos adequados para definir e verificar regras de design
específicas para a política de tratamento de exceções das aplicações. Este trabalho
propõe uma abordagem prática para preservar o comportamento excepcional de uma
aplicação ou família de aplicações, baseada na definição e verificação automática em
tempo de execução de regras de design de tratamento de exceção para sistemas
desenvolvidos em Java ou AspectJ. Para apoiar esta abordagem foi desenvolvida, no
contexto deste trabalho, uma ferramenta chamada VITTAE (Verification and
Information Tool to Analyze Exceptions) que estende o framework JUnit e permite
automatizar atividades do teste de regras de design excepcionais. Foi realizado um
estudo de caso preliminar com o objetivo de avaliar a eficácia da abordagem proposta
sobre uma linha de produto de software. Além deste, foi realizado um experimento
cujo objetivo foi realizar uma análise comparativa entre a abordagem proposta e uma
abordagem baseada na ferramenta JUnitE, que também propõe testar o código de
tratamento de exceções utilizando testes JUnit. Os resultados mostraram que as
regras de design excepcionais evoluem ao longo de diferentes versões de um sistema
e que a VITTAE pode auxiliar na detecção de defeitos no código de tratamento de
exceção.
Palavras-chave: Tratamento de Exceções, Comportamento Excepcional, Testes de
Software, Regras de Design.
Abstract
Checking the conformity between implementation and design rules in a system is an
important activity to try to ensure that no degradation occurs between architectural
patterns defined for the system and what is actually implemented in the source code.
Especially in the case of systems which require a high level of reliability is important to
define specific design rules for exceptional behavior. Such rules describe how
exceptions should flow through the system by defining what elements are responsible
for catching exceptions thrown by other system elements. However, current
approaches to automatically check design rules do not provide suitable mechanisms
to define and verify design rules related to the exception handling policy of
applications. This paper proposes a practical approach to preserve the exceptional
behavior of an application or family of applications, based on the definition and runtime
automatic checking of design rules for exception handling of systems developed in
Java or AspectJ. To support this approach was developed, in the context of this work,
a tool called VITTAE (Verification and Information Tool to Analyze Exceptions) that
extends the JUnit framework and allows automating test activities to exceptional
design rules. We conducted a case study with the primary objective of evaluating the
effectiveness of the proposed approach on a software product line. Besides this, an
experiment was conducted that aimed to realize a comparative analysis between the
proposed approach and an approach based on a tool called JUnitE, which also
proposes to test the exception handling code using JUnit tests. The results showed
how the exception handling design rules evolve along different versions of a system
and that VITTAE can aid in the detection of defects in exception handling code.
Keywords: Exception Handling, Exceptional Behavior, Exception Policy, Software
Testing, Design Rules.
Sumário
1
Introdução ........................................................................................................ 14
1.1
Exemplo Motivacional...................................................................................... 17
1.2
Limitações das abordagens atuais .................................................................. 21
1.3
Solução proposta............................................................................................. 22
1.4
Objetivos ......................................................................................................... 22
1.5
Contribuições escritas ao longo da pesquisa .................................................. 23
1.6
Organização do texto ...................................................................................... 23
2
Fundamentação Teórica .................................................................................. 25
2.1
Tolerância a falhas .......................................................................................... 25
2.1.1
Tratamento de exceções .............................................................................. 26
2.1.2
Tratamento de exceções em Java ............................................................... 26
2.2
Verificação de Software ................................................................................... 27
2.2.1
Análise estática ............................................................................................ 28
2.2.2
Testes de Software ...................................................................................... 28
Testes funcionais (ou testes caixa-preta) .................................................................. 29
Testes estruturais (ou testes caixa-branca) .............................................................. 29
2.2.3
2.3
Níveis de teste ............................................................................................. 30
Automação de testes ....................................................................................... 31
2.3.1
JUnit ............................................................................................................. 32
2.4
Programação Orientada a Aspectos e AspectJ ............................................... 36
2.5
Regras de Design ............................................................................................ 37
2.6
Discussões ...................................................................................................... 38
3
Abordagem Proposta ....................................................................................... 39
3.1
Exemplo Motivador – Refatoração de Sistemas Orientados a Objetos para
Aspectos.................................................................................................................... 39
3.2
Visão Geral da Abordagem Proposta .............................................................. 43
3.2.1
Passo1: Definição das Regras de Design Excepcionais da Aplicação ........ 44
3.2.2
Passo 2 : Projeto e Automação do Teste ..................................................... 47
3.2.3
Passo 3 : Execução do teste ........................................................................ 49
3.2.4
Passo 4 : Avaliação do Teste ....................................................................... 49
3.3
4
Discussões ...................................................................................................... 52
A Ferramenta VITTAE ...................................................................................... 54
4.1
Visão Geral da Arquitetura da VITTAE ............................................................ 54
4.2
Pacote de aspectos ......................................................................................... 54
4.3
Pacote de controladores .................................................................................. 56
4.4
Pacote de exceções ........................................................................................ 56
4.5
Pacote de classes utilitárias ............................................................................ 56
4.6
Pacote de geradores ....................................................................................... 57
4.7
Pacote de entidades ........................................................................................ 57
4.8
Verificação da regra de design ........................................................................ 58
4.9
Interface gráfica ............................................................................................... 59
4.10
5
Discussões ................................................................................................... 59
Estudo de Caso ................................................................................................ 61
5.1
Mobile Media (MM) .......................................................................................... 61
5.2
Aplicando a Abordagem a uma Linha de Produto de Software ....................... 63
5.3
Discussões ...................................................................................................... 67
6
Estudo avaliativo – Comparação entre VITTAE e JUnitE ............................. 70
6.1
JUnitE .............................................................................................................. 71
6.2
Avaliação quantitativa ...................................................................................... 73
6.2.1
Definição do experimento............................................................................. 74
6.2.2
Hipóteses ..................................................................................................... 75
6.2.3
Projeto do experimento ................................................................................ 76
6.2.3.1
Escolha dos sujeitos ................................................................................. 77
6.2.3.2
Escolha dos sistemas ............................................................................... 77
6.2.3.3
Organização do experimento .................................................................... 80
6.2.3.4
Operação do experimento......................................................................... 81
6.2.4
Análise dos resultados ................................................................................. 86
6.2.5
Ameaças à validade do estudo .................................................................. 102
6.3
6.3.1
6.4
Avaliação qualitativa ...................................................................................... 102
Ameaças à validade do estudo .................................................................. 108
Discussões .................................................................................................... 109
7
Trabalhos relacionados ................................................................................. 110
7.1
Abordagens de verificação estática ............................................................... 110
7.2
Abordagens de teste ..................................................................................... 111
7.3
Abordagens de regras de design para tratamento de exceção ..................... 113
7.4
Processos de Desenvolvimento .................................................................... 115
7.5
Discussões .................................................................................................... 116
8
Considerações Finais .................................................................................... 117
8.1
Publicações relacionadas .............................................................................. 118
8.2
Trabalhos futuros........................................................................................... 119
Referências ............................................................................................................ 121
Apêndice A – Artigos Publicados ........................................................................ 131
Apêndice B – Regras de Design excepcionais da Mobile Media ...................... 132
Lista de Figuras
Figura 1: Arquitetura de um sistema baseada em camadas ..................................... 18
Figura 2: Técnica de Teste Funcional ....................................................................... 29
Figura 3:Técnica de teste estrutural .......................................................................... 30
Figura 4: Modelo V que descreve a correspondência entre as atividades de
desenvolvimento e os níveis de teste ........................................................................ 30
Figura 5: Arquitetura do JUnit (Fonte: http://www.devmedia.com.br/junitimplementando-testes-unitarios-em-java-parte-i/1432) ............................................. 32
Figura 6: Resultados visuais do JUnit ....................................................................... 35
Figura 7: Arquitetura de um sistema refatorado para Aspectos ................................ 40
Figura 8: Visão esquemática do "ladrão de exceções" ............................................. 42
Figura 9: Sequência de Atividades da Abordagem Proposta .................................... 44
Figura 10: XML Schema para o arquivo que define as regras de design da aplicação
.................................................................................................................................. 46
Figura 11: Estrutura do projeto com a VITTAE e localização do arquivo de contratos
.................................................................................................................................. 47
Figura 12: Tela do JUnit sem a extensão da VITTAE ............................................... 50
Figura 13: Exemplo de utilização do framework JUnit em conjunto com a VITTAE
para execução automatizada dos testes ................................................................... 50
Figura 14: Arquitetura da Vittae................................................................................. 55
Figura 15: Inicialização do aspecto de checagem ..................................................... 58
Figura 16: Diagrama de sequência para verificação da regra de design .................. 59
Figura 17: Interface gráfica da VITTAE para configuração do projeto eclipse .......... 60
Figura 18: Janela da VITTAE para escolha do projeto eclipse a ser configurado ..... 60
Figura 19: Design OO da MobileMedia ..................................................................... 62
Figura 20: Design AO da MobileMedia ...................................................................... 62
Figura 21: Diagrama de classes e pacotes com a estrutura do Sistema Bancário.... 78
Figura 22: Diagrama de classes e pacotes com a estrutura do Sistema de Fichas .. 79
Figura 23:Resultado do teste na VITTAE .................................................................. 85
Figura 24: Resultado do teste na JUnitE ................................................................... 85
Figura 25: Gráfico Box-Plot para os dados da Q1 ..................................................... 89
Figura 26: Resultados individuais para cada ferramenta na Q1 ................................ 90
Figura 27: Comparação visual entre os dados obtidos na Q1 e o modelo predito no
gráfico de envelope ................................................................................................... 91
Figura 28: Gráfico Box-Plot para os dados da Q2 ..................................................... 93
Figura 29: Resultados individuais para os dados da Q2 ........................................... 94
Figura 30: Comparação visual entre os dados obtidos na Q2 após a transformação e
o modelo predito no gráfico de envelope. ................................................................. 95
Figura 31: Gráfico em barras para a Q4 .................................................................... 98
Figura 32: Gráfico Box-Plot para a Q5 ...................................................................... 99
Figura 33: Resultados individuais para os dados da Q5 ......................................... 100
Figura 34: Comparação visual entre os dados obtidos na Q5 após a transformação e
o modelo predito no gráfico de envelope. ............................................................... 101
Figura 35: Gráfico em barras com o resultado da P5 .............................................. 104
Figura 36: Gráfico em barras com o resultado da P6 .............................................. 104
Figura 37: Gráfico em barras com o resultado da P7 .............................................. 104
Figura 38: Gráfico em barras com o resultado da P8 .............................................. 105
Figura 39: Gráfico em barras com o resultado da P9 .............................................. 105
Figura 40: Gráfico Box-Plot referente ao tempo para a configuração do ambiente
para as duas ferramentas........................................................................................ 107
Figura 41: Desempenho individual dos sujeitos ao configurar o ambiente das
ferramentas. ............................................................................................................ 108
Lista de Tabelas
Tabela 1: Métricas da MobileMedia ........................................................................... 61
Tabela 2: Exemplos de Contratos de Tratamento de Exceções da MobileMedia ..... 64
Tabela 3:Resultados da execução da abordagem da MobileMedia em diferentes
versões ...................................................................................................................... 69
Tabela 4: Comparação de características entre a VITTAE e JUnitE ......................... 73
Tabela 5: Questões relacionadas ao objetivo do experimento .................................. 75
Tabela 6: Métricas relacionadas com as questões levantadas ................................. 75
Tabela 7: Hipóteses baseadas nas questões ............................................................ 76
Tabela 8: Comparação de características entre os dois sistemas ............................ 79
Tabela 9: Organização dos sujeitos no experimento utilizando o delineamento em
quadrado latino.......................................................................................................... 81
Tabela 10 Relação entre as fases do experimento e os dias em que elas foram
executadas ................................................................................................................ 81
Tabela 11: Regra de Design Excepcional de cada sistema ...................................... 82
Tabela 12: Resultado esperado das regras de design descritas nas ferramentas .... 83
Tabela 13: Lista de perguntas do questionário e a relação delas com as questões
levantadas para o experimento ................................................................................. 86
Tabela 14: Dados obtidos para a Q1 ......................................................................... 88
Tabela 15: Mediana e Desvio padrão dos dados da Q1 ........................................... 89
Tabela 16: Resultados do teste ANOVA para os dados da Q1 ................................. 92
Tabela 17: Dados obtidos para a Q2 ......................................................................... 92
Tabela 18: Mediana e Desvio padrão dos dados da Q2: .......................................... 93
Tabela 19: Dados da Q2 após transformação Box-Cox ............................................ 95
Tabela 20: Resultados do teste ANOVA para os dados da questão 2 ...................... 96
Tabela 21: Dados obtidos para a Q3 ......................................................................... 96
Tabela 22: Dados obtidos para a Q4 ......................................................................... 97
Tabela 23: Dados obtidos para a Q5 ......................................................................... 99
Tabela 24: Mediana e Desvio padrão dos dados da Q5: .......................................... 99
Tabela 25: Dados da Q5 após transformação Box-Cox .......................................... 101
Tabela 26: Resultados do teste ANOVA para os dados da questão 2 .................... 102
Tabela 27: Perguntas da avaliação qualitativa ........................................................ 103
Tabela 28: Tempo para preparação do ambiente (P11).......................................... 106
Tabela 29: Expressões da LSD destinadas ao comportamento excepcional .......... 114
Tabela 30: Construções da DCL voltadas para o fluxo excepcional ....................... 114
1 Introdução
Na atualidade, os sistemas resultantes do desenvolvimento de software
geralmente são compostos por uma coleção de componentes distribuídos que
precisam lidar com entradas provenientes de uma variedade de fontes, executar em
diversos tipos de ambientes e obedecer a requisitos rigorosos de confiabilidade. No
que diz respeito aos requisitos de confiabilidade, diversas técnicas podem ser
utilizadas, mas uma das mais conhecidas e que está embutida na maior parte das
linguagens modernas de programação é a utilização de mecanismos de tratamento
de exceção [Cristian 1982]. Estes mecanismos auxiliam os desenvolvedores a
construir aplicações mais robustas através da separação do fluxo de comportamento
excepcional do fluxo normal de controle [Parnas and Wurges 1976].
Estes mecanismos ajudam a garantir a modularidade do sistema na presença
de erros, visto que eles oferecem abstrações para: (i) representar situações de erros
em módulos do sistema como exceções; (ii) encapsular atividades de tratamento de
exceção em entidades manipuladoras (handlers); (iii) definir partes dos módulos do
sistema como regiões protegidas para a ocorrência do tratamento de exceções; (iv)
associar essas regiões com os handlers; (v) especificar explicitamente as interfaces
de tratamento de exceção dos módulos.
Embora estudos como os de Castor et al. [Castor et al. 2006], Cabral e Marques
[Cabral and Marques 2007] e Cristian [Cristian 1994] mostrem que uma grande
quantidade de código é dedicada à descoberta e tratamento de erros, outros trabalhos
como os de Rubira et al. [Rubira et al. 2005] e de Kienzle [Kienzle 2008] mostram que
lidar com as manifestações de erros e exceções em outros estágios do
desenvolvimento, como por exemplo requisitos, design e testes, é um aspecto que
tem recebido pouca atenção nos processos de desenvolvimento. Os desenvolvedores
tendem a concentrar suas atividades de design no comportamento normal da
aplicação e esquecem o design do comportamento excepcional [Sha, Gerg and
Harrold 2008]. Eles geralmente lidam com a detecção e o tratamento de exceções
somente em atividades de implementação [Cristian 1982]. O resultado disso é o uso
inadequado dos mecanismos de tratamento de exceção, comprometendo a
confiabilidade do sistema [Kienzle 2008].
14
As construções para tratamento de exceções geralmente são propensas a
falhas devido à falta de atenção com o comportamento excepcional durante outras
disciplinas e atividades do desenvolvimento de software [Avizienis 1997]. Nesta
realidade, mesmo que a intenção inicial seja utilizar o tratamento de exceções para
melhorar a robustez dos sistemas, o uso indevido pode ser uma fonte de falhas. Um
bom exemplo disso está na utilização da orientação a aspectos para modularizar o
tratamento de exceções. Os trabalhos de Castor et al. [Castor et al. 2006], Coelho et
al. [Coelho et al 2008a, Coelho et al 2008b] e Sales Júnior et al. [Sales Júnior et al.
2010] destacam que se determinados cuidados não forem tomados, a modularização
do tratamento de exceções através de aspectos pode ser uma fonte de erros.
Para lidar com este tipo de problema, abordagens baseadas em análise
estática foram propostas para descobrir falhas no código de tratamento de exceção
[Coelho et al. 2008a, Fahndrich et al. 1998, Robiliard and Murphy 2003, Chang et al.
2001, Garcia et al. 2011]. Estas abordagens são baseadas em ferramentas que
descobrem os caminhos que as exceções fazem a partir dos métodos que as lançam
até os elementos responsáveis por capturá-las. Entretanto, devido às limitações
inerentes às abordagens de análise estática e às características das linguagens de
programação modernas, tais como herança, polimorfismo e chamadas virtuais, estas
abordagens geralmente trazem um número muito grande de fluxos de exceção a
serem analisados, sem contar os muitos falsos positivos, que nada mais são do que
fluxos que na realidade não serão executados [Sinha and Harrold 2000].
Consequentemente, trabalho manual adicional deve ser realizado para verificar
quando um fluxo excepcional detectado pode realmente acontecer, e quando ele
realmente é importante. Esse trabalho adicional manual pode tornar tais abordagens
tão caras em termos de tempo e recursos a ponto de serem na realidade proibitivas.
Além disso, uma vez que os fluxos de interesse são encontrados, tanto quanto
é do nosso conhecimento, a única forma de documentá-los é através de um texto
informal em linguagem natural. Ainda outra limitação destas abordagens é o fato de
que elas somente podem ser utilizadas após pelo menos parte do sistema estar
implementada.
Por outro lado, outras abordagens como as de Rubira et al. [Rubira et al 2005]
e Kienzle [Kienzle 2008] focadas em metodologias de desenvolvimento, tem como
base a especificação do comportamento excepcional. Contudo, estas metodologias
não são atualmente passíveis de automação e estão fortemente baseadas em
15
documentação, impondo uma sobrecarga indesejável para ambientes em que se
exige uma maior agilidade. A experiência de trabalhos como os de Misra, Kumar e
Kumar [Misra, Kumar and Kumar 2009] tem mostrado que metodologias mais leves
estão sendo usadas com mais sucesso no desenvolvimento de aplicações
atualmente. Tais metodologias incentivam fortemente a automação dos testes, e a
construção deles antecipadamente à implementação baseando-se nas especificações
de requisitos, protótipos [Beck and Andres 2004, Palmer and Felsing 2002] e modelos
arquiteturais.
Partindo do pressuposto que as regras de comportamento excepcional são
padrões que devem ser obedecidos durante o desenvolvimento de um software, podese considerar então que elas são um tipo de regra de design [Baldwin and Clark 1999].
Uma das principais aplicações para utilização das regras de design é definir padrões
arquiteturais que devem ser obedecidos durante a implementação e manutenção de
um software. Tais padrões precisam ser estritamente seguidos em todas as fases do
desenvolvimento de software. Atualmente estas regras de design, geralmente, são
definidas e checadas manualmente. Porém essa atividade se torna demasiadamente
custosa para sistemas de grande porte. Trabalhos como os de Brunet, Neto e
Figueredo [Brunet, Neto and Figueredo 2009] e Neto [Neto 2010] apresentam
abordagens para definir e automaticamente verificar as regras de design dos
sistemas. Uma limitação comum dessas abordagens é que elas não proveem
maneiras apropriadas de definir regras de design relacionadas com fluxos de
exceção.
Pelas razões acima citadas verifica-se que são necessárias ferramentas que
(i) ajudem os desenvolvedores a entender o impacto das mudanças na política de
tratamento de exceções e (ii) encontrem inconformidades entre as regras de design e
o código que está sendo incluído ou alterado.
Este trabalho propõe uma abordagem baseada em contratos em uma
linguagem baseada em xml [Sales Júnior, Coelho and Lustosa Neto 2010] que
definem regras de design específicas para fluxos de exceção através da especificação
de que quando determinados elementos (sejam eles métodos, classes, aspectos ou
pacotes) lançarem uma exceção (chamamos aqui esses elementos de signalers)
quais deverão ser os outros elementos específicos por tratar essa exceção
(chamamos aqui esses elementos de handlers). Além disso, a abordagem provê uma
ferramenta chamada VITTAE (Verification and Information Tool to Analyze
16
Exceptions) que permite (i) verificar automaticamente através de testes unitários
(estendendo o framework JUnit [JUnit 2012]) se as regras de design estão sendo
obedecidas em sistemas Java [Java 2012] e AspectJ [AspectJ 2012], (ii) gerar parte
do código dos testes de comportamento excepcional baseados nas regras de design,
(iii) gerar também os objetos mock dinâmicos [Freese 2002] que simulam a
instanciação de objetos de teste de comportamento excepcional e (iv) montar o
ambiente no projeto Java para realização dos testes utilizando a ferramenta.
Nossa abordagem pode ser utilizada tanto para escrever as regras de design e
os testes previamente às fases de desenvolvimento, usando o conceito de
desenvolvimento dirigido a testes [Beck 2003], quanto posteriormente ao
desenvolvimento, auxiliando nas atividades de manutenção.
Esta abordagem foi aplicada inicialmente em uma linha de produto de software
[Clements and Northrop 2002] escrita em Java e AspectJ, a MobileMedia [Figueiredo
et al. 2008], com o objetivo de avaliar sua eficácia em detectar a degradação das
regras de design de uma aplicação durante sua evolução em diversas versões.
Adicionalmente foi realizado um experimento controlado, que comparou esta
abordagem com a JUnitE [Di Bernardo et al. 2011], proposta também para
especificação e verificação de fluxos de tratamento de exceções utilizando testes
unitários.
1.1 Exemplo Motivacional
Nesta subseção é apresentado um exemplo ilustrativo simples para levantar
algumas limitações da utilização dos casos de teste JUnit puros para detectar bugs
de tratamento de exceção.
17
Figura 1: Arquitetura de um sistema baseada em camadas
Considere um sistema com arquitetura em camadas, como o exemplificado na
Figura 1. Ele é composto pelas seguintes camadas:
Dados (Data Layer)
Negócio (Business Layer)
Interface Gráfica (GUI Layer)
Uma regra de design de tratamento de exceções que deve ser obedecida nesta
aplicação é a seguinte: as exceções lançadas por Objetos da camada de Dados (Data
Layer) representam problemas no acesso à base de dados. Elas são subtipos da
exceção DAOException. Essas exceções devem ser tratadas pela Servlet na
camada de interface gráfica (GUI Layer). Com o objetivo de verificar se esta política
está sendo obedecida, o desenvolvedor pode tentar construir o seguinte caso de teste
JUnit:
1. public void testDAOEHPolicy (){
2.
Servlet s1 = new Servlet();
3.
s1.service();
4. }
18
Se nenhuma instância de DAOException
escapar deste método, o
desenvolvedor pode presumir que a exceção foi adequadamente tratada pela
Servlet. Entretanto, dois problemas podem acontecer, e estes não serão
detectados por este teste:
A instância de DAOException pode ter sido erroneamente tratada pelo
Facade
que neste caso estaria atuando como um elemento
intermediário entre o signaler e o handler definidos na regra de design.
Durante o teste, o DAO pode não lançar a exceção, o que tornaria o
teste sem efeito.
A construção do JUnit que permite verificar as exceções é a utilização da
anotação @Test(expected=<NOME_DA_EXCEÇÃO>.class). Exemplificamos a
utilização dessa construção no trecho de código a seguir, supondo agora uma outra
situação onde o desenvolvedor deseja verificar se a Servlet lança uma exceção do
tipo DAOException quando ocorre um erro no acesso aos dados.
1. @Test(expected=DAOException.class)
2. public void testDAOEHPolicy(){
3.
Servlet s1 = new Servlet();
4.
s1.service();
5. }
Embora o teste detecte se a exceção foi realmente lançada, esta abordagem
ainda apresenta limitações, visto que ela pode esconder um tipo sutil de erro. Por
exemplo, espera-se que a exceção DAOException seja lançada a partir da camada
de dados devido a um erro no acesso ao banco. Porém devido a um problema de
programação
a
camada
de
dados
pode
ter
lançado
uma
exceção
NullPointerException, e essa exceção pode ter sido capturada por um tratador
genérico (generic handler), ou seja, uma cláusula catch para o tipo genérico
Exception (ou também conhecido como catch-all). Se dentro dessa cláusula a
exceção DAOException for lançada, o teste irá passar, mas um erro de programação
terá sido ocultado.
19
Outro cenário comum no contexto da utilização de orientação a aspectos, é o
padrão de bug chamado “ladrão de exceções” [Coelho et al. 2008c]. Esse cenário
ocorre quando um aspecto responsável por tratar uma exceção não consegue realizar
a tarefa visto que um elemento do código base prematuramente capturou a exceção,
geralmente também pelo abuso na utilização da cláusula catch-all.
Uma maneira de prevenir esses dois últimos problemas relatados seria a troca
das cláusulas catch-all indevidamente utilizadas por cláusulas de catch específicas.
No entanto, essa solução ainda é muito frágil, visto que depende de convenções de
código. Além disso, nada impede que uma cláusula catch-all indevida possa ser
inserida posteriormente durante uma manutenção de código.
O framework JUnit não provê uma maneira de verificar quando um elemento
intermediário erroneamente trata uma exceção. A única maneira de realizar isso seria
criar um teste para cada elemento intermediário, uma atividade que consome um
tempo adicional muito grande e está sujeita a erros. Esta limitação é compreensível
visto que o objetivo do JUnit é realizar testes unitários e não testes de tratamento de
exceções. Não obstante, quando se trata de exceções, situações em que o tratamento
de exceções é realizado incorretamente, como por exemplo uma propagação global
de uma exceção inesperada, pode ser uma fonte significativa de bugs, como mostram
os trabalhos de Coelho et al. [Coelho et al. 2008a] e Robillard e Murphy [Robillard and
Murphy 2003]. Isso é particularmente percebido em linguagens de programação como
Java e C#, onde exceções são objetos que podem ser capturados através da
subsunção de tipos [Robillard and Murphy 2003], ou seja, um tratador para uma
exceção E captura também exceções E’, onde E’ é um subtipo de E.
Por outro lado, JUnit é considerado como um padrão para automatização de
testes, e tem sido estendida em alguns cenários para ser utilizada em um escopo
maior do que o de testes unitários, como mostra o artigo de Partington no caso dos
testes de integração [Partington 2012]. Além disso, esta dissertação parte do princípio
de que a preocupação com o tratamento de exceções deve fazer parte de todas as
atividades de desenvolvimento de software [Rubira et al. 2005], e o teste
automatizado dá um suporte melhor a essa ideia do que um processo de
desenvolvimento baseado fortemente em documentação. Como consequência, este
trabalho considera que a abordagem baseada em JUnit para testes pode e deve ser
empregada em outros contextos, não somente para o teste unitário comum.
20
A linha de base desta dissertação é que existe a necessidade de uma
abordagem que facilite o desenvolvimento de casos de teste para comportamento
excepcional que permita que: (i) sejam definidas regras de design, especificando
quais elementos (handlers) são responsáveis por tratar as exceções (exceptions)
lançadas por outros elementos (signalers); (ii) essas regras sejam consideradas como
uma documentação do tratamento de exceções do sistema; (iii) sejam construídos e
executados testes que verifiquem se essas regras de design foram obedecidas;
1.2 Limitações das abordagens atuais
Como observado anteriormente, as abordagens baseadas em análise estática
como as de Coelho et al. [Coelho et al. 2008a] e Garcia et al. [Garcia et al. 2011]
descobrem todos os possíveis fluxos de exceção. Essas abordagens de análise
estática possuem limitações como, por exemplo, o fato de que elas incluem uma
massa de dados referentes aos fluxos de exceção muito grande a ser analisada, no
caso de sistemas de grande porte, e podem retornar muitos falsos positivos, o que
exige um trabalho manual adicional.
Outras abordagens, como por exemplo, as de Cacho et al. [Cacho et al. 2008]
e Silva e Castor [Silva and Castor 2013] estendem as linguagens de programação
Java e AspectJ para permitir que os desenvolvedores especifiquem os fluxos de
exceções através da descrição de canais de exceção e verifiquem estaticamente
esses fluxos. Essas abordagens compartilham das mesmas limitações das
abordagens de análise estática visto que as exceções não são exercitadas em tempo
de execução.
Também foi observado que os casos de teste JUnit puros não conseguem
detectar problemas como por exemplo o bug “ladrão de exceções” e o tratamento
indevido de exceções durante o seu fluxo no sistema.
As abordagens baseadas em documentação “pesada” como as de Rubira et al.
[Rubira et al 2005] e Kienzle [Kienzle 2008] vão de encontro à ideia de evitar uma
sobrecarga nas atividades de desenvolvimento e ainda não permitem a verificação
automática dos fluxos de exceção.
Já no que diz respeito às regras de design, abordagens como a Desig Wizard
[Brunet, Neto and Figueredo 2009], DCL [Terra and Valente 2008] e a LSD [Neto
2010] permitem a definição e verificação automática de regras de design em sistemas
21
orientados a objetos e orientados a aspectos. Porém elas não possuem construções
específicas para tratamento de exceções, ou as construções são por demais
limitadas, para detectar as inconformidades alistadas na seção anterior.
1.3 Solução proposta
A solução proposta neste trabalho oferece uma abordagem baseada na
definição de regras de design específicas para o comportamento excepcional em
conjunto com a verificação dessas regras através de testes automáticos baseados no
framework JUnit. Para isso é proposta uma sequência de atividades para a realização
da abordagem, que são suportadas por uma ferramenta que é resultado desta
dissertação, a VITTAE (Verification and Information Tool to Analyze Exceptions).
1.4 Objetivos
A dissertação tem como objetivo principal propor uma abordagem sistemática
para a definição e checagem de regras de design voltadas para fluxos excepcionais.
Além disso, o trabalho apresenta os seguintes objetivos específicos:
Propor uma linguagem para definição das regras de design voltadas para
fluxos excepcionais;
Propor uma ferramenta que permita realizar a verificação automática das
regras através de testes automatizados;
Aplicar e avaliar a abordagem proposta através da sua aplicação em um
sistema com várias versões;
Comparar sistematicamente através de um experimento controlado a
abordagem proposta nesta dissertação com a JUnitE [Di Bernardo et al. 2011]
também voltada para teste de fluxo excepcional.
22
1.5 Contribuições escritas ao longo da pesquisa
Parte dos resultados obtidos neste trabalho foram publicados nos seguintes
artigos, que estão no Apêndice A:
SALES JUNIOR, R. ; COELHO, R. S. Preserving the Exception Handling
Design Rules in Software Product Line Context: A Practical Approach.
In: I Workshop on Exception Handling in Contemporary Software
Systems (EHCOS 2011), 2011. Proceedings of I Workshop on Exception
Handling in Contemporary Software Systems (EHCOS 2011),, 2011
SALES JUNIOR, R.; COELHO, R. S. ; LUSTOSA NETO, V. .ExceptionAware AO Refactoring. In: IV Latin American Workshop on Aspect
Oriented Programming, 2010, Salvador. Anais do CBSoft, 2010.
Di Bernardo, R., Sales Júnior, R., Castor, F. Coelho, R., Cacho, N.,
Soares S. Agile Testing of Exceptional Behavior . 25th Brazilian
Symposium on Software Engineering (SBES 2011). São Paulo,
September 28-30, 2011 DOI: 10.1109/SBES.2011.28.
1.6 Organização do texto
Além deste capitulo introdutório, esta dissertação apresenta mais 7 capítulos,
organizados como descrito a seguir: o capítulo 2 apresenta a fundamentação teórica,
apresentando os conceitos essenciais relacionados com o teste de comportamento
excepcional e as regras de design; o capítulo 3 apresenta o núcleo da dissertação,
descrevendo a abordagem proposta; o capítulo 4 detalha a ferramenta desenvolvida
neste trabalho para suportar as atividades da abordagem; o capítulo 5 apresenta um
estudo de caso realizado através da aplicação da ferramenta em diversas versões de
uma linha de produto de software; o capítulo 6 apresenta o experimento controlado
que foi realizado para comparar a abordagem apresentada neste trabalho com a
JUnitE [Di Bernarndo et al. 2011], outra abordagem voltada para teste de fluxo
excepcional; o capítulo 7 apresenta os trabalhos relacionados com a pesquisa
realizada nesta dissertação; por fim o capítulo 8 conclui a dissertação e sugere
trabalhos futuros; o Apêndice A apresenta os artigos cujos resultados foram fruto da
pesquisa realizada durante a elaboração desta dissertação.
23
24
2 Fundamentação Teórica
O objetivo deste capítulo é apresentar uma fundamentação dos principais
conceitos necessários para o entendimento do restante da dissertação. Inicialmente
será apresentado o conceito de tolerância a falhas (seção 2.1). Outra parte
importante, a verificação de software, bem como suas vertentes – testes e análise
estática – receberão destaque na seção 2.2. Também será feita uma introdução breve
a ideia de automação de testes (seção 2.3) e será apresentada a ferramenta de testes
unitários para a Linguagem Java, o JUnit. Na seção seção 2.4 o paradigma de
Programação Orientada a Aspectos será apresentado. Por fim, o conceito de Regras
de Design será abordado na seção 2.5.
2.1 Tolerância a falhas
O termo tolerância a falhas [Avizienis 1998] tem sido utilizado pela comunidade
acadêmica para designar a propriedade que permite que os sistemas, em geral
computacionais, continuem a operar adequadamente mesmo com a presença de
falhas. Porém, para entender melhor este termo é necessário entender os conceitos
básicos de falha, falta e erro. Os conceitos a seguir são baseados nos trabalhos de
Laprie [Laprie 1985] e Anderson e Lee [Anderson and Lee 1981].
Uma falha (do inglês failure) num sistema de hardware ou de software ocorre
quando o serviço prestado pelo sistema desvia-se do comportamento especificado.
Por exemplo, quando num determinado estado do programa o resultado produzido
por uma determinada entrada não corresponde ao esperado, geralmente uma falha é
manifestada para o usuário, seja ele um ser humano seja ele outro sistema. Uma
falha é ocasionada por uma falta (do inglês fault). A falta é um acontecimento que
altera o padrão normal de funcionamento de um dado componente de sistema, por
exemplo, uma queda na rede ou um trecho de código escrito de forma incorreta. O
erro, já é o estado intermediário de instabilidade proveniente da falta e que pode ou
não resultar em uma falha.
25
Assim, o objetivo das técnicas de tolerância a falhas é evitar que erros
acarretem falhas. Nesse contexto, exceções podem ser utilizadas para indicar um erro
ou uma condição anormal do sistema, e mecanismos de tratamento de exceções
podem ser empregados para prover tolerância a falhas.
2.1.1 Tratamento de exceções
De acordo com o clássico “The C++ Programming Language” [Stroustrup et al
2001] uma exceção é um mecanismo que permite a uma parte de um programa
informar a outra parte de um programa que uma situação fora do normal foi detectada.
Em linguagens modernas de programação uma exceção modela uma condição de
erro de tal forma que ela possa ser tratada. Desta forma, tratamento de exceções é a
capacidade que um software tem para reagir apropriadamente diante da ocorrência
de uma exceção, continuando ou interrompendo sua execução, a fim de preservar a
integridade do sistema [Cristian 1982]. Visto que este trabalho é focado em sistemas
desenvolvidos utilizando-se as linguagens Java e AspectJ, a subseção a seguir
apresenta como funciona o tratamento de exceções nestas linguagens.
2.1.2
Tratamento de exceções em Java
Em Java as exceções são objetos de tipos herdados de uma classe especial
chamada Throwable. Na hierarquia de Classes Java essa classe se divide em duas
Error e Exception. A classe Error representa os erros internos da linguagem
Java ou erros da máquina virtual. A classe Exception representa os erros de
programação. As exceções podem ser de dois tipos: checadas e não checadas. As
checadas devem ser explicitamente tratadas ou propagadas no programa. As não
checadas não são obrigadas a serem lançadas ou tratadas. Alguns exemplos de
exceções não checadas são as exceções devido a acesso a objetos nulos
(NullPointerException),
exceções
devido
a
divisão
por
zero
(ArithmeticException) dentre outros casos. O tratamento de exceções em Java
é apresentado no trecho de código a seguir. Um conjunto de instruções é colocado
dentro de um bloco protegido (circundado pela palavra reservada try e um conjunto
de abre e fecha chaves). Se dentro desse trecho de código uma exceção for lançada,
o programa é interrompido na linha de código que gerou a exceção e tem o seu fluxo
26
transferido para o trecho de código que fica circundado pela palavra reservada catch
e um conjunto de abre e fecha chaves associado. Blocos try-catch podem ser
aninhados sem restrição. Neste caso específico no momento que o sistema tentar
realizar a divisão por zero o fluxo do sistema é transferido para o bloco catch onde
será exibida uma mensagem de erro.
1.
2.
3.
4.
5.
6.
try{
divisao = divisao / 0;
}
catch (Exception e){
System.out.println(“Erro! Divisão por Zero!”)
}
Uma exceção pode ser lançada ou relançada através do comando throw. A
palavra reservada throws na assinatura do método define quais as Exceções
checadas que podem ser lançadas pelo método. Caso uma exceção não seja
capturada em nenhum ponto da hierarquia de chamadas ela pode ser propagada até
o ponto de entrada do programa fazendo com que a execução seja interrompida de
forma inesperada.
2.2 Verificação de Software
De acordo com o IEEE [IEEE 1983], a verificação de software permite constatar
se o produto está sendo construído corretamente de acordo com sua especificação.
O processo de verificação de software deve ser aplicado em cada estágio do
desenvolvimento e tem basicamente o objetivo de descobrir problemas em um
sistema. A verificação de software não permite garantir que o software está
completamente livre de erros, mas sim que ele é bom o suficiente para o uso
pretendido. A verificação de software pode ser estática, como por exemplo, através
de análise estática de código, inspeção e verificação formal, e dinâmica, como é o
caso dos testes de software. Os tipos de verificação tratados nesta dissertação são a
análise estática e os testes de software.
27
2.2.1
Análise estática
A análise estática consiste na análise de representações estáticas do sistema
com o objetivo de descobrir problemas sem executá-lo. Chess e West [Chess and
West 2007] afirmam que a análise estática pode ser utilizada com diferentes objetivos,
dentre eles a verificação de tipos, verificação de estilo de escrita e verificação do
programa, ou seja, a análise verifica se o programa está de acordo com os requisitos.
Nesta dissertação estamos interessados principalmente em encontrar inadequações
deste último tipo.
A análise estática traz algumas vantagens, dentre elas: (i) para realizar a
análise estática não é necessário que o código seja executado, o que permite que ela
possa ser realizada até nas fases mais iniciais de desenvolvimento; (ii) por examinar
o código a análise estática geralmente permite identificar a causa dos problemas, e
não somente os sintomas; (iii) pode ser realizada tanto de forma manual como
automática e sobre o código-fonte ou sobre o código-objeto. Pelos motivos acima
citados podemos dizer que a análise estática é uma ótima complementação ao teste
de software.
Porém, a análise estática tem algumas desvantagens. Por exemplo, a análise
estática automatizada é um problema computacional indecidível no pior caso, visto
que este é um caso clássico de um programa que analisa outro [Sipser 2005], ou seja,
resolvê-lo é equivalente a resolver o problema da parada [Turing 1936]. Além disso,
toda a análise estática produz algum falso positivo ou algum falso negativo, ou os dois
[Chess and West 2007]. Ambos são indesejáveis, visto que os falsos positivos podem
induzir o desenvolvedor a gastar tempo na resolução de um problema que não existe,
e os falsos negativos escondem problemas que realmente existem. Além disso, em
sistemas de grande porte, a massa de dados gerada pela análise estática e que deve
ser analisada pode tornar inviável esse tipo de verificação.
2.2.2
Testes de Software
O Teste de Software é um processo de verificação que consiste em executar
um sistema com o objetivo de encontrar defeitos [Myers 1979]. Nos testes são
selecionados dados de entrada, é configurado o ambiente para execução do
programa, ele é executado e então é feita uma análise dos resultados obtidos.
28
No que diz respeito às estratégias de teste elas podem ser classificadas de
acordo com a informação usada como base para o design dos testes. Segundo
Meyers [Meyers 1979] os testes podem ser divididos em testes funcionais ou testes
estruturais, que são apresentados a seguir:
Testes funcionais (ou testes caixa-preta)
De acordo com Meyers [Meyers 1979], nos testes funcionais o programa é
visto como uma caixa-preta, ou seja, não considera-se o comportamento interno do
mesmo. Como ilustra a Figura 2 dados de entrada são fornecidos, o teste é executado
e o resultado obtido é comparado ao resultado esperado de acordo com os requisitos.
O teste passa caso o resultado obtido for igual ao esperado. Caso contrário, o teste
falha. Entre alguns exemplos de critérios de teste para testes funcionais listados por
Meyers estão: particionamento em classes de equivalência; análise do valor limite e
grafo de causa-efeito. Os testes JUnit (apresentado na seção 2.3.1) são exemplos
típicos de testes funcionais.
Figura 2: Técnica de Teste Funcional
Testes estruturais (ou testes caixa-branca)
Também de acordo com Meyers [Meyers 1979], diferentemente dos testes
funcionais, os testes estruturais são baseados no comportamento interno do sistema.
Como ilustra a figura Figura 3, essa técnica trabalha diretamente em cima do códigofonte do componente de software que está sendo testado. São exemplos de testes
estruturais os testes de condição, testes de fluxo de dados, testes de ciclo e testes de
caminhos lógicos.
29
Figura 3:Técnica de teste estrutural
Visto que este trabalho foca na realização de testes JUnit que envolvem o
fluxo excepcional, para projetar tais testes é necessário conhecer a estrutura do
programa, e esses testes portanto se caracterizam como caixa-branca. A seguir é
apresentado o conceito de níveis de teste.
2.2.3 Níveis de teste
A atividade de testes é realizada em diferentes níveis ou estágios do
desenvolvimento e pode envolver o sistema inteiro ou parte dele durante o andamento
de seu desenvolvimento. Como o modelo V [Craig and Jaskiel 2002] da Figura 4 esses
estágios dependem da fase de desenvolvimento em que os testes podem ser
aplicados.
Figura 4: Modelo V adaptado de Craig e Jaskiel [Craig and Jaskiel, 2002] que descreve a
correspondência entre as atividades de desenvolvimento e os níveis de teste
30
.
Crespo et al. [Crespo et al. 2004] caracteriza tais níveis da seguinte forma:
Testes de Unidade: também conhecidos como testes unitários, tem por objetivo
explorar a menor unidade do projeto, procurando provocar falhas ocasionadas
por defeitos de lógica e de implementação em cada componente de software,
separadamente. O universo alvo desse tipo de teste são os métodos, classes
ou pequenos trechos de código.
Testes de Integração: visam provocar falhas associadas às interfaces entre os
módulos quando esses são integrados para construir a estrutura do software
que foi estabelecida na fase de projeto.
Teste de Sistema: avalia o software em busca de falhas por meio da utilização
do mesmo, simulando a utilização por um usuário final. Dessa maneira, os
testes são executados nos mesmos ambientes, com as mesmas condições e
com os mesmos dados de entrada que um usuário utilizaria no seu dia-a-dia
de manipulação do software. Verifica se o produto satisfaz seus requisitos.
Teste de Aceitação: são realizados geralmente por um restrito grupo de
usuários finais do sistema. Esses simulam operações de rotina do sistema de
modo a verificar se seu comportamento está de acordo com o solicitado.
Existem ainda os testes de regressão que não são considerados como um
nível visto que podem ser executados durante todo o desenvolvimento. Eles são
executados quando um componente é modificado e deseja-se verificar se outras
funcionalidades não foram quebradas pela modificação.
2.3 Automação de testes
Embora o objetivo do processo de testes seja encontrar falhas em uma
aplicação de software, a sua realização de forma manual frequentemente consome
muito tempo, exige um trabalho intensivo, a comparação dos resultados de teste é
tediosa e propensa a erros. A automação de testes pode reduzir de maneira
significativa o esforço necessário para realizar este processo, ou aumentar a
quantidade de testes que podem ser realizados dentro de um tempo limitado [Fewster
and Graham 1999].
31
Com o objetivo de automatizar parte deste processo, algumas ferramentas de
software vem sendo propostas. O trabalho de Meudec [Meudec 2001] divide em três
principais categorias os softwares de automação de testes: (i) softwares para tarefas
administrativas de testes – espeficicação dos testes e geração de relatórios de testes;
(ii) softwares para tarefas mecânicas de testse – execução e monitoramento de testes,
captura e execução de testes automatizados; (iii) softwares para geração de testes.
Dentre as ferramentas da segunda categoria, que são o foco desta dissertação,
podemos destacar a ferramenta JUnit.
2.3.1
JUnit
O JUnit [JUnit 2012] foi criado como um framework para escrever testes de
unidade automatizados em Java. Esse framework facilita a criação de código para
automação de testes com apresentação de resultados. Com ele pode ser verificado
se cada método de uma classe funciona da forma esperada, exibindo possíveis erros
ou falhas.
Figura 5: Arquitetura do JUnit (Fonte: http://www.devmedia.com.br/junit-implementandotestes-unitarios-em-java-parte-i/1432)
32
Como pode ser observado na Figura 5, no JUnit um teste de unidade
corresponde a uma classe que estende a classe TestCase. Essa classe possui os
seguintes métodos:
run(): Cria um contexto (método setUp), em seguida executa o código e
verifica o resultado (método runTest), limpando o contexto ao final (método
tearDown);
setUp(): Método chamado antes de cada método de teste;
runTest(): Método responsável por controlar a execução do teste em si;
tearDown(): Método chamado após cada método de teste, devendo ser
utilizado para desfazer as operações realizadas no método setUp;
Para entendermos melhor como funciona o JUnit apresentamos a seguir um
trecho de código de uma classe de um sistema de gerenciamento de contas bancárias
chamada Conta.
1. package br.ufrn.banco;
2.
3. //imports…
4.
5. public class Conta{
6.
7.
private double saldo;
8.
9.
//gets e sets
10.
11.
public void creditar(double valor){
12.
saldo += valor;
13.
Logger.log(“Movimentação na conta em ” +
14.
dataAtual);
15.
}
16.
17.
...
18.
19.
public void debitar(double valor){…}
20.
21.
...
23.
24.
public void realizarEmprestimo(doubleValor valor){…}
25.
26. }
33
O trecho de código a seguir exemplifica um teste simples em JUnit (Classe
SomaTeste) para os métodos da classe Conta. Os métodos desta classe
correspondem aos métodos de teste, sendo que na versão 3 do JUnit cada método
de teste precisa ter seu nome iniciado com a palavra “test”. Neste exemplo, depois de
instanciar uma conta e definir o saldo com um determinado valor é realizada a
operação que se deseja testar, neste caso a operação creditar. Após a execução
do método, deseja-se verificar se ele foi executado corretamente. Então é feita uma
asserção, ou uma verificação de condição, e para isso o JUnit disponibiliza métodos
que realizam estas verificações. Neste caso foi utilizada a asserção assertEquals
que avisa ao framework que ocorreu uma falha caso os dois parâmetros não sejam
os mesmos.
1. //Exemplo de teste no JUnit 3
2. package br.ufrn.testes;
3.
4. import br.ufrn.banco.Conta;
5. import junit.framework.TestCase;
6.
7. public class SomaTest extends TestCase{
8.
public void testSoma(){
9.
Conta conta = new Conta();
10.
conta.setSaldo(100.0);
11.
conta.creditar(50.0);
12.
assertEquals(150.0,conta.getSaldo());
13.
}
14.}
15. //Agora o mesmo teste no JUnit 4
16. package br.ufrn.testes;
17.
18. import br.ufrn.banco.Conta;
19.
20. public class SomaTest{
21.
@Test
22.
public void testSoma(){
34
23.
Conta conta = new Conta();
24.
conta.setSaldo(100.0);
25.
conta.creditar(50.0);
26.
assertEquals(150.0,conta.getSaldo());
27.
}
28.}
Como pode ser observado no trecho código acima, na versão 4 do framework
para criar um teste não é mais necessário estender a classe TestCase. Além disso,
para identificar um método como sendo de teste não é necessário mais que o método
inicie com a palavra “test”, bastando incluir a anotação @Test para identificá-lo.
Também podemos observar na Figura 5, que existe uma classe chamada
TestSuite. Essa classe permite que sejam executados vários testes, adicionandoos através do método addTest().
Para exibir os resultados, o Junit apresenta visualmente barras que
identificam se o teste passou, conforme mostra a Figura 6. A barra verde identifica
que o teste passou. A barra vermelha identifica que houve um erro durante a
execução do teste.
Figura 6: Resultados visuais do JUnit
Esta dissertação apresenta uma extensão do framework JUnit para testes de
fluxos excepcionais.
35
2.4 Programação Orientada a Aspectos e AspectJ
A Programação orientada a Aspectos é uma metodologia utilizada em
conjunto com os paradigmas de programação orientado a objetos e procedural, que
visa incrementá-los com conceitos e construções que permitam modularizar conceitos
transversais do sistema [Laddad 2003].
A programação Orientada a Aspectos permite aos desenvolvedores e
projetistas de software separarem e organizarem o código de interesse comum a
várias partes de um sistema (como por exemplo, conexão com banco de dados,
logging, tratamento de exceções) encapsuladas em unidades de programação
chamadas aspectos. A esses interesses comuns chamamos “crosscutting concerns”
(ou interesses transversais).
Tomemos como exemplo o caso de logging de uma aplicação, como
mostrado no código abaixo na implementação de orientação a aspectos utilizando a
linguagem AspectJ [AspectJ 2012].
Em basicamente toda a operação que se deseja registrar seria necessário
incluir manualmente código para armazenamento do logging, código este que não
teria nenhuma relação com o objetivo da classe em que ele estaria sendo inserido. A
este tipo de código chamamos de código entrelaçado. Agora imaginemos o quanto
isso dificultaria a manutenção e evolução do sistema se houvesse uma quantidade
grande de classes em que seria necessário fazer logging. A orientação a aspectos
resolve esse problema e complementa a orientação a objetos com a criação de um
aspecto logging (exemplificado no código AspectJ abaixo) que encapsula a operação
de logging e é ativado de acordo com uma regra indicada no aspecto (neste caso,
todas as operações na classe conta bancária).
1. package br.ufrn.aspects;
2.
3. imports…
4.
5. public aspect Logging{
6.
7.
pointcut operacaoBancaria(): execution(
8.
*br.ufrn.banco.Conta.*(..));
9.
10.
after() : operacaoBancaria(){
36
11.
12.
13.}
Logger.log(“Movimentação na conta em ” + dataAtual);
}
Vamos analisar melhor este código. O primeiro elemento que aparece é o
pointcut. O pointcut é uma regra que define onde o aspecto deverá ser ativado. No
nosso exemplo criamos um pointcut chamado operacaoBancaria que é ativado
toda vez que um método com quaisquer tipos de parâmetros da classe Conta é
executado.
O segundo elemento do nosso aspecto é o advice. O advice define que após
a execução do pointcut operacaoBancaria será executado um trecho de código,
neste caso o trecho de Logging. Dessa forma em apenas algumas linhas de código
definimos um conceito transversal, evitando a replicação em diversas partes de
código de logging.
Outro elemento importante do AspectJ é a declaração declare soft. Esta
declaração da linguagem permite “suavizar” uma exceção, o seja, silenciar a exceção
e relançá-la como uma exceção não-checada.
A ferramenta fruto deste trabalho utiliza AspectJ no seu core. Além disso, ela
permite a verificação de regras de fluxo excepcional também em código orientado a
aspectos usando AspectJ. A seguir apresentamos o conceito base para as regras de
fluxo excepcional que servem de dados de entrada para os testes executados
utilizando a ferramenta desta dissertação.
2.5 Regras de Design
Uma atividade comum a todo processo de software é o design do software. O
design incorpora a descrição da estrutura do software, os dados que são manipulados
pelo sistema, a descrição das interfaces entre os componentes do sistema, e algumas
vezes o algoritmo utilizado [Belady 1981]. Ele implementa importantes decisões sobre
a arquitetura do software e possui um papel crucial no desenvolvimento, implantação
e evolução do sistema [Krutchen et al. 2006].
Contudo, nem sempre a implementação reflete o design proposto. Isso acontece
porque frequentemente a documentação que expressa o design é imprecisa e/ou
obsoleta. Esta inconsistência entre o design documentado e a implementação tem
37
sido apontada como uma das principais causas de baixa qualidade de software
[Parnas 1994, van Gurp and Bosh 2002].
Neste contexto, a verificação de conformidade entre o design e a implementação
é uma prática que promove a qualidade do software. Neste sentido as regras de
design (do inglês design rules) [Baldwin and Clark 1999] especificam como as
entidades de software devem ou não se relacionar com as demais. Essas regras
definem contratos que devem ser rigidamente obedecidos em todas as fases
posteriores do ciclo de vida do processo de construção do software. Essa abordagem,
além de promover a modularidade do sistema, favorece o paralelismo de
desenvolvimento. Ela pode ser considerada como uma camada de abstração acima
da camada de implementação.
Nosso trabalho propõe uma forma de descrever regras de design específicas
para o comportamento excepcional da aplicação.
2.6 Discussões
Este capítulo apresentou os conceitos fundamentais que norteiam este trabalho,
como Tolerância a Falhas, Verificação de Software, JUnit, Programação Orientada a
Aspectos e Regras de Design. O próximo capítulo apresentará a abordagem proposta
por este trabalho.
38
3 Abordagem Proposta
O objetivo deste capítulo é apresentar a abordagem de testes de fluxos
excepcionais proposta neste trabalho. Um exemplo motivador é apresentado na
seção 3.1. Logo depois a abordagem é descrita na seção 3.2 com a ajuda de um
exemplo. Por fim, na seção 3.3, é feito um fechamento do capítulo e algumas
discussões são realizadas.
3.1 Exemplo Motivador – Refatoração de Sistemas Orientados a
Objetos para Aspectos
Na seção 1.1 apresentamos um exemplo motivacional que mostrou algumas
limitações da utilização de casos de teste JUnit puros para detectar problemas de
comportamento excepcional. Nesta seção, apresentamos um exemplo adicional que
demonstra ainda outros problemas no tratamento de exceções que podem ocorrer
quando um sistema computacional é refatorado para utilizar um mecanismo de
orientação a aspectos.
Quando um sistema orientado a objetos é refatorado para utilizar orientação a
aspectos uma questão surge: o conceito transversal a ser encapsulado em um
aspecto pode lançar alguma exceção? Se a resposta for positiva, o time de
desenvolvimento precisa lidar com duas tarefas igualmente importantes: (i) permitir
que o aspecto criado lance a exceção; (ii) pôr em prática a regra de design voltada
para fluxos excepcionais descrita no trabalho de Coelho et al. como solução para bug
pattern “Handlerless Signaler Aspect” [Coelho et al. 2008b] que consiste em criar um
aspecto tratador de erro que será responsável por tratar as exceções lançadas pelo
aspecto que encapsula o conceito transversal. Se a exceção lançada pelo aspecto
criado é checada, devido a uma limitação da linguagem AspectJ, é necessário
convertê-la em uma outra exceção não checada, através da utilização da construção
declare soft do AspectJ, para que ela possa ser lançada. Este tipo de exceção
também deve ser tratada pelo aspecto tratador de erro.
Entretanto, algumas características do tratamento de exceção que é realizado
no código base (ou seja, o código orientado a objetos), podem impedir que os
39
aspectos de tratamento de erro realizem o tratamento de exceção adequadamente,
levando assim a quebra de uma regra de design da aplicação. Para ilustrar, na Figura
7 é apresentada a arquitetura de um sistema de gerenciamento de contas bancárias
que foi refatorado para utilizar orientação a aspectos.
Figura 7: Arquitetura de um sistema refatorado para Aspectos
Como pode ser observado este sistema utiliza o padrão de arquitetura em
camadas e implementa os seguintes interesses transversais:
PerformanceMonitoring:
É
o
responsável
por
monitorar
a
performance de cada requisição à servlet.
NegativeValueOnOpCheck: Verifica quando a transferência foi
realizada com um valor negativo.
NegativeValueOnOpHandler: Responsável por tratar as instâncias
da exceção não checada NegativeValueOnOpException. Lançadas
pelo aspecto NegativeValueOnOpCheck.
No sistema, nenhum aspecto tratador de erro foi definido para o aspecto
PerformanceMonitoring porque cada exceção lançada por este monitor pode ser
tratada dentro do próprio aspecto. Em outras palavras, as exceções lançadas durante
o monitoramento não devem causar distúrbios aos fluxos excepcionais do sistema
que está sendo monitorado.
O trecho de código a seguir pertence ao aspecto NegativeValueOnOpCheck:
40
1. public aspect NegativeValueOnOpCheck {
2.
3.
//intercepta retiradas, créditos e transferências
4.
pointcut valueReceiversOps = ...;
5.
6.
before (double value) :
7.
execution(valueReceiversOps) && args(value)
8.
{
9.
if (value < 0){
10.
throw new NegativeValueOnOpException();
11.
}
12.
}
13.}
Percebemos que nesta aplicação, uma regra de design de fluxo excepcional é
que todas as instâncias da exceção não checada NegativeValueOnOpException
lançadas pelo aspecto NegativeValueOnOpCheck devem ser tratadas pelo
aspecto tratador de erro NegativeValueOnOpHandler, cujo código é exibido a
seguir.
1. public aspect NegativeValueOnOpHandler {
2.
...
3.
pointcut valueReceiversOps=...;
4.
void around () : call (valueReceiversOps){
5.
try{
6.
proceed();
7.
}catch(NegativeValueOnOpException nvoe){
8.
//treating exception
9.
}
10.
}
11.}
Uma quebra da regra de design de fluxo de tratamento de exceção que pode
surgir durante o refatoramento é conhecida como “exception stealer” ou “ladrão de
exceções” [Coelho et al. 2008c], e foi abordado na seção 1.1. Ela ocorre quando o
aspecto de tratamento de erro não consegue tratar a exceção lançada por um aspecto
que encapsula um interesse transversal porque um elemento do código base
41
prematuramente captura a exceção. No exemplo do sistema de gerenciamento de
contas bancárias o aspecto NegativeValueOnOpHandler foi criado para tratar
qualquer instância de NegativeValueOnOpException lançada pelo aspecto
NegativeValueOnOpCheck. Entretanto, uma cláusula catch-all definida no código
base irá tratar qualquer instância NegativeValueOnOpException antes de
NegativeValueOnOpHandler entrar em ação, como ilustra a Figura 8.
Figura 8: Visão esquemática do "ladrão de exceções"
O trecho de código a seguir ilustra a classe Bank que contém a cláusua catchall (linha 6), impedindo o aspecto NegativeValueOnOpHandler de tratar as
instâncias de NegativeValueOnOpException.
1. public class Bank{
2. ...
3.
4.
5.
6.
public void credit(Account c, double val){
try{
//method body
}catch (Exception e){
42
7.
//handles the exception
8.
}
9.
}
10.
public void debit (Account c, double val) throws
11.
NegativeBalanceException{
12.
double aux = c.getBalance();
13.
aux = aux – val;
14.
if(aux<0) throw new NegativeBalanceException();
15.
else c.setBalance(aux);
16.
}
17. }
Este problema pode ocorrer de forma similar também em sistemas orientados
a objetos. Uma maneira de prever este problema seria substituir todas as cláusulas
catch-all por cláusulas de tratamento específicas de acordo com o tipo da exceção.
Mas como já explicado na seção 1.1, essa solução ainda é passível de problemas.
Observa-se então que tanto no caso de sistemas orientados a objetos, como
sistemas que utilizam orientação a aspectos seria bastante útil a existência de uma
abordagem que permitisse a especificação das regras de design do comportamento
excepcional da aplicação para que depois elas sejam verificadas ao longo de todo o
desenvolvimento do sistema, e posteriores manutenções, evitando assim o problema
de quebra da regra de design explanada nesta seção.
Esta dissertação visa propor uma abordagem que permite a especificação e
verificação das regras de design da aplicação, tanto em nível de métodos, quanto de
classes e pacotes de software. Essa abordagem começa a ser apresentada a partir
da próxima seção.
3.2 Visão Geral da Abordagem Proposta
Nesta seção apresentamos a abordagem proposta em linhas gerais. Ela tem
como objetivo permitir a definição e verificação de regras de design para tratamento
de exceções. A Figura 9 ilustra os passos da nossa abordagem.
43
Figura 9: Sequência de Atividades da Abordagem Proposta
Com o objetivo de auxiliar nas atividades dessa abordagem, foi elaborada a
ferramenta VITTAE [Sales Júnior and Coelho 2011] que estende o framework JUnit e
apoia as atividades de projeto e automação dos testes, execução e avaliação.
A estrutura da ferramenta será explanada no próximo capítulo. Porém, seu
funcionamento será explicado à medida que os passos da abordagem forem
explicados a partir das próximas subseções.
3.2.1 Passo1: Definição das Regras de Design Excepcionais da Aplicação
A tarefa de definir as regras de design excepcionais é essencial, visto que estas
regras é que deverão ser obedecidas durante o desenvolvimento e manutenção do
software. Além disso, as regras de design são muito úteis como uma forma de
documentação complementar do sistema. A descoberta das regras de design foge do
escopo desta dissertação. Entretanto, podemos indicar algumas fontes de
informações podem ser utilizadas para realizar esta tarefa. Para tanto podemos
vislumbrar duas situações possíveis:
a) As regras de design são estabelecidas antes do início do
desenvolvimento: Quando isso acontece as regras podem ser extraídas de
documentos e modelos que definem a arquitetura, do conhecimento do
arquiteto de software sobre como o sistema irá funcionar, das próprias
especificações de requisitos do sistema e também de anti-padrões de
tratamento de exceções [McCune 2006] e de padrões de bugs [Allen 2001].
Um exemplo que ilustra como seria o estabelecimento dessas regras de
design previamente seria o caso de um sistema onde é definido que será
utilizado a orientação a Aspectos. Como explicado na seção 3.1, o padrão
de bug Handlerless Signaler Aspect ocorre quando um aspecto lança uma
44
exceção, mas nenhum handler é definido para trata-la. Para evitar este
problema, pode-se previamente definir uma regra de design excepcional que
exija que uma exceção lançada por um aspecto seja capturada por outro
determinado aspecto (Error Handling Aspect).
b) As regras de design são estabelecidas ou evoluídas após o início
do desenvolvimento: Nestes casos a variedade de fontes é maior. Além
das fontes citadas anteriormente podemos nos valer de uma análise do
código-fonte e dos relatórios de bugs e logs de exceção do sistema.
Para entender melhor a estrutura do XML Schema a exibe o XML Schema do
arquivo de regras de design.
Figura 10: XML Schema para o arquivo que define as regras de design da aplicação
Em nossa abordagem as regras de design são definidas em um arquivo XML. Nossa
escolha por essa linguagem se deveu ao fato de que XML consegue expressar bem
os conceitos representados na ferramenta, é bastante utilizada por programadores
45
Java em outras ferramentas e, além disso, possui diversas bibliotecas estáveis e
simples em diversas linguagens de programação.
Podemos observar que o XML inicia com um elemento raiz chamado contract.
A partir daí podemos definir diversos elementos signaler. Este elemento representa
um método (ou conjunto de métodos através do uso de um wildcards com o uso do
caractere ‘*’ semelhante aos utilizado nos pointcuts do AspectJ) responsável por
lançar, através da criação ou propagação, um ou mais tipos de exceção, que é
definido através do atributo signature. Para cada signaler podemos ter diversos
elementos do tipo exception que representam as exceções lançadas por este
método, descritas no atributo type. Para cada elemento exception podemos ter
um ou mais elementos handler. Esse elemento representa o método (ou conjunto
de métodos utilizando o mesmo wildcard descrito para o elemento signaler)
responsável por capturar a exceção, definido através do atributo signature.
Para ilustrar como esse XML é preenchido vamos tomar como base o código do
método debit da classe Bank exibida na seção 3.1. Este método pode lançar uma
exceção se o saldo da conta ficar negativo com a operação de débito. Para este
método podemos formular a seguinte regra:
<contract>
<signaler signature=“Bank.debit(Account,double)”>
<exception type=“NegativeBalanceException”>
<handler signature=“ServletClient.handlerNegativeBalance()”/>
</exception>
</signaler>
</contract>
Neste exemplo analisado, a especificação é que cada instância da exceção
NegativeBalanceException lançada pela operação debit com valores do tipo
Account
e double da camada Facade deve ser tratada pelo método
handlerNegativeBalance() da classe ServletClient.
Em um projeto que utiliza nossa abordagem, os contratos ficam armazenados
em um arquivo chamado contract.xml que deve ficar dentro de uma pasta
chamada contracts na raiz do projeto, conforme mostra a Figura 11.
46
Figura 11: Estrutura do projeto com a VITTAE e localização do arquivo de contratos
3.2.2 Passo 2 : Projeto e Automação do Teste
Após a definição das regras de design excepcionais é necessário que estas
sejam verificadas. Para isso é preciso estimular tanto o código que lança a exceção
quanto o código que trata a exceção. Para estimular o código que trata a exceção e
automatizar este teste, deve ser criado um caso de teste JUnit que chama o método
responsável por tratar uma determinada exceção do contrato. Através da VITTAE
nossa abordagem provê um suporte ferramental para criação de um esqueleto do
teste, destacando a chamada do método que trata a exceção. Para fazer isso é
necessário
chamar
o
método
chamado
generateTests()
da
classe
br.ufrn.gits.fear.generators.ExceptionContractTestGenerator. No
código abaixo é apresentado um exemplo de esqueleto de teste gerado pela
ferramenta, baseado no contrato apresentado na seção anterior para a o método
debit da classe Bank.
1. //declare package
2.
3. //imports
4.
5. ...
6. public class NegativeBalanceExceptionBankOperationEHTest extends
7. TestCase{
8.
public void test01 (){
9.
47
10.
ServletClient handler = new ServerClient();
11.
12.
13.
handler.handlerNegativeBalance();
}
14.}
Como podemos observar somente o esqueleto gerado ainda não é suficiente
para realizar o teste. A complementação do caso de teste e a garantia de que o
método que trata a exceção seja chamado corretamente ficam a cargo do testador.
Além disso, faz-se necessário garantir que durante a realização do teste,
existam condições para que a exceção esperada definida na regra de design seja
lançada. No caso do nosso exemplo acima, seria necessário durante o teste instanciar
os objetos de tal forma que quando o método handlerNegativeBalance seja
chamado, o fluxo alcance o método debit da classe Bank. Além disso, o método
debit precisa ser chamado com parâmetros que o levem a lançar a exceção
NegativeBalanceException (por exemplo com o parâmetro value com valor
negativo). Com esse objetivo de auxiliar nessa tarefa aconselha-se o uso de objetos
mock [Freeman and Craig 2001]. Os objetos mock são um padrão de design para
testes unitários onde um objeto mock simula o propósito de um objeto real sem a
necessidade de instanciar o objeto real. Em nossa abordagem, a ferramenta VITTAE
gera juntamente com as classes de Teste, mocks para simular o lançamento da
exceção quando o método signaler for chamado com qualquer parâmetro. Em termos
simples, o mock gerado é um aspecto que, independente da situação, quando o
método signaler é chamado dentro do teste JUnit lança automaticamente a exceção
definida na regra. O código a seguir apresenta um exemplo de aspecto mock para
simular o lançamento da exceção pelo signaler.
1. package br.ufrn.gits.tests;
2.
3. //imports
4. @Aspect
5. public class
6. NegativeBalanceExceptionBankOperationEHMockTestAspect {
7.
8.
@Around("execution (*Bank.debit(Account,double)) &&
48
9.
cflow(execution(*
10.
NegativeBalanceExceptionBankOperationEHTest.*(..)))")
11.
12.
public Object
13.
launchNegativeBalanceException(ProceedingJoinPoint
14.
thisJoinPoint)
15.
throws NegativeBalanceException
16.
{
17.
throw new NegativeBalanceException();
18.
}
19. }
O aspecto acima, que utiliza a forma de anotações do AspectJ, é executado no
lugar do método signaler, neste caso, Bank.debit, e lança automaticamente a
exceção NegativeBalanceException.
Caso não fosse utilizado este mock, seria necessário garantir que durante a
execução do método para debitar, o método handlerBankOperation chamasse o
método Bank.debit com um valor para o parâmetro value negativo.
3.2.3 Passo 3 : Execução do teste
Após a construção do teste, faz-se necessário realizar a execução automatizada
dos testes. Visto que a abordagem constrói os casos de teste em cima do framework
JUnit e utiliza os mecanismos desse framework para reportar os sucessos e erros,
pode-se utilizar toda a sua estrutura para automatizar essa execução. O JUnit permite
a criação de conjuntos, ou suítes, de testes, e a geração de relatórios de execução,
que são usados para avaliação dos testes. Como já explicado no capítulo anterior o
JUnit reporta visualmente os casos de uso que passaram e que falharam.
3.2.4 Passo 4 : Avaliação do Teste
Após a construção dos testes, o próximo passo é avaliar os resultados da
execução automatizada. O JUnit caracteriza uma falha em um teste através de uma
barra vermelha e um failure trace.
49
Quando ocorre uma quebra da regra de design a VITTAE lança uma exceção do
tipo ContractCheckingException, exibindo qual a regra que foi quebrada e quais
foram os signalers e handlers. Dessa forma é possível analisar os motivos pelos quais
ocorreu a quebra de design.
A Figura 12 a seguir exibe inicialmente como o JUnit puro não consegue fornecer
informações relacionadas ao fluxo do tratamento de exceção, enquanto a Figura 13
exibe um exemplo da atuação da VITTAE para avaliação do resultado durante a
execução de um teste.
Figura 12: Tela do JUnit sem a extensão da VITTAE
Figura 13: Exemplo de utilização do framework JUnit em conjunto com a VITTAE para
execução automatizada dos testes
Para ilustrar como seria a análise tomemos os mesmos contratos e testes que
já foram criados nos passos anteriores, mas agora injetaremos uma quebra da regra
de design com uma pequena modificação na classe Bank como ilustrada no código
a seguir.
50
1. public class Bank{
2. ...
3.
public void credit(Account c, double val){
4.
try{
5.
//method body
6.
}catch (Exception e){
7.
//handles the exception
8.
}
9.
}
10.
public void debit (Account c, double val){
12.
double aux = c.getBalance();
13.
aux = aux – val;
14.
try{
15.
...
16.
if(aux<0) throw new NegativeBalanceException();
15.
else c.setBalance(aux);
16.
}
17.
catch(Exception e){
18.
...
19.
16.
}
}
17. }
Observe que agora dentro do método debit foi inserido um catch-all, o
que
quebraria
a
regra
de
NegativeBalanceException
design,
nunca
visto
que
o
chegará
fluxo
ao
da
exceção
método
handlerNegativeBalance da classe ServletClient.
Caso executássemos o teste de fluxo excepcional o JUnit retornaria um erro (red
bar) com a seguinte mensagem:
51
Error in exception handling contract:
<Signaler: Bank.debit(Account,double)>
<Exception:NegativeBalanceException>
Expected: <Handlers:[ServletClient.handlerNegativeBalance()]>
but was: <Handler:[Bank.debit(Account,double)]>
Podemos verificar que a ferramenta VITTAE identificou que um elemento
intermediário não declarado na regra de design interceptou a exceção, e a tratou
quando não deveria ter feito isso de acordo com a regra de design de fluxo
excepcional da aplicação. Se a mesma exceção for lançada a partir de vários lugares
do sistema, basta incluir uma cláusula signaler nova para a mesma exception no
arquivo contract.xml. Se a mesma exception do mesmo signaler for
capturada em mais de um ponto no sistema, basta incluir uma nova cláusula handler
dentro da mesma cláusula signaler e dentro da mesma cláusula exception da
regra.
Após a avaliação dos testes pode-se observar que determinadas regras não são
mais aplicáveis, ou que sofreram atualizações. Além disso, pode ser que durante o
desenvolvimento novas regras sejam estabelecidas. Nestes casos é necessário
retornar ao passo 1 e redefinir os contratos excepcionais, executando todo ciclo
novamente a cada evolução do sistema.
3.3 Discussões
Neste capítulo apresentamos uma abordagem para especificação e verificação
de regras de design excepcionais em sistemas OO e AO. Foi apresentado um
exemplo que mostra a necessidade da criação de uma abordagem para o problema.
Após isso, foram apresentados os passos necessários para realizar as atividades da
abordagem, que envolve definição das regras de design excepcionais, construção e
automação dos testes, execução dos testes e avaliação. Por fim, demos um exemplo
de caso de teste utilizando a abordagem.
Visto que a confiabilidade é um atributo que deve ser considerado como
atendido durante todas as fases de desenvolvimento de um sistema [Avizienis et al
2004], inclusive na manutenção, podemos listar alguns cenários de uso para essa
52
abordagem. Podemos a partir disso, imaginar a utilização dessa abordagem em dois
cenários: novos sistemas e sistemas já existentes.
Para os novos sistemas, como já mencionado, antes do desenvolvimento
algumas decisões referentes ao tratamento de exceções devem ser tomadas e
documentadas. Utilizando a nossa abordagem, tais decisões ficam registradas nas
regras de design excepcionais. Além disso, durante as fases do ciclo de
desenvolvimento é necessário verificar se tais regras estão sendo cumpridas, para
evitar a degradação da conformidade entre as regras de design excepcionais e a
implementação.
No caso dos sistemas existentes, a abordagem pode ser aplicada com o objetivo
de verificar se a evolução do sistema não quebrou ou evoluiu as regras de design de
fluxo excepcional. Por exemplo, ao aplicar a abordagem e extrair as regras de design
excepcionais de uma primeira versão estável de um sistema, pode-se então aplicar
os testes nas versões posteriores e identificar mudanças nos requisitos relacionados
com o fluxo excepcional, quebras de regras de design definidas no contrato ou
evoluções na arquitetura. Essa identificação promove a correção ou atualização dos
outros artefatos do ciclo de desenvolvimento, como especificações de requisitos e os
documentos e modelos de arquitetura do sistema para as versões posteriores.
Além dos cenários gerais em que a abordagem pode ser utilizada, também é
interessante discutir algumas questões referentes a como a abordagem realiza a
verificação do comportamento excepcional. O capítulo a seguir apresenta a
ferramenta desenvolvida para suportar a abordagem proposta, a VITTAE.
53
4 A Ferramenta VITTAE
O objetivo deste capítulo é apresentar a estrutura da ferramenta que apoia a
abordagem proposta, a VITTAE (Verification and Information Tool to Analyze
Exceptions) [Sales Júnior and Coelho 2011]. Uma visão geral da arquitetura é
apresentada na seção 4.1. As seções seguintes expõem a estrutura de cada
componente da ferramenta.
4.1 Visão Geral da Arquitetura da VITTAE
A ferramenta VITTAE foi projetada para ser implementada de forma modular e
orientada a aspectos, como mostra a Figura 14 na página 55. Ela contém sete
pacotes, treze classes e aproximadamente setecentas linhas de código-fonte.
Basicamente o pacote de aspectos (seção 4.2) atua sobre o sistema alvo e realiza
suas operações de verificação através de um pacote controlador (seção 4.3). Existe
um pacote de exceções específicas utilizadas pela ferramenta (seção 4.4) e um
pacote de classes utilitárias de apoio (seção 4.5). Além destes há um pacote com os
geradores de testes e mocks (seção 4.6) e um pacote com as classes de entidade
que representam os elementos da regra de design e do código alvo (seção 4.7).
4.2 Pacote de aspectos
O pacote de aspectos (aspects) contém um único aspecto chamado
ContractCheckingAspect. Esse aspecto extende o framework Junit, pois
intercepta todas as capturas de exceções (sejam elas subclasses de Throwable,
Exception ou RunTimeException) dentro de métodos de teste. Quando uma
exceção é capturada dentro de um método de teste, esse aspecto chama o
controlador (seção 4.3) para verificar se existe alguma regra para a exceção
capturada e, caso exista, se a regra foi quebrada.
54
Figura 14: Arquitetura da Vittae
55
4.3 Pacote de controladores
O
pacote
de
controladores
contém
uma
classe
chamada
ContractController. O objetivo desta classe é orquestrar todas as verificações
de regras de design realizadas pelo aspecto que verifica os contratos, assim como
outras verificações que possam vir a ser realizadas por outros pacotes em possíveis
extensões da ferramenta. É também responsabilidade deste controlador gerenciar o
acesso às entidades que representam conceitos da regra de design, como
signalers e handlers, por exemplo. Também é este controlador que efetivamente
lança uma exceção específica da ferramenta quando o teste de fluxo excepcional
falha.
4.4 Pacote de exceções
Esse pacote contém as exceções específicas da ferramenta. Estas são:
MethodNotFoundException: Essa exceção é lançada quando um
contrato é lido, mas o método definido como signaler ou handler não foi
encontrado no código, impedindo assim a verificação do contrato.
ContractCheckingException: Essa exceção tem papel chave dentro
da ferramenta. É ela que assinala quando um teste de fluxo excepcional
falhou. Quando isso ocorre, a exceção fornece na sua mensagem a
informação sobre qual exceção foi lançada, qual método definido na regra
de design deveria ter feito o tratamento e qual o método que efetivamente
realizou o tratamento. Caso a exceção não tenha sido tratada, a exceção
também exibe essa informação em sua mensagem. Essa mensagem é
visível quando ocorre um erro no teste JUnit.
4.5 Pacote de classes utilitárias
Esse pacote contém uma classe utilitária que é a classe ContractUtil. Essa
classe é responsável principalmente por fazer a leitura do arquivo xml com os
contratos excepcionais e convertê-los na estrutura de objetos arquitetada na
56
ferramenta. Ela é utilizada tanto por classes do pacote de controladores, quanto pelo
aspecto verificador.
4.6 Pacote de geradores
Esse pacote contém uma classe ExceptionContractTestGenerator que é
responsável por gerar os casos de teste JUnit com base nas informações fornecidas
pela regra de design. Além de gerar os casos de teste, essa classe também gera os
mock aspects (descritos na seção 3.2.2) para simular o lançamento das exceções
pelos signalers.
4.7 Pacote de entidades
Esse pacote abriga as classes de entidade que representam os conceitos da
ferramenta. A seguir descrevemos cada uma dessas classes:
Contract: Representa o contrato que descreve as regras de design;
possui uma lista de itens de contrato (ContractItem) ;
ContractItem: Representa um item do contrato. Um item do contrato é
formado por um Signaler e uma lista de ExceptionTargets
Signaler: Representa um método que lança uma exceção;
Handler: Representa um método que é responsável por capturar uma
exceção;
ExceptionTarget:
Representa
uma
tupla
formada
por
uma
Exception definida no contrato e um conjunto de Handlers
associados, isso porque uma mesma exceção pode ser capturada por
vários handlers;
MethodExpression: Classe abstrata que representa uma expressão de
método, permitindo inclusive o uso de wildcards, de forma semelhante
aos pointcuts AspectJ. As classes Signaler e Handler herdam desta
classe abstrata;
A seguir apresentamos como a ferramenta verifica os contratos.
57
4.8 Verificação da regra de design
Quando o teste inicia, o aspecto de checagem inicializa o controlador e o
contrato é lido como mostra o diagrama de sequência na Figura 15.
Figura 15: Inicialização do aspecto de checagem
Para verificar as regras de design, quando uma exceção é capturada dentro do
teste, o verificador entra em ação. Como mostra a Figura 16 a seguir, o verificador
aciona o controller para que este verifique o contrato. O controller por sua vez repassa
a responsabilidade para a própria entidade de contrato, para verificar se a regra foi
obedecida. Para tanto, verifica-se se existe algum item do trace da exceção que seja
contemplado como signaler. Caso exista, é verificado se a exceção que foi lançada
pelo signaler está no contrato, e caso esteja, verifica qual o handler que a capturou.
Caso o handler esteja especificado na regra, nada acontece. Caso contrário, o
sistema lança uma exceção do tipo ContractCheckingException.
58
Figura 16: Diagrama de sequência para verificação da regra de design
4.9 Interface gráfica
Com o objetivo da facilitar a preparação do ambiente para a realização dos
testes de comportamento excepcional em um projeto Java, foi desenvolvida uma
interface, utilizando a API Swing do Java, que permite que o usuário selecione um
projeto eclipse [Eclipse 2012] e este projeto tenha o seu ambiente automaticamente
configurado para a utilização da VITTAE, incluindo as libs que precisam ser
adicionadas ao projeto, adequações para o uso do AspectJ, criação de um pacote de
testes, da pasta contracts e do arquivo contract.xml. A Figura 17 e a Figura 18
na página 60 apresentam essa GUI.
4.10 Discussões
Este capítulo apresentou como foi construída a ferramenta que foi resultado
deste trabalho, a VITTAE, descrevendo pacote a pacote como a ferramenta funciona.
O capítulo a seguir apresenta um estudo de caso prévio que foi aplicado sobre uma
linha de produto de software.
59
Figura 17: Interface gráfica da VITTAE para configuração do projeto eclipse
Figura 18: Janela da VITTAE para escolha do projeto eclipse a ser configurado
60
5 Estudo de Caso
Este capítulo apresenta um estudo de caso que foi utilizado para avaliar previamente
a abordagem em ação. Ele envolveu a aplicação dos passos da abordagem sobre a
MobileMedia [Figueiredo et al. 2008], um sistema desenvolvido no ambiente
acadêmico cujas várias versões têm o seu código-fonte disponível, permitindo avaliar
a degradação das regras de design ao longo do tempo, e que é apresentado na seção
5.1. Na seção 5.2, descrevemos como foi realizado o estudo e os seus resultados.
Por fim, na seção 5.3 realizamos o fechamento do capítulo.
5.1 Mobile Media (MM)
A MobileMedia(MM) [Figueiredo et al. 2008] é uma linha de produto de software
(LPS) que manipula fotos, música e vídeo em dispositivos moveis e que já foi utilizada
em diversos estudos de caso empíricos como os realizados pelos trabalhos de Ribeiro
e Borba [Ribeiro and Borba 2010], Torres et al. [Torres et al. 2010] e Alencar et al.
[Alencar et al. 2010].
Ela foi desenvolvida baseada em uma linha de produto prévia chamada
MobilePhoto, desenvolvida pela universidade de British Columbia. Para implementar
a MobileMedia os desenvolvedores estenderam o núcleo da MobilePhoto incluindo
novas funcionalidades, podendo ser estas obrigatórias, opcionais ou alternativas.
A Tabela 1 exibe algumas das métricas de quatro versões da MobileMedia,
apresentando o número de linhas de código (LOC) e o número de classes de cada
versão orientada a objetos (OO) e orientada a aspectos(AO). A análise dessa tabela
mostra que essa LPS tem um tamanho razoável de linhas de código, classes e
aspectos, tornando-a adequada para a realização de um estudo de caso que aplica
nossa abordagem.
MM OO
v1
LOC
Classes
MM OOv
MM AO v1 2
MM OO
MM AO v2 v3
MM OO
MM AO v3 v4
MM AO
v4
1159
1254
1316
1470
1364
1578
1559
1805
24
27
25
29
25
32
30
39
Tabela 1: Métricas da MobileMedia
A Figura 19 e a Figura 20 mostram o design das versões Orientada a Objetos e
Orientada a Aspectos respectivamente.
61
View
MainUIMidlet
S
SMSScreen
PhotoViewScreen MediaListScreen PlayMediaScreen
E
E
E
E
CommandList
CommandListener
Controller
AbstractController
Media
Controller
Legend:
E
Exception Handling
C
Count views optional feature
F
Select favorites optional feature
S
Send SMS
E
Model
F
C
S
PhotoView
Controller
MusicPlay
Controller
E
MediaAccessor
E
E
VideoAccessor
MusicAccessor
PhotoAccessor
VideoAlbumData MusicAlbumData PhotoAlbumData
Figura 19: Design OO da MobileMedia
Figura 20: Design AO da MobileMedia
62
E
AlbumData
O núcleo da implementação da MobileMedia foi desenvolvido para rodar sobre
a plataforma J2ME. Este sistema possui algumas peculiaridades: (i) políticas de
tratamento de exceções bem definidas, (ii) funcionalidades alternativas e opcionais,
(iii) possui subversões orientadas a objetos e orientadas a aspectos e (iv)
representatividade no domínio de dispositivos móveis.
5.2 Aplicando a Abordagem a uma Linha de Produto de Software
O estudo de caso realizado foi reportado no trabalho de Sales Júnior e Coelho
(2011). Neste estudo de caso foram utilizadas 4 versões da MobileMedia, tanto as
versões orientadas a objetos (OO), quanto as versões orientadas a aspectos (AO). A
utilização dessas múltiplas implementações foi importante para verificar os contratos
de comportamento excepcional não somente entre diferentes verões do mesmo
sistema, mas também entre diferentes implementações.
Após selecionar o sistema, o comportamento do tratamento de exceções da
aplicação foi analisado manualmente na primeira versão orientada a objetos (OO
1.0). Com base na análise manual dessa nessa versão foram extraídos os contratos
de tratamento de exceção que seriam verificados nas versões posteriores (tanto OO
quanto AO). Como resultado foi obtido um conjunto de 16 regras de design
excepcionais (essas regras de design são descritas no Apêndice B). A Tabela 2
apresenta alguns exemplos dos contratos encontrados.
ID
Contratos de tratamento de exceções
1
<signaler signature="ubc.midp.mobilephoto.core.
ui.datamodel.AlbumData.deleteImage(java.lang.String,j
ava.lang.String)">
<exception type="lancs.midp.mobilephoto.lib.
exceptions.ImageNotFoundException">
<handler signature="ubc.midp.mobile
photo.core.ui.controller.BaseController.handleCommand
(javax.microedition.lcdui.Command,javax.microedition.
lcdui.Displayable)"/>
</exception>
</signaler>
<signaler
signature="ubc.midp.mobilephoto.core.ui .datamodel.Im
ageAccessor.loadAlbums()">
<exception type="lancs.midp.mobilephoto.lib
.exceptions.InvalidImageDataException">
2
63
3
<handler
signature="ubc.midp.mobilephoto .core.ui.datamodel.Al
bumData.getAlbumNames()"/>
</exception>
</signaler>
<signaler signature="ubc.midp.mobilephoto.core.ui.dat
amodel.AlbumData.getImageNames(java.lang.String)">
<exception type="lancs.midp.mobilephoto.lib
.exceptions.UnavailablePhotoAlbumException">
<handler
signature="ubc.midp.mobilephoto
.core.ui.controller.BaseController.showImageList(java
.lang.String)"/>
</exception>
</signaler>
Tabela 2: Exemplos de Contratos de Tratamento de Exceções da MobileMedia
A partir dos contratos definidos, passou-se para o passo seguinte da
abordagem, com a geração semiautomática dos testes JUnit e dos Mock Aspects para
cada teste. O trecho de código a seguir ilustra o teste gerado para o contrato 2 da
Tabela 2.
1. imports …
2.
3. public class ImageAccessorloadAlbumsInvalidImage
4.
DataExceptionTest
extends TestCase {
5.
6.
public void
7.
testContractHandlergetAlbumNames0() {
8.
9.
AlbumData handler = new AlbumData();
10.
handler.getAlbumNames();
11.
}
12.
13.}
Com o objetivo de estimular o lançamento da exceção que deve ser verificada,
foi gerado o mock aspect apresentado no trecho de código a seguir.
64
1.imports...
2.
3. @Aspect public class
4. ImageAccessorloadAlbumsInvalidImageData
ExceptionTestMockTestAspect {
5.
6.
@Around("execution (* ubc.midp.mobilephoto.
7.
core.ui.datamodel.ImageAccessor.loadAlbums()) &&
8.
cflow(execution(* ImageAccessorloadAlbumsInvalid
9.
ImageDataExceptionTest.*(..)))")
10.
11.
public ObjectlaunchInvalidImageDataException
12.
(ProceedingJoinPoint thisJoinPoint)
13.
throws
InvalidImageDataException {
14.
15.
throw new lancs.midp.mobilephoto.lib.
16.
17.
exceptions.InvalidImageDataException();
}
18.
19.}
Em alguns cenários não foi simples estimular o código do signaler e handler,
visto que existiam algumas classes que exigiam muito código de inicialização. Em tais
cenários foi necessário definir mock objects de suporte, utilizando frameworks como,
por exemplo, o EasyMock [Freese and Tammo 2002]. O trecho de código abaixo
ilustra o teste unitário criado para checar o contrato 3 da Tabela 2.
1. imports …
2.
3. public class AlbumDatadeleteImageImageNotFound
4.
ExceptionTest extends TestCase {
5.
6. public void testContractHandlerhandleCommand0()
7. {
8.
9.
Command comando =
10.
EasyMock.createMock(Command.class);
11.
65
12.
13.
14.
15.
EasyMock.expect(comando.getLabel()).
andReturn("Delete").anyTimes();
EasyMock.replay(comando);
16.
BaseController handler = new BaseController(new
17.
MainUIMidlet(),new AlbumData());
18.
19.
handler.handleCommand(comando, null);
20. }
No caso de teste acima, para instanciar o código do handler foi necessário criar
objetos auxiliares (apresentados destacados nas linhas de 9-15) que devem ser
passados como parâmetros mas que não interferem a maneira como a exceção é
tratada.
O último passo foi executar a ferramenta VITTAE nas 4 versões OO e AO da
MobileMedia. O resultado da execução dos testes pode ser observado na Tabela 3 da
página 69.
Para um melhor entendimento, descrevemos a seguir o significado de cada um dos
resultados apresentados na Tabela 3:
Passed: O teste passou, ou seja, a regra de design excepcional foi
obedecida o que significa que a exceção lançada pelo signaler
especificado, foi tratada pelo handler descrito no contrato excepcional;
Passed-Esc: O teste passou, mas durante o tratamento da exceção
uma nova exceção foi lançada.
Faled – Exc Han : O JUnit retornou uma falha no teste, não porque a
exceção não foi capturada, mas sim porque havia ocorrido um
refatoramento OO -> AO e a exceção não estava mais sendo tratada
por uma classe, mas sim por um aspecto tratador de erro (como
explicado na seção 3.1).
Failed – Soft: O JUnit retornou uma falha porque a exceção foi
transformada em uma RuntimeException (também explicado na
seção 3.1) e nenhuma outra entidade (objeto ou aspecto) tratou essa
nova exceção lançada.
Failed – Unc: O JUnit retornou uma falha porque a exceção não foi
tratada.
66
n/a: O teste não é mais aplicável, devido a uma classe ou método que
deixou de existir.
Como pode ser observado, nas versões OO a ferramenta detectou problemas
no tratamento de exceções em oito casos. Nestes casos de teste, a exceção que
estava especificada na regra de design era capturada corretamente através de um
comando try. Porém, durante a execução do trecho de tratamento foi lançada uma
nova exceção que não foi tratada por nenhum elemento do sistema.
Outros problemas surgiram ainda ao executar os testes nas versões AO. Sete
casos de teste falharam porque a exceção não foi tratada pelo elemento descrito na
regra de design, mas sim por um aspecto tratador de erro. Esses casos indicam que
houve uma mudança na política de captura de exceções do sistema, o que exigiria
uma atualização nos contrafiguratos excepcionais, e consequentemente nos testes.
Em um caso, a exceção que era capturada na versão OO não foi capturada por
ninguém e demonstra que no refatoramento OO -> AO o time de desenvolvimento
descuidou-se deixando uma exceção sem tratamento. Igualmente perigosos foram os
outros seis casos onde a exceção foi capturada e transformada numa exceção não
checada do tipo RuntimeException. Como no Java as exceções não checadas não
têm que obrigatoriamente ser tratadas, isso representa um perigo visto que durante o
desenvolvimento o seu tratamento pode ser esquecido, por descuido do time de
desenvolvimento.
5.3 Discussões
Este capítulo apresentou um estudo de caso preliminar que aplicou os passos
da abordagem proposta neste trabalho sobre uma linha de produto de software, a
Mobile Media. Foram apresentados detalhes sobre como este estudo foi realizado e
os resultados obtidos. O estudo mostrou que com o passar das versões e na
transformação do sistema na versão orientada a objetos para a versão orientada a
aspectos houve mudanças na política de tratamento de exceção – o que exigiria uma
mudanças nas regras de design excepcionais –, algumas exceções foram
simplesmente transformadas em exceções não-checadas – representando um risco
visto que essas exceções podem ser facilmente “esquecidas” durante o
67
desenvolvimento – e uma exceção simplesmente deixou de ser tratada e escapou
para o usuário, o que se constitui num grave problema.
O próximo capítulo apresenta um estudo avaliativo que realizou a comparação
entre a ferramenta que resultou deste trabalho, a VITTAE, com a JUnitE, ferramenta
proposta no trabalho de DiBernardo et al [Di Bernardo et al. 2011] para especificação
e verificação de fluxos de tratamento de exceções.
68
Tabela 3:Resultados da execução da abordagem da MobileMedia em diferentes versões
69
6 Estudo avaliativo – Comparação entre VITTAE e
JUnitE
Este capítulo apresenta uma avaliação empírica cujo objetivo é comparar
duas ferramentas para teste de comportamento excepcional: a JUnitE [Di
Bernardo et al 2011] e a VITTAE [Sales Júnior and Coelho 2011]. As duas
ferramentas permitem a definição de regras que governam o comportamento
excepcional da aplicação e estendem o JUnit [JUnit 2012] para a criação de
testes que verificam essas regras. Devido à semelhança entre os objetivos
dessas ferramentas, realizar um estudo avaliativo comparando-as é uma forma
tanto de exercitar a utilização delas e analisar a sua usabilidade, quanto de
verificar se na prática realmente elas são equivalentes. Tal avaliação foi
realizada em duas etapas. Na primeira foi realizada uma análise quantitativa
baseada na técnica GQM (Goal Question Metric) [Basili, Caldiera and Rombach
1994] e organizada de acordo com o Quadrado Latino [Box, Hunter and Hunter
2005] para avaliar a eficácia das duas ferramentas em descrever regras de
comportamento excepcional e realizar os testes que verificam tais regras. Os
resultados da execução da primeira etapa do experimento proveram dados que
foram comparados com um conjunto de testes de hipóteses que permitiram
chegarmos a determinadas conclusões. A segunda etapa foi uma análise
qualitativa baseada em questionários para avaliar a satisfação dos indivíduos
em utilizar as ferramentas para realização das atividades de teste de
comportamento excepcional.
O capítulo está organizado como descrito a seguir. A seção 6.1 apresenta
a JUnitE, a ferramenta gerada como resultado da abordagem proposta por Di
Bernardo [Di Bernardo et al. 2011] e que está sendo comparada com a VITTAE.
A seção 6.2 descreve a primeira etapa, a avaliação quantitativa, incluindo as
hipóteses levantadas, como o experimento foi organizado, os dados obtidos
durante a realização do experimento e o teste de hipótese realizado, levando
assim a algumas conclusões que também são detalhadas nesta seção. A seção
6.3 descreve a segunda etapa do experimento, a avaliação qualitativa, quais os
seus objetivos e os resultados obtidos. A seção 6.4 faz o fechamento do capítulo.
70
6.1 JUnitE
O trabalho de Di Bernardo et al. [Di Bernardo et al. 2011] propõe uma
abordagem para especificar o comportamento excepcional por meio de um
conjunto de testes que estendem o framework JUnit. Esta abordagem permite
especificar no caso de teste todo o caminho que deve ser percorrido pelas
exceções, dos métodos que as lançam até os métodos que as capturam. Através
de um monitoramento em tempo de execução é checado se o comportamento
excepcional do sistema satisfaz sua especificação durante a realização dos
testes. A versão analisada por esta dissertação foi a 1.2.
O trecho de código a seguir ilustra um teste JUnitE para o mesmo contrato
ilustrado nas seções 3.2.1 e 3.2.2.
1. //imports and packages...
2. ...
3. public class NegativeBalanceExceptionBankOperationEHTest
4.
extends JUnitETestCase{
5.
5.
@ForceException (exception=”NegativeBalanceException”,
6.
method=” br.ufrn.dados.Bank.debitar”,
7.
methodReturnType=”void”,
8.
methodParType=”double”)
9.
10.
@Test
11.
public void test01 (){
12.
String trace [] = new String []{
13.
exception(“NegativeBalanceException”),
14.
raiseSite(“br.ufrn.Bank.debitar”),
15.
handlingSite(“BankOperationEH.
16.
handlerBankOperation”)
17.
};
18.
super.setExceptionPath(trace);
19.
BankOperationEH handler = new BankOperationEH();
20.
handler.handlerBankOperation();
71
21.
}
22.}
Como nesta abordagem o contrato está especificado dentro do caso de
teste, é necessário definir o trace em uma String que tem os seguintes
elementos:
exception: exceção a ser lançada
raiseSite: método que lança a exceção
handlingSite: método que captura a exceção
Além de especificar o trace, a anotação @ForceException (pode ser
observada a partir da linha 5 do código anteriormente apresentado) pode ser
utilizada para especificar o lançamento de uma exceção (semelhante ao aspect
mock utilizado em nossa abordagem, porém com a necessidade de incluir um
trecho de código específico para essa geração). Para isso é necessário
descrever qual a exceção deve ser lançada, o método que lançará a exceção
quando executado, os parâmetros deste método e o tipo de retorno. Após isso,
uma classe geradora pode ser utilizada para, a partir da classe de teste, gerar
um aspecto que força o lançamento da exceção (aspect mock). O trecho de
código a seguir exibe um exemplo do código necessário para gerar o aspect
mock para o teste apresentado anteriormente utilizando a JUnitE.
1. AspectGenerator.generateAspect
2.
(NegativeBalanceExceptionBankOperationEHTest.class);
Como
pode
ser
observado
é
necessário
utilizar
o
método
generateAspect para gerar o mock aspect. A partir dos dados obtidos na
anotação @ForceException, é gerado o mock.
Assim como a VITTAE a JUnitE também utiliza a infraestrutura do Junit
para executar os testes. Quando um teste é executado na JUnitE, é gerado um
arquivo “.txt” na pasta raiz do projeto com um log da realização do teste. O trecho
a seguir é um exemplo de log da ferramenta.
72
#NegativeBalanceException
br.ufrn.Bank.debitar
@BankOperation.EH.handlerBankOperation
O símbolo ‘#’ é utilizado para indicar qual foi a exceção lançada. As linhas
abaixo indicam qual o caminho pela qual a exceção fluiu até chegar ao método
que a capturou, indicado pelo símbolo ‘@’.
Na tabela Tabela 4, apresentamos uma comparação entre algumas
características da VITTAE e da JUnitE.
Tabela 4: Comparação de características entre a VITTAE e JUnitE
6.2 Avaliação quantitativa
Podemos dividir a realização da avaliação quantitativa nas seguintes
etapas: definição do experimento, formulação das hipóteses, projeto do
experimento, operação do experimento, delineamento do experimento, análise
73
dos resultados obtidos e a discussão sobre ameaças à validade do experimento.
Cada uma dessas etapas é analisada a seguir.
6.2.1 Definição do experimento
A avaliação quantitativa deste trabalho foi realizada com base na
abordagem GQM (Goal-Question-Metric) [Basili, Caldiera and Rombach 1994].
Esta abordagem para métricas de software define um modelo de medição em
três níveis: objetivo (goal), questões (question) relacionadas com este objetivo,
e métricas (metric) que respondem a cada uma das questões de uma forma que
possa ser medida.
O objetivo (goal) desta etapa na avaliação foi comparar a eficácia das duas
ferramentas na especificação de regras de comportamento excepcional e na
construção e realização dos testes de comportamento excepcional com relação
ao tempo para definição das regras de comportamento excepcional, tempo para
criação e execução dos testes, quantidade de erros de testes e quantidade de
vezes em que a origem da falha foi encontrada, do ponto de vista de alunos de
engenharia de software no contexto de experimentos realizados com estudantes
de engenharia de software e testes de software no ambiente universitário.
Para realizar essa comparação foi elaborado um conjunto de questões
(question) relacionadas com este objetivo. Essas questões são descritas na
Tabela 5.
Código
Q1
Questão
O tempo para especificar regras de comportamento excepcional
utilizando a VITTAE é menor do que o tempo para especificar regras de
comportamento excepcional na JUnitE?
Q2
O tempo para escrever e realizar os testes utilizando a VITTAE é menor
do que o tempo utilizado para realizar as mesmas atividades na JUnitE?
Q3
A quantidade de erros de teste encontrados durante a realização de
testes com a VITTAE é maior do que a quantidade de erros de teste
encontrados utilizando a JUnitE?
74
Q4
Os usuários identificaram mais vezes as causas dos erros de teste
através do log utilizando a VITTAE do que utilizando a JUnitE?
Q5
O tempo total para realização dos testes de comportamento
excepcional (tempo para criação das regras + criação/execução dos
testes) é menor usando a VITTAE em comparação com a JUnitE?
Tabela 5: Questões relacionadas ao objetivo do experimento
Para cada uma destas questões foi listada uma métrica para avaliá-la.
Essas métricas são apresentadas na Tabela 6.
Código
Descrição da Métrica
Questões
relacionadas
M1
Tempo utilizado para especificar as regras de Q1
comportamento excepcional.
M2
Tempo para escrita e realização dos testes.
Q2
M3
Quantidade de erros de teste encontrados.
Q3
M4
Quantidade de vezes que os usuários encontraram Q4
as causas dos erros de teste.
M5
Tempo total utilizado para especificar as regras e Q5
realizar os testes.
Tabela 6: Métricas relacionadas com as questões levantadas
Decidimos não considerar separadamente o tempo para criação do caso
de teste e o tempo para execução do teste visto que as duas ferramentas
utilizam a infraestrutura do JUnit para execução dos testes. Na próxima seção
descrevemos as hipóteses do nosso experimento.
6.2.2 Hipóteses
O objetivo de nosso experimento é simular um ambiente de criação e
execução de casos de teste utilizando as duas ferramentas a fim de coletar
métricas para realizarmos análises sobre elas. As hipóteses foram elaboradas
75
com base nas questões descritas na seção anterior.
nula e
representa a hipótese
representa a hipótese alternativa. A Tabela 7 apresenta as hipóteses
para cada questão. A próxima seção apresenta o projeto do experimento.
Referente ao tempo para descrição das regras
Q1
Referente ao tempo para realização do teste
Q2
Referente ao número de erros de teste encontrados
Q3
Referente ao número de vezes em que a causa dos erros foi encontrada
Q4
Referente ao tempo total para teste de comportamento excepcional
Q5
Tabela 7: Hipóteses baseadas nas questões
6.2.3 Projeto do experimento
Em nosso contexto, além das métricas avaliadas existem fatores de
interesse que são controlados pelo pesquisador. Esses são chamados de
variáveis independentes ou fatores de controle. Neste experimento os fatores
são os sujeitos e os sistemas sobre os quais os testes serão realizados. Esses
fatores são discutidos nesta seção.
76
6.2.3.1
Escolha dos sujeitos
O primeiro fator de controle é a escolha dos sujeitos para o experimento.
As diferentes experiências e habilidades dos sujeitos podem influenciar
diretamente nas métricas escolhidas. Os sujeitos escolhidos para a realização
do experimento foram alunos de graduação do curso de Bacharelado em
Engenharia de Software da Universidade Federal do Rio Grande do Norte que
estavam cursando a disciplina de testes de software. Essa disciplina comumente
é cursada pelos alunos durante o quarto período do curso.
A escolha foi motivada pelo fato de que neste período os alunos já
pagaram as disciplinas básicas de programação e já tiveram contato com a
linguagem de programação Java. Além disso, durante a primeira parte das
disciplinas os alunos tiveram noções de testes de software e utilizaram a
ferramenta JUnit para realização de testes unitários, e tanto a VITTAE quanto a
JUnitE estendem a infraestrutura do JUnit para realização dos testes de
comportamento excepcional.
A turma era constituída de 29 alunos, dos quais 8 participaram do
experimento. Estes sujeitos foram os escolhidos visto que foram os únicos que
compareceram a todos os dias de realização do experimento.
6.2.3.2
Escolha dos sistemas
Os sistemas sobre os quais os testes são realizados podem influenciar
nas métricas obtidas visto que as complexidades dos sistemas variam. Com o
objetivo de diminuir essa variabilidade, para a realização do experimento foram
criados dois sistemas simples em Java, que apresentam semelhanças no que
diz respeito ao tamanho, complexidade e políticas de tratamento de exceção.
O Sistema Bancário é uma aplicação MVC que permite que sejam
criados e inseridos clientes e sejam realizadas operações comuns em um
ambiente bancário, como saque, depósito e verificação de saldo. A Figura 21 a
seguir apresenta um diagrama de classes e pacotes que fornece uma noção da
estrutura do sistema.
77
Figura 21: Diagrama de classes e pacotes com a estrutura do Sistema Bancário
O Sistema de Fichas também é um sistema MVC construído utilizando a
linguagem de programação Java que permite que sejam inseridos Produtos e
seja realizada a compra de tais produtos através da inserção de fichas, de
acordo com os preços de cada um. A Figura 22 apresenta um diagrama de
classes e pacotes que fornece uma noção da estrutura do sistema.
78
Figura 22: Diagrama de classes e pacotes com a estrutura do Sistema de Fichas
Esses dois sistemas foram escolhidos por causa da semelhança de sua
complexidade e sua simplicidade. A Tabela 8 apresenta alguns dados1 que
comprovam essas características.
Sistema de Fichas
Sistema Bancário
LOC
112
115
Pacotes
3
3
Número de Classes
5
5
Complexidade
1,05
1,05
Ciclomática Média
Tabela 8: Comparação de características entre os dois sistemas
Optou-se por dois sistemas simples para facilitar a condução do
experimento, permitindo que os sujeitos focassem na parte mais importante que
era a construção das regras de comportamento excepcional e dos testes.
Experiências de trabalhos anteriores com experimentos controlados como foi o
1
Dados obtidos através da ferramenta METRICS disponível em http://metrics.sourceforge.net/
79
caso de Accioly [Accioly 2012] mostraram que no caso de pouca disponibilidade
de tempo para realização do experimento, o que era o caso deste trabalho, o
melhor é permitir que os sujeitos atuem sobre sistemas simples para não
dificultar a execução da atividade, e o objetivo do experiment seja alcançado.
6.2.3.3
Organização do experimento
A fim de gerenciar estes fatores de controle e devido às características do
experimento optou-se por um projeto baseado no delineamento por quadrado
latino [Box et al. 2005]. Essa abordagem é comum em experimentos onde a
quantidade de tratamentos e de itens nos fatores de controle são iguais. No caso
deste experimento os fatores de controle são considerados como unidades
experimentais e os tratamentos são as ferramentas que estão sendo avaliadas.
As unidades experimentais são distribuídas em linhas e colunas. Os tratamentos
são sorteados de tal forma que só aparecem uma vez em cada coluna e uma
vez em cada linha. Para aplicar esta abordagem em nossos fatores, nós
dispusemos os sujeitos nas linhas e os sistemas nas colunas do quadrado latino
como pode ser observado na Tabela 9. Em cada linha e em cada coluna os
tratamentos aparecem apenas uma vez e em cada atividade o sujeito executa
dois testes de comportamento excepcional, um para cada sistema. A ordem dos
sistemas é aleatória. Devido à quantidade de sujeitos, foi necessária a
realização de quatro réplicas. A próxima seção apresenta como o experimento
foi operacionalizado.
Sujeito 1
Sujeito 2
Sujeito 1
Sujeito 2
Sujeito 1
1o. Quadrado
Sistema Bancário
JUnitE
VITTAE
2o. Quadrado
Sistema Bancário
JUnitE
VITTAE
3o. Quadrado
Sistema Bancário
VITTAE
80
Sistema Fichas
VITTAE
JUnitE
Sistema Fichas
VITTAE
JUnitE
Sistema Fichas
JUnitE
Sujeito 2
Sujeito 1
Sujeito 2
JUnitE
Vittae
4o. Quadrado
Sistema Bancário
JUnitE
VITTAE
Sistema Fichas
VITTAE
JUnitE
Tabela 9: Organização dos sujeitos no experimento utilizando o delineamento em
quadrado latino
6.2.3.4
Operação do experimento
A execução do experimento foi realizada durante fases, como mostra a
tabela Tabela 10 cada fase com uma duração de 1:30 h.
Dia 1
Apresentação
Dia 2
das Treinamento
Ferramentas
ferramentas
Dia 3
nas Execução
experimento
do
e
coleta
dos dados
Tabela 10 Relação entre as fases do experimento e os dias em que elas foram
executadas
A primeira fase constituiu-se de uma apresentação sobre a definição de
regras de tratamento de exceção e sobre as duas ferramentas que seriam
avaliadas, bem como seu funcionamento. Para permitir que os sujeitos tivessem
maior proficiência com as ferramentas a segunda fase foi o treinamento na
utilização da VITTAE e da JUnitE permitindo que os sujeitos treinassem o uso
delas em uma aplicação toy program e também exercitassem a realização de
teste de comportamento excepcional. A terceira fase foi a realização do
experimento em si, separando as duplas de acordo com a distribuição do
quadrado
latino.
Nesta
fase,
os
sujeitos
receberam
uma
regra
de
comportamento excepcional, em linguagem natural, para cada sistema, onde as
seguintes atividades deveriam ser realizadas: 1) escrita da regra de
comportamento excepcional no formato da ferramenta; 2) escrita e execução do
teste de comportamento excepcional para a regra; 3) os sujeitos deveriam
responder a um questionário informando os dados das atividades anteriores
81
(incluindo o tempo e a quantidade de erros) e uma pesquisa de satisfação,
pesquisa essa cujos dados foram analisados na segunda etapa do experimento;
4) Envio dos arquivos contendo os projetos e os testes. A seguir detalhamos
cada uma dessas atividades.
Atividade 1) Escrita da regra de comportamento excepcional no formato
da ferramenta.
Para cada um dos sistemas foi descrita uma regra de design de
comportamento excepcional em linguagem natural. Os sujeitos deveriam
descrever essas regras de acordo com a sintaxe da ferramenta, e escrever e
executar os testes que verificavam se as regras estavam sendo obedecidas. A
Tabela 11 apresenta a regra para cada sistema.
Sistema Bancário
“Quando
uma
exceção
do
tipo
OperadorNegativoException
for lançada pelo método creditar da
classe
ContaBancaria,
o
método
creditarNaConta da
classe
AlterarSaldoBancarioControlador
deve tratar essa exceção.”
Sistema de Fichas
“Quando uma exceção do tipo
SaldoDeFichasNegativoException
for lançada pelo método gastarFichas
da classe UsuarioMaquina, o método
comprarProduto
da
classe
OperacoesFichaController
deve tratar essa exceção.”
Tabela 11: Regra de Design Excepcional de cada sistema
Como resultado, para cada um dos sistemas as regras deveriam ser
descritas como mostrado na Tabela 12. O motivo pelo qual foi utilizada somente
uma regra foi a pequena janela disponível para realização do experimento, o que
82
inviabilizou a realização de um experimento com mais contratos a serem
verificados pelos sujeitos.
Sistema
Bancário
Sistema
de Fichas
JUnitE
VITTAE
String trace[] = new String
[]{
raiseSite("br.ufrn.entities.
ContaBancaria.creditar"),
exception("br.ufrn.exception
s.OperacaoValorNegativoExcep
tion"),catchSite("br.ufrn.co
ntroller.AlterarSaldoBancari
oControlador.creditarNaConta
")};
<contract>
<signaler
signature="br.ufrn.entities.credit
ar(..)">
<exception
type="br.ufrn.exceptions.OperacaoV
alorNegativoException">
<handler
signature="br.ufrn.controller.Alte
rarSaldoBancarioControlador.credit
arNaConta(..)"/>
</exception>
</signaler>
</contract>
String trace [] = new
String[]{raiseSite("
br.ufrn.entities.UsuarioMaqu
ina.java.gastarFichas"),exce
ption("br.ufrn.exceptions.Sa
ldoDeFichasNegativoException
"),
catchSite("br.ufrn.controlle
r.OperacoesFichaController.j
ava.comprarProduto")};
<contract>
<signaler
signature="br.ufrn.entities.Usuari
oMaquina.gastarFichas(..)">
<exception
type="br.ufrn.exceptions.SaldoDeFi
chasNegativoException">
<handler
signature="br.ufrn.controller.Oper
acoesFichaController.comprarProdut
o(..)"/></exception>
</signaler>
</contract>
Tabela 12: Resultado esperado das regras de design descritas nas ferramentas
Atividade 2) Escrita e execução do teste de comportamento excepcional
para a regra.
Após descrever a regra de comportamento excepcional, o próximo passo
era a escrita e execução do teste que verificaria tal regra. No 1º e no 2º dia
mostrou-se aos sujeitos que o teste envolvia executar os métodos que tratariam
as exceções (handlers ou catchers) com parâmetros que fizessem com que os
métodos que lançariam as exceções (signalers ou raisers) lançassem a exceção
do tipo especificado.
Para permitir que os sujeitos focassem na definição destes parâmetros,
no 3º dia foi fornecido aos sujeitos um trecho de código de exemplo que
83
inicializava os objetos cujos métodos teriam a função de tratadores para cada
sistema. Dessa maneira, permitiu-se que os sujeitos focassem seu raciocínio na
escrita do trecho em que os métodos tratadores de exceção eram chamados
com parâmetros que lançariam as exceções.
O trecho de código a seguir apresenta o exemplo de um trecho de código
de
inicialização
de
um
objeto
AlterarSaldoBancarioControlador
(creditarNaConta)
que
que
da
possui
captura
um
a
classe
método
exceção
OperadorNegativoException.
1. ...
2. AlterarSaldoBancarioControlador controlador = new
3. AlterarSaldoBancarioControlador();
4. ContaBancaria conta = new ContaBancaria();
5. Cliente c = new Cliente();
6. conta.setCliente(c);
7. controlador.setContaBancaria(conta);
8. ...
Após instanciar o objeto seria necessário então escrever o trecho de
código que faria com que a exceção fosse lançada. O trecho de código a seguir
mostra como o método creditarNaConta deveria ser chamado para que a
exceção
OperadorNegativoException
fosse
lançada
pelo
método
creditar da classe ContaBancaria.
1. ...
2. controlador.creditarNaConta("-1");
3. ...
Neste caso, após a realização do teste o resultado deveria ser como o
exibido na Figura 23 para a VITTAE e na Figura 24 na JUnitE.
84
Figura 23:Resultado do teste na VITTAE
Figura 24: Resultado do teste na JUnitE
Atividade 3) Contabilização dos erros e resposta aos questionários
Após a realização das atividades 1 e 2 os sujeitos deveriam responder a
um questionário. Ele se constituía de perguntas sobre os dados coletados nas
atividades 1 e 2 para cada sistema e uma pesquisa de satisfação sobre o uso
das ferramentas. Nesta fase iremos detalhar apenas o questionário referente à
coleta de dados das atividades. As questões relacionadas ao questionário de
satisfação serão abordadas na seção sobre a avaliação qualitativa. A Tabela 13
apresenta as perguntas realizadas e a relação delas com as questões
levantadas pela técnica GQM.
Código
Pergunta
P1
Quanto tempo (em Q1,Q5
segundos) você levou
para definir a regra
85
Questão (GQM)
neste sistema com
esta ferramenta?
P2
Quanto tempo (em Q2,Q5
segundos) você levou
para
escrever
e
executar
o
teste
(incluindo
o
tempo
para
analisar
o
resultado)?
P3
Seu caso de teste Q3
resultou numa falha
do JUnit?
P4
Você
conseguiu Q4
através
das
informações
fornecidas
no
log
identificar a causa da
falha?
Tabela 13: Lista de perguntas do questionário e a relação delas com as questões
levantadas para o experimento
Atividade 4) Envio dos arquivos de projeto e dos testes
A atividade final do experimento foi o envio dos arquivos dos projetos do
eclipse e dos testes através do sistema acadêmico. O objetivo era comparar os
dados obtidos no questionário com o que os sujeitos realmente haviam feito. Na
próxima seção apresentamos a análise dos resultados dessa primeira fase.
6.2.4 Análise dos resultados
Após coletar as métricas, foi necessário tentar tirar algumas conclusões
baseadas nos dados coletados. Por isso, ao final da execução do experimento
os projetos enviados pelos sujeitos na atividade 4 foram baixados a partir do
sistema acadêmico e os dados do questionário foram tabulados para iniciar a
86
análise. A partir dessa análise inicial, percebeu-se que houve uma incongruência
entre o que um dos sujeitos informou no questionário e o resultado da execução
dos testes que ele construiu e que foram enviados através do sistema
acadêmico. Isso fez com que para a pergunta P4, ele respondesse
negativamente para a utilização das duas ferramentas. Acreditamos que este
sujeito não entendeu bem o log das ferramentas talvez devido à baixa
proficiência no idioma inglês. Porém como o sujeito respondeu negativamente a
pergunta para as duas ferramentas e conseguiu realizar as outras tarefas do
experimento normalmente, decidiu-se por não removê-lo no experimento.
Após essa análise inicial passamos para análise dos dados das respostas
para cada uma das perguntas. Para isso, utilizamos o software de análise
estatística JMP [JMP 2012]. Para cada uma das perguntas foi criada uma tabela
no formato do JMP com os dados coletados. Para cada questão indicada na
Tabela 5, que correspondia a uma ou mais perguntas, foram realizados testes
estatísticos para refutar ou não as hipóteses nulas indicadas na Tabela 7.
Referente ao tempo para descrição das regras
Q1
Referente ao tempo para realização do teste
Q2
Referente ao número de erros de teste encontrados
Q3
Referente ao número de vezes em que a causa dos erros foi encontrada
Q4
Referente ao tempo total para teste de comportamento excepcional
Q5
Tabela 7: Hipóteses baseadas nas questões
87
O objetivo básico dos testes estatísticos é verificar se é possível ou não
rejeitar a hipótese nula. Para o propósito de nossa análise, o valor mais
importante a ser observado é o valor-p associado com o fator do tratamento.
Para evitar erros do Tipo 1, que significa rejeitar a hipótese nula quando de fato
ela é verdadeira, foi seguida a convenção de considerar um fator como sendo
significativa para uma resposta variável quando o valor-p for menor que 0.05
[Box, Hunter and Hunter 2005] .
Questão 1 : O tempo para especificar regras de comportamento
excepcional utilizando a VITTAE é menor do que o tempo para especificar
regras de comportamento excepcional na JUnitE?
A hipótese nula associada a esta questão é
, ou seja,
o tempo para descrever as regras de design utilizando é a VITTAE é igual ao
tempo para realizar a mesma atividade na JUnitE. Com base nisso analisamos
os dados para verificar se podemos refutar essa hipótese.
Os dados obtidos nessa questão foram os descritos na Tabela 14.
Réplica
1
1
2
2
3
3
4
4
1
1
2
2
3
3
4
4
Sujeito
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
Sistema Tratamento Tempo(s)
Bancario
JUnitE
685
Bancario
Vittae
189
Bancario
JUnitE
232
Bancario
Vittae
159
Bancario
Vittae
627
Bancario
JUnitE
636
Bancario
Vittae
394
Bancario
JUnitE
497
Fichas
Vittae
783
Fichas
JUnitE
210
Fichas
Vittae
154
Fichas
JUnitE
264
Fichas
JUnitE
422
Fichas
Vittae
539
Fichas
JUnitE
257
Fichas
Vittae
410
Tabela 14: Dados obtidos para a Q1
88
Para interpretar os dados foi realizada primeira uma análise descritiva
para observar a distribuição dos dados baseados em algumas características
como dispersão de dados e o valor da mediana. Para este propósito plotamos o
gráfico Box-plot [Jedlitschka and Pfahl 2005] que aparece na Figura 25. Apenas
observando esse gráfico observamos uma tendência para semelhança entre as
ferramentas. A Tabela 15 apresenta as medianas e o desvio padrão e a
semelhança entre as medianas é mais um indício de que os resultados obtidos
pelas ferramentas foram semelhantes.
Figura 25: Gráfico Box-Plot para os dados da Q1
JUnitE
VITTAE
Média
400,37
406,87
Desvio padrão
189,01
233,12
Tabela 15: Mediana e Desvio padrão dos dados da Q1
Além de analisar os resultados da mediana, nós queríamos comparar as
observações de acordo com os resultados dos sujeitos. A Figura 26 a seguir
89
mostra essa performance individual. De 8 sujeitos, 5 descreveram as regras de
design com a VITTAE mais rapidamente do que com a JUnitE.
Figura 26: Resultados individuais para cada ferramenta na Q1
Continuando com a análise, nós utilizamos o modelo de regressão linear
a seguir para realizar o teste de hipótese baseado no quadrado latino:
Yli jk =+l +li+j +k+li jk, onde
Yli jk : resposta da lª replica, iº sujeito, jº sistema e kº tratamento
média dos dados da resposta
l : efeito da lª réplica
li : efeito da interação entre a lª réplica e o iº sujeito
j : efeito do jº sistema
k : efeito do kº tratamento (JUnitE ou VITTAE)
90
li jk : erro aleatório
Este modelo considera os efeitos dos diferentes fatores sobre a resposta
variável (tempo). Para podermos adequar nossos dados a este modelo é
necessário verificar a normalidade dos dados. Para isso foi rodado o teste de
normalidade Shapiro-Wilk Test [Shapiro and Wilk 1965] que teve como resultado
o valor-p de 0,187. Este teste parte da hipótese nula de que os dados são
normais. Com este valor-p não é possível rejeitar esta hipótese, o que leva à
conclusão de que os dados são normais.
Dessa forma podemos observar no gráfico de envelope da Figura 27 que
os dados obtidos são adequados aos dados que se esperaria do modelo de
regressão linear anteriormente apresentado.
Figura 27: Comparação visual entre os dados obtidos na Q1 e o modelo predito no
gráfico de envelope
Por fim rodamos o teste de hipótese ANOVA (Analysis of Variance)
[Wohlin et al 2000] para o qual obtivemos os dados descritos na Tabela 16.
91
Como pode ser observado o valor-p associado ao tratamento foi de 0.88, o que
não satisfaz a condição de valor-p < 0.05 o que não nos permite rejeitar a
hipótese nula.
Source
Replica
Sujeito[Replica]
Sistema
Tratamento
Nparm
3
4
1
1
DF
3
4
1
1
Sum of Squares
271817,3
306385,5
9025
169
F Ratio
12,5641
10,6214
1,2515
0,0234
P-value
0,0054
0,0069
0,306
0,8834
Tabela 16: Resultados do teste ANOVA para os dados da Q1
Apesar de os resultados não serem estatisticamente relevantes, através
da avaliação destas análises o estudo deu indícios que as ferramentas são
equivalentes no que diz respeito ao tempo para descrever as regras de design.
Questão 2 : O tempo para escrever e realizar os testes utilizando a VITTAE
é menor do que o tempo utilizado para realizar as mesmas atividades na
JUnitE?
A hipótese nula associada a esta pergunta é
, ou
seja, o tempo escrever os testes utilizando é a VITTAE é igual ao tempo para
realizar a mesma atividade na JUnitE. Os dados obtidos nessa questão foram
os descritos na Tabela 17.
Réplica
1
1
2
2
3
3
4
4
1
1
2
2
3
3
4
4
Sujeito
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
Sistema Tratamento Tempo(s)
Bancario
JUnitE
718
Bancario
Vittae
130
Bancario
JUnitE
112
Bancario
Vittae
338
Bancario
Vittae
334
Bancario
JUnitE
98
Bancario
Vittae
188
Bancario
JUnitE
116
Fichas
Vittae
456
Fichas
JUnitE
200
Fichas
Vittae
75
Fichas
JUnitE
337
Fichas
JUnitE
145
Fichas
Vittae
292
Fichas
JUnitE
251
Fichas
Vittae
38
Tabela 17: Dados obtidos para a Q2
92
Para os dados dessa questão, o gráfico Box-plot é o que aparece na
Figura 28 a seguir. Apenas observando esse gráfico também observamos uma
tendência para semelhança entre os resultados das ferramentas. A Tabela 18
apresenta as medianas e o desvio padrão, dando uma pequena vantagem para
a VITTAE.
Figura 28: Gráfico Box-Plot para os dados da Q2
JUnitE
VITTAE
Média
247,12
231,37
Desvio padrão
207,07
146,40
Tabela 18: Mediana e Desvio padrão dos dados da Q2:
A Figura 29 a seguir mostra o desempenho individual dos participantes.
De 8 sujeitos, 5 descreveram as regras de design com a VITTAE mais
rapidamente do que com a JUnitE.
93
Figura 29: Resultados individuais para os dados da Q2
Continuando com a análise, nós utilizamos o mesmo modelo de
regressão linear da questão 1. O teste de normalidade retornou um valor-p de
0,03, o que nos permitiu refutar a hipótese de que a distribuição dos dados era
normal. Neste caso, quando a distribuição não é normal, faz-se necessário
realizar uma transformação Box-Cox [Sakia 1992] que é uma técnica de
transformação paramétrica que tem o objetivo de reduzir anomalias como a nãonormalidade. Após realizar a transformação o resultado foi o descrito na Tabela
19.
Réplica
1
1
2
2
3
3
Tempo –
Após
Sujeito Sistema Tratamento Box Cox
1
Bancario
JUnitE
744,426
2
Bancario
Vittae
347,1771
1
Bancario
JUnitE
323,7409
2
Bancario
Vittae
535,6934
1
Bancario
Vittae
532,8739
2
Bancario
JUnitE
303,8971
94
4
4
1
1
2
2
3
3
4
4
1
2
1
2
1
2
1
2
1
2
Bancario
Bancario
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Vittae
JUnitE
Vittae
JUnitE
Vittae
JUnitE
JUnitE
Vittae
JUnitE
Vittae
411,5692
329,134
611,2124
423,3308
267,198
534,9904
365,2579
501,9613
469,0897
189,8136
Tabela 19: Dados da Q2 após transformação Box-Cox
Após realizar a transformação, verificamos se os dados correspondiam
ao modelo, e como podemos observar pelo gráfico na Figura 30, após a
normalização, os dados se adequam melhor ao modelo.
Figura 30: Comparação visual entre os dados obtidos na Q2 após a transformação e o
modelo predito no gráfico de envelope.
Após realizarmos o teste ANOVA obtivemos os dados descritos na Tabela
20. Como se pode observar o valor-p associado ao tratamento foi de 0.81, o que
não satisfaz a condição de valor-p < 0.05 o que não nos permite rejeitar a
hipótese nula.
95
Source
Replica
Sujeito[Replica]
Sistema
Tratamento
Nparm
DF
3
4
1
1
3
4
1
1
Sum of Squares
F Ratio
P-value
67809,69
2,4176
0,1646
177970,58
4,7588
0,0452
1715,15
0,1834
0,6834
580,43
0,0621
0,8115
Tabela 20: Resultados do teste ANOVA para os dados da questão 2
Apesar de os resultados não serem estatisticamente relevantes, através
da avaliação destas análises o estudo deu indícios que as ferramentas são
equivalentes no que diz respeito ao tempo para escrever os testes para verificar
as regras de design.
Questão 3 : A quantidade de erros de teste encontrados durante a
realização de testes com a VITTAE é maior do que a quantidade de erros
de teste encontrados utilizando a JUnitE?
A hipótese nula associada a esta pergunta é
, ou
seja, o número de erros encontrados utilizando a VITTAE é igual ao número de
erros encontrados utilizando a JUnitE. Os dados obtidos nessa questão foram
os descritos na Tabela 21.
Réplica
1
1
2
2
3
3
4
4
1
1
2
2
3
3
4
4
Sujeito
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
Sistema Tratamento
Bancario
JUnitE
Bancario
Vittae
Bancario
JUnitE
Bancario
Vittae
Bancario
Vittae
Bancario
JUnitE
Bancario
Vittae
Bancario
JUnitE
Fichas
Vittae
Fichas
JUnitE
Fichas
Vittae
Fichas
JUnitE
Fichas
JUnitE
Fichas
Vittae
Fichas
JUnitE
Fichas
Vittae
Tabela 21: Dados obtidos para a Q3
96
Erros
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
Apenas observando a tabela chega-se a conclusão de que há indícios
que para este contexto as ferramentas são equivalentes no que diz respeito à
quantidade de erros encontrados.
Questão 4 : Os usuários identificaram mais vezes as causas dos erros de
teste através do log utilizando a VITTAE do que utilizando a JUnitE?
A hipótese nula associada a esta pergunta é
, ou
seja, a quantidade de vezes que os usuários conseguiram encontrar a causa da
falha através das informações fornecidas pelo log utilizando a VITTAE é igual à
quantidade de vezes utilizando a JUnitE. Os dados obtidos nessa questão foram
os descritos na Tabela 22.
Réplica
1
1
2
2
3
3
4
4
1
1
2
2
3
3
4
4
Sujeito
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
Encontrou
Sistema Tratamento a causa?
Bancario
JUnitE
Não
Bancario
Vittae
Sim
Bancario
JUnitE
Sim
Bancario
Vittae
Sim
Bancario
Vittae
Sim
Bancario
JUnitE
Sim
Bancario
Vittae
Sim
Bancario
JUnitE
Sim
Fichas
Vittae
Não
Fichas
JUnitE
Não
Fichas
Vittae
Sim
Fichas
JUnitE
Não
Fichas
JUnitE
Sim
Fichas
Vittae
Sim
Fichas
JUnitE
Sim
Fichas
Vittae
Sim
Tabela 22: Dados obtidos para a Q4
Como se pode observar apenas um sujeito não encontrou a causa da
falha utilizando a VITTAE. Esse mesmo sujeito também não encontrou a causa
da falha utilizando a JUnitE e uma análise dos dados do experimento desse
sujeito mostrou que provavelmente a sua resposta foi negativa para a VITTAE
devido a um mal entendimento da pergunta realizada.
Para os dados dessa questão, o gráfico em barras exibido na Figura 31,
demonstra que visualmente existe uma vantagem para a VITTAE em relação à
97
quantidade de vezes que os sujeitos acharam a origem do erro. Porém,
podemos aplicar o teste estatístico Chi Quadrado de Pearson que permite
verificar igualdade entre categorias discretas e mutuamente exclusivas
[McCreery and Phil 2007]. Neste caso o valor-p encontrado foi 0.24 o que nos
indica que não podemos descartar a hipótese nula de que não existe diferença
entre as contagens.
Figura 31: Gráfico em barras para a Q4
Questão 5 : O tempo total para realização dos testes de comportamento
excepcional (tempo para criação das regras + criação/execução dos
testes) é menor usando a VITTAE em comparação com a JUnitE?
A hipótese nula associada a esta pergunta é
, ou
seja, o tempo total para realização dos testes utilizando a VITTAE é igual ao
tempo para realizar a mesma atividade na JUnitE. Os dados obtidos nessa
questão foram os descritos na Tabela 23.
Réplica
1
1
2
2
3
Sujeito
1
2
1
2
1
Sistema Tratamento Tempo(s)
Bancario
JUnitE
1403
Bancario
Vittae
319
Bancario
JUnitE
344
Bancario
Vittae
497
Bancario
Vittae
961
98
3
4
4
1
1
2
2
3
3
4
4
2
1
2
1
2
1
2
1
2
1
2
Bancario
Bancario
Bancario
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
JUnitE
Vittae
JUnitE
Vittae
JUnitE
Vittae
JUnitE
JUnitE
Vittae
JUnitE
Vittae
734
582
613
1239
410
229
601
567
831
508
448
Tabela 23: Dados obtidos para a Q5
Para os dados dessa questão, o gráfico Box-plot é o que aparece na
Figura 32. Apenas observando esse gráfico também observamos uma tendência
para semelhança entre os resultados das ferramentas. A Tabela 24 apresenta
as medianas e o desvio padrão, dando uma pequena vantagem para a VITTAE.
Figura 32: Gráfico Box-Plot para a Q5
JUnitE
VITTAE
Média
647,50
638,25
Desvio padrão
328,65
344,53
Tabela 24: Mediana e Desvio padrão dos dados da Q5:
99
A Figura 33 mostra o desempenho individual dos participantes. De 8
sujeitos, 5 realizaram os testes (escreveram as regras e escreveram os testes)
de forma mais rápida na VITTAE que na JUnitE.
Figura 33: Resultados individuais para os dados da Q5
Continuando com a análise, nós utilizamos o mesmo modelo de
regressão linear das questões 1 e 2. O teste de normalidade retornou um valorp de 0,06, que permitiu refutar a hipótese de que a distribuição dos dados era
normal. Neste caso, quando a distribuição não é normal, faz-se necessário
realizar uma transformação Box-Cox. Após realizar a transformação o resultado
foi o descrito na Tabela 25.
Réplica
1
1
2
Tempo –
Após Box
Sujeito Sistema Tratamento
Cox
1
Bancario
JUnitE
2631,2552762
2
Bancario
Vittae
1749,6961074
1
Bancario
JUnitE
1788,5714581
100
2
3
3
4
4
1
1
2
2
3
3
4
4
2
1
2
1
2
1
2
1
2
1
2
1
2
Bancario
Bancario
Bancario
Bancario
Bancario
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Fichas
Vittae
Vittae
JUnitE
Vittae
JUnitE
Vittae
JUnitE
Vittae
JUnitE
JUnitE
Vittae
JUnitE
Vittae
1986,7922502
2380,6452429
2213,3936242
2076,4209524
2106,5048692
2546,8252923
1881,3067109
1585,6907479
2095,0071068
2061,4016469
2289,3139667
1999,0516489
1929,3901176
Tabela 25: Dados da Q5 após transformação Box-Cox
Após realizar a transformação, verificamos se os dados correspondiam
ao modelo, e como podemos observar pelo gráfico na Figura 34, após a
normalização, os dados se adequam melhor ao modelo.
Figura 34: Comparação visual entre os dados obtidos na Q5 após a transformação e o
modelo predito no gráfico de envelope.
Após realizarmos o teste ANOVA obtivemos os dados descritos na Tabela
26. Como pode ser observado o valor-p associado ao tratamento foi de 0.65, o
que não satisfaz a condição de valor-p < 0.05 o que não nos permite rejeitar a
hipótese nula.
101
Source
Replica
Sujeito[Replica]
Sistema
Tratamento
Nparm
DF
3
4
1
1
3
4
1
1
Sum of Squares
F Ratio
P-value
354753,02
7,9507
0,0164
724826,12
12,1835
0,0048
18584,00
1,2495
0,3064
3355,82
0,2256
0,6516
Tabela 26: Resultados do teste ANOVA para os dados da questão 2
Apesar de os resultados não terem sido estatisticamente relevantes, a
avaliação destas análises mostra que há indícios de que as ferramentas são
equivalentes no que diz respeito ao tempo total para realizar os testes de
comportamento excepcional (tempo de escrita das regras + tempo de criação
dos testes).
6.2.5 Ameaças à validade do estudo
Uma das ameaças à validade do estudo diz respeito à marcação do
tempo. Como ela foi realizada pelos próprios alunos, utilizando uma ferramenta
comum para contagem do tempo. Com esta contagem manual, erros
relacionados ao tempo gasto podem alterar os resultados obtidos. Outra ameaça
diz respeito à proficiência dos alunos na língua inglesa, que pode também alterar
os resultados referentes à leitura do log. Ainda outra ameaça diz respeito às
diferentes proficiências dos sujeitos em programação e testes de software.
Embora procurou-se utilizar alunos quem tivessem um nível de conhecimento
semelhante sobre testes de software, um ou outro aluno pode demonstrar ter
experiência pessoal adicional em testes e desenvolvimento de software,
alterando o resultado do estudo.
6.3 Avaliação qualitativa
A avaliação qualitativa consistiu em verificar de maneira menos formal a
satisfação dos sujeitos ao realizar as tarefas utilizando as duas ferramentas. Os
sujeitos responderam um questionário que continha cinco perguntas de
satisfação e uma pergunta relacionada ao tempo que os usuários levaram para
preparar o ambiente de testes utilizando cada uma das ferramentas durante o
102
treinamento fornecido no 2º dia. A Tabela 27 apresenta as perguntas feitas aos
sujeitos.
Código
Pergunta
P6
Qual a ferramenta que lhe ofereceu
melhor
suporte
para
realizar
a
definição das regras de design?
P7
Qual a ferramenta que lhe ofereceu
melhor suporte para realizar a criação
dos
testes
de
comportamento
excepcional?
P8
Qual a ferramenta que lhe ofereceu
um log mais detalhado e legível?
P9
Qual abordagem você acha que lhe
forneceria uma documentação mais
legível
sobre
o
comportamento
excepcional da aplicação?
P10
Qual a ferramenta, no geral, que lhe
propiciou uma melhor realização de
teste de comportamento excepcional?
P11
Quanto tempo você levou para montar
o ambiente da ferramenta no seu
projeto de Software (no segundo dia)?
Tabela 27: Perguntas da avaliação qualitativa
Para cada uma das perguntas de satisfação (P6 a P10) obteve-se as
respostas descritas nos gráficos exibidos a partir da Figura 35 até Figura 39 a
seguir:
103
Figura 35: Gráfico em barras com o resultado da P6
Figura 36: Gráfico em barras com o resultado da P7
Figura 37: Gráfico em barras com o resultado da P8
104
Figura 38: Gráfico em barras com o resultado da P9
Figura 39: Gráfico em barras com o resultado da P10
No geral, observa-se que houve uma maior satisfação com a utilização da
VITTAE em relação a JUnitE. Por exemplo, o resultado da P5 provavelmente se
deu devido ao fato de que a utilização do XML Schema permitiu que os sujeitos
utilizassem o editor de XML da ferramenta Eclipse [Eclipse 2012] com
características de autocomplete e visualização da estrutura utilizando uma
interface gráfica, diferentemente da JUnitE onde as regras são descritas dentro
de um vetor de objetos do tipo String. O resultado das perguntas P7, P8 e P10
provavelmente foi devido ao nível de amadurecimento da ferramenta, que possui
uma visualização do log no estilo já conhecido do JUnit e fornece toda uma
infraestrutura para auxiliar na criação dos testes através da geração dos mocks
105
e do esqueleto de testes, enquanto a JUnitE fornece o log em um arquivo texto,
utilizando uma sintaxe simbólica para representar o fluxo da exceção durante o
teste. No caso da P9 a utilização de um arquivo separado dos testes para
definição das regras parece permitir não só que regras sejam adicionadas sem
a necessidade de recompilar os testes como também que o arquivo das regras
sirva como um dos documentos que descreve a arquitetura do sistema,
permitindo que os desenvolvedores e projetistas tenham um melhor
entendimento do comportamento excepcional do sistema como um todo,
enquanto a utilização da JUnitE não permite essa concentração das regras em
um único documento ou arquivo.
No que diz respeito ao tempo gasto para preparação do ambiente (P11), a
medição foi realizada no dia 2 por 10 alunos (apenas ressaltando que estes
sujeitos não estavam organizados de acordo com o quadrado latino, como
ocorreu na análise quantitativa) que executaram a configuração do ambiente de
cada ferramenta no mesmo sistema, com base em um tutorial fornecido. Os
resultados são descritos na Tabela 28.
Sujeito Ferramenta Tempo(s)
1
Vittae
758
2
Vittae
120
3
Vittae
377
4
Vittae
539
5
Vittae
392
6
Vittae
402
7
Vittae
840
8
Vittae
720
9
Vittae
360
10
Vittae
1200
1
JUnitE
1226
2
JUnitE
240
3
JUnitE
607
4
JUnitE
524
5
JUnitE
846
6
JUnitE
993
7
JUnitE
600
8
JUnitE
840
9
JUnitE
720
10
JUnitE
1800
Tabela 28: Tempo para preparação do ambiente (P11)
106
Figura 40: Gráfico Box-Plot referente ao tempo para a configuração do ambiente para
as duas ferramentas
107
Figura 41: Desempenho individual dos sujeitos ao configurar o ambiente das
ferramentas.
Embora os resultados não tenham relevância estatística, é facilmente
observável através das Figuras 40 e 41 que os sujeitos realizaram a
configuração do ambiente da VITTAE em um tempo menor que os que
realizaram a configuração da JUnitE. Isso provavelmente se deve ao fato de que
a VITTAE possui uma opção direta para seleção do projeto Java, onde ela
automaticamente inclui as libs, cria os pacotes de teste e os arquivos de regras
de design. Já o processo utilizando a JunitE é totalmente manual.
6.3.1
Ameaças à validade do estudo
Uma das ameaças à validade do estudo diz respeito à marcação do
tempo para realização da configuração do ambiente. Essa marcação foi
realizada manualmente pelos alunos e está passível de erro. Além disso, visto
que as perguntas realizadas foram bastante subjetivas, e os alunos tiveram
contato com as ferramentas em apenas três ocasiões, um maior período de
contato com as ferramentas poderia alterar o resultado do estudo.
108
6.4 Discussões
Este capítulo apresentou um estudo experimental avaliativo para comparar
as ferramentas resultantes das abordagens de Sales Júnior e Coelho [Sales
Júnior and Coelho 2011], a VITTAE, e de Di Bernardo et al. [Di Bernardo et al.
2011], a JUnitE. Foram realizadas duas análises: uma quantitativa mais formal,
utilizando um experimento controlado organizado através do quadrado latino, e
um qualitativo menos formal baseado em questionários. O estudo quantitativo
mostrou que há indícios de que os resultados obtidos pelas duas ferramentas
são equivalentes no contexto em que foram aplicadas. O estudo qualitativo,
embora não tenha relevância estatística, deu indícios de que a VITTAE aparenta
ser mais amigável para o usuário. No próximo capítulo falaremos sobre os
trabalhos relacionados.
109
7 Trabalhos relacionados
Nesta seção são apresentados os trabalhos de pesquisa diretamente
relacionados com esta dissertação. Podemos organizar tais pesquisas em
quatro categorias: (i) abordagens de verificação estática do código base de
tratamento de exceção (seção 7.1); (ii) abordagens de testes do código base de
tratamento de exceção (seção 7.2); (iii) abordagens para definição de regras de
design para tratamento de exceção (seção 7.3); (iv) processos de
desenvolvimento que integram exceções dentro do inteiro ciclo de vida do
desenvolvimento de software (secão 7.4). Por fim, na seção 7.5 realizamos o
fechamento do capítulo.
7.1 Abordagens de verificação estática
O primeiro trabalho de que se tem notícia a propor a ideia de utilizar
análise estática para verificação de fluxos excepcionais foi o trabalho de
Schaefer e Bundy [Schaefer and Bundy 1993] que propôs uma ferramenta de
análise estática para tentar identificar violações do projeto de tratamento de
exceções.
Além destes, na literatura diversos trabalhos propõem soluções baseadas
em análise estática do fluxo excepcional, dentre os quais podemos citar Coelho
et al. [Coelho et al 2008a], Fu e Ryder [Fu e Ryder 2007], Robillard e Murphy
[Robillard e Murphy 2003], Fahndrich et al. [Fahndrich et al. 1998] e Chang et al.
[Chang et al 2001].
Uma ferramenta de análise estática percorre o gráfico de chamadas do
programa e estima os fluxos de exceção que irão ocorrer em tempo de
execução. Chang et al. [Chang et al 2001] usa análise estática para detectar
especificações de exceções (throws clauses) ou tratadores genéricos ou
desnecessários. Fahndrich et al. [Fahndrich et al. 1998] desenvolveu um
conjunto de ferramentas para descobrir exceções não tratadas em programas
ML usando análise de programas baseada em restrições. Robillard e Murphy
[Robillard and Murphy 2003] propuseram uma abordagem de design para
simplificar a estrutura de exceções em programas Java e desenvolveram a
ferramenta JEX. Esta ferramenta extrai informações sobre o fluxo de exceções,
110
provendo uma visão dos tipos de exceções que podem surgir de diferentes
pontos do programa e dos tratadores existentes no sistema. Fu e Ryder [Fu and
Ryder 2007] propuseram a ideia de análise da cadeia de exceções como um
meio de reduzir o tamanho dos resultados obtidos por ferramentas como a JEX.
Essa ideia tem o objetivo de reduzir o número de caminhos de propagação de
exceções por meio da fusão de caminhos que estão relacionados.
Os trabalhos de Csallner e Smaragdakis [Csallner and Smaragdakis
2005; Csallner and Smaragdakis 2006] propõem uma ferramenta chamada
“Check ’n’ Crash” que combina análise estática e geração de testes.
Algumas ferramentas têm como objetivo prover uma visualização útil do
comportamento excepcional. JEXVIS [Sinha and Harrold 2000] utiliza
informação textual para gerar uma visualização interativa da estrutura de
exceções. EXTEST [Fu and Ryder 2007] apresenta uma visão em árvore dos
tratadores de exceção, seus gatilhos e seus caminhos de propagação. EXPO
[Sinha and Harrold 2004] computa estatísticas de tratamento de exceções e
visualiza o contexto dos pares lançador-tratador como um gráfico de fluxo. A
ferramenta EHANCE [Sha, Gorg and Harrold 2008] apresenta quatro diferentes
visões das exceções em um sistema. A principal limitação das abordagens de
análise estática, bem como de suas ferramentas de visualização, é que elas
suportam a descoberta, mas não a execução dos fluxos excepcionais. Se os
desenvolvedores empregam tais técnicas para entender a estrutura de exceções
de um sistema e depois o sistema for modificado, uma grande quantidade de
esforço pode ser gasto para descobrir como essas modificações afetam os
caminhos das exceções. Ao mesmo tempo, devido a fatores como herança e
polimorfismo, essas ferramentas frequentemente reportam falsos positivos e
falsos negativos. Finalmente, a análise estática é útil somente quando o sistema
já está implementado. Portanto, ela não pode ser útil para ajudar nas atividades
de especificação e design.
7.2 Abordagens de teste
O framework JUnit [JUnit 2012], vastamente utilizado para testes unitários
automáticos, permite aos desenvolvedores verificarem quando um trecho de
código lança uma determinada exceção durante a execução de um caso de
111
teste. No entanto, este framework não oferece nenhuma construção que habilite
o desenvolvedor a verificar tanto se um caminho de uma exceção específica foi
percorrido ou um tratador especifico capturou uma dada exceção.
Sales Júnior, Coelho e Lustosa Neto [Sales Júnior, Coelho and Lustosa
Neto 2011] propõem uma abordagem para suportar a definição e teste dos
chamados contratos de tratamento de exceção. Um contrato de tratamento de
exceção especifica os elementos responsáveis por lançar exceções e os
elementos responsáveis por tratá-las. Este trabalho é uma evolução desta
abordagem, onde esses contratos representam as regras de design abordadas
nesta dissertação.
Como explicado na Seção 6.1, o trabalho de Di Bernardo et al. [Di
Bernardo et al. 2011], propôs uma abordagem baseada na ferramenta JUnitE.
Essa ferramenta estende o framework JUnit por permitir testar se uma
determinada seção teve o seu fluxo de acordo com o que foi especificado dentro
do próprio teste. Nossa abordagem difere desta visto que, embora o objetivo das
duas seja verificar se a exceção teve o seu fluxo de acordo com certas regras e
ambas também façam isso através de testes JUnit, tais regras em nossa
abordagem não são armazenadas dentro do teste em si. Elas são armazenadas
em um documento que é verificado durante o teste, permitindo que mais de um
contrato seja verificado durante a realização do teste, diferentemente da JUnitE
que só permite a verificação de um contrato por vez e não exigindo que o código
de teste seja recompilado caso ocorra uma mudança no contrato. Além disso, a
VITTAE, ferramenta que apoia nossa abordagem, gera o mock aspect, que força
o lançamento da exceção durante o teste, a partir do próprio contrato, sem a
necessidade de replicação de código ao informar uma anotação a mais como a
@ForceException. Outro diferencial da VITTAE em relação a JUnitE é a
possibilidade do uso dos wildcards. O wildcard permite que os contratos possam
representar regras de design em nível arquitetural, não somente no nível de
métodos de classes. Além disso, o uso de wildcards permite que a abordagem
seja utilizada também em sistemas orientados a aspectos, o que não ocorre com
a JUnitE.
112
7.3 Abordagens de regras de design para tratamento de
exceção
O trabalho de Neto [Neto 2010] propôs uma linguagem para especificar
regras de design (LSD) tanto em sistemas orientados a objetos quanto em
sistemas orientados a aspectos, baseado na ideia de design por contrato. Essa
linguagem permite desacoplar classes e aspectos, melhorando a modularidade
dos sistemas através da criação de regras de interação entre classes e
aspectos. Essa linguagem possui uma sintaxe similar ao Java/AspectJ e provê
uma ferramenta chamada COLA, que nada mais é do que uma extensão do
compilador AspectJ, que verifica em tempo de compilação se as regras de
design estão sendo obedecidas. Essas regras podem ser verificadas nas
diversas fases do desenvolvimento.
No que diz respeito ao comportamento excepcional, a LSD possui
construções que especificam somente quais exceções o método deve poder
lançar. A Tabela 29 mostra as construções da LSD para o comportamento
excepcional.
Expressão
Descrição
void m()
Nenhuma restrição quanto a exceção
void m() throws
O método m() não pode lançar
nenhuma exceção
void m() throws E
O
método
m()
somente
lança
exceções do tipo E. Nenhuma outra
exceção, nem subclasses de E,
podem ser lançadas
void m() throws E,*
O método m() somente pode lançar
exceções do tipo E e mais um outro
tipo de exceção
void m() throws !E,!E
O método m() somente pode lançar
dois tipos de exceção, mas nenhuma
delas pode ser do tipo E.
113
Tabela 29: Expressões da LSD destinadas ao comportamento excepcional
Como pode ser observado, tais construções em comparação com as
construções fornecidas pela nossa abordagem são bastante limitadas, visto que
só especificam quais exceções certo método pode ou não lançar. A nossa
abordagem permite declarar e verificar se a exceção está sendo tratada
corretamente, possuindo construções mais ricas para representar o fluxo
excepcional.
O trabalho de Terra e Valente [Terra and Valente 2008] propõe a DCL
(dependency constraint language), uma linguagem declarativa que permite
definir regras arquiteturais que restringe as dependências entre as partes do
sistema. A DCL possui construções que indicam quais dependências são
aceitáveis e quais não, e possui uma ferramenta integrada ao Eclipse (dclcheck)
que permite verificar se tais restrições estão sendo obedecidas.
No que diz respeito ao comportamento excepcional a DCL possui as
seguintes construções, indicadas na Tabela 30.
Expressão
Descrição
only A can-throw B
Somente
métodos
de
classes
declaradas no módulo A podem
lançar
exceções
declaradas
no
módulo B
A can-only-throw B
Os métodos das classes declaradas
no módulo A podem somente lançar
Exceções declaradas no módulo B.
A cannot-throw B
Os métodos das classes declaradas
no módulo A não podem lançar
exceções declaradas no módulo B.
A must-throw B
Todos
os
métodos
de
classes
declaradas no módulo A precisam
declarar que podem lançar exceções
que foram declaradas no módulo B.
Tabela 30: Construções da DCL voltadas para o fluxo excepcional
114
Como pode ser observado a DCL somente permite expressar que
métodos podem ou devem lançar ou não certas exceções, tornado sua
expressividade restrita para fluxos excepcionais.
O trabalho de Cacho et al. [Cacho et al. 2008] propõe a EJFlow, que é
uma extensão do AspectJ, específica de domínio para modularização do código
de tratamento de exceções, permitindo aos programadores selecionar o fluxo de
uma exceção através de um canal explícito de exceção. Já o trabalho de Silva e
Castor [Silva and Castor 2013] propõe uma especificação de interface de
exceção chamada EPC (Exception Propagation Channel), complementando as
interfaces de exceção Java. Apresenta uma extensão da linguagem Java
chamada EPiC-Java, incorporando a abordagem proposta.
7.4 Processos de Desenvolvimento
Algumas
abordagens
propõem
a
definição
do
comportamento
excepcional dos sistemas ao longo de todas as atividades do desenvolvimento
de software, antes da implementação em si. Algumas delas incorporam
atividades relacionadas com tratamento de exceções dentro de atividades já
existentes nas metodologias de desenvolvimento de software [Rubira et al.
2005], enquanto outras têm como alvo atividades de desenvolvimento
específicas [Shui, Mustafiz and Kienzle 2006]. Todas elas, entretanto, têm uma
forte ênfase em produzir documentação sobre o comportamento excepcional.
Essa característica de documentação densa sobre o comportamento
excepcional pode ser considerada como a maior limitação destas abordagens
visto que é bem conhecido que os desenvolvedores frequentemente não
mantém a documentação atualizada de acordo com a implementação do
sistema [Beck and Andres 2004].
Embora as regras de design de nossa abordagem possam ser
consideradas como um tipo de documentação estruturada que podem ser
utilizadas em estágios iniciais do processo de desenvolvimento, o principal foco
é na realização de testes. Acreditamos, entretanto, que a abordagem proposta
neste trabalho pode ser facilmente combinada com trabalhos prévios como uma
maneira de fazer com que tais metodologias sejam menos centradas em
115
documentação. O capítulo a seguir conclui este trabalho e apresenta o
planejamento para a elaboração da dissertação.
7.5 Discussões
Este capítulo apresentou um os trabalhos de pesquisa diretamente
relacionados com esta dissertação. Os trabalhos foram organizados em (i)
abordagens de verificação estática, (ii) abordagens de teste, (iii) abordagens de
regras de design para tratamento de exceções e (iv) processos de
desenvolvimento. O próximo capítulo apresentará as considerações finais desta
dissertação.
116
8 Considerações Finais
Os mecanismos de tratamentos de exceção vêm sendo utilizados como
uma forma de auxiliar os desenvolvedores a construir aplicações mais robustas
[Parnas and Wurges 1976]. Estudos têm mostrado que, embora uma grande
quantidade código seja dedicada à descoberta e tratamento de erros, as
manifestações de erros e exceções tem recebido pouca atenção em outros
estágios do desenvolvimento como design e testes [Rubira et al. 2005, Kienzle
2008]. Os desenvolvedores tendem a concentrar suas atividades no design do
comportamento
normal
da
aplicação
e
esquecem-se
do
design
do
comportamento do tratamento de exceções [Sha, Gerg and Harrold 2008].
Visto que as regras de design definem padrões arquiteturais que devem
ser obedecidos durante a implementação do comportamento normal da
aplicação, faz-se necessário que existam regras de design específicas para o
comportamento excepcional. Embora trabalhos proponham abordagens para
especificação e verificação de regras de design dos sistemas [Brunet, Neto and
Figueredo 2009, Neto 2010], uma limitação comum dessas abordagens é que
elas não proveem maneiras apropriadas de definir regras de design relacionadas
com fluxos de exceção. Outras abordagens permitem a especificação do
comportamento
excepcional,
porém
estão
fortemente
baseadas
em
documentação [Rubira et al. 2005, Kienzle 2008], impondo uma sobrecarga
indesejável em ambientes ágeis, que incentivam a automação de testes e o
desenvolvimento dirigido a testes.
Neste trabalho nós apresentamos uma abordagem sistemática com o
objetivo de permitir a especificação e verificação de regras de design voltadas
para o comportamento excepcional de aplicações Java e AspectJ. Essa
abordagem se constitui na seguinte sequência de atividades realizadas para
tentar garantir que as regras de design de comportamento excepcional não
sejam degradadas em relação ao que está implementado na aplicação: (i) definir
as regras de design excepcionais da aplicação; (ii) projetar e automatizar os
testes de regras de design excepcionais; (iii) executar os testes; (iv) avaliar os
117
resultados, e caso necessário, executar novamente toda a sequência para
refletir atualizações no sistema ou nas regras de design.
Para apoiar a abordagem foi desenvolvida uma ferramenta, a VITTAE
(Verification and Information Tool to Analyze Exceptions), que permite dentre
outras coisas: (i) geração automática do template de testes baseado nas regras
de design excepcionais especificadas, (ii) geração de mock aspects que forçam
o lançamento da exceção durante a execução dos testes e (iii) verificação das
regras de design excepcionais .
Foi proposto e realizado um estudo de caso preliminar para comprovar a
eficácia da ferramenta sobre várias versões de uma linha de produto de
software. Este estudo mostrou que durante a evolução de um sistema em suas
várias versões orientadas a aspectos e orientadas a objetos, a política de
tratamento de exceções for modificada e que ocorreu um problema sério, onde
exceções foram transformadas em exceções não checadas sem um tratamento
específico ou até mesmo chegaram a ser disparadas alcançando o usuário final.
Além deste também foi realizado um estudo avaliativo comparando a
VITTAE com a JUnitE [Di Bernardo et al.
2011] que avaliou em termos
qualitativos e quantitativos as duas ferramentas utilizando metodologias de
engenharia de software experimental. Este estudo mostrou que ambas as
ferramentas são bem sucedidas em permitir a especificação e verificação de
regras de comportamento excepcional, aparentemente são equivalentes no que
diz respeito a sua eficiência, mas possuem diferenças quanto à qualidade de
sua utilização pelos usuários.
8.1 Publicações relacionadas
A pesquisa que resultou nesta dissertação permitiu a publicação de 3
artigos até o momento:
SALES JUNIOR, R.; COELHO, R. S. ; LUSTOSA NETO,
V. .Exception-Aware AO Refactoring. In: IV Latin American
Workshop on Aspect Oriented Programming, 2010, Salvador.
Anais do CBSoft, 2010 – Este artigo faz uma apresentação
118
preliminar da abordagem proposta e da ferramenta VITTAE (que
na época era conhecida como FEAR).
SALES JUNIOR, R. ; COELHO, R. S. Preserving the Exception
Handling Design Rules in Software Product Line Context: A
Practical Approach. In: I Workshop on Exception Handling in
Contemporary
Proceedings
Software
of
I
Systems
Workshop
on
(EHCOS
Exception
2011),
2011.
Handling
in
Contemporary Software Systems (EHCOS 2011), 2011 – Este
artigo apresentou evoluções realizadas na abordagem proposta e
o estudo de caso preliminar aplicado sobre uma linha de produto
de software.
Di Bernardo, R., Sales Júnior, R., Castor, F. Coelho, R., Cacho,
N., Soares S. Agile Testing of Exceptional Behavior . 25th
Brazilian Symposium on Software Engineering (SBES 2011). São
Paulo, September 28-30, 2011 DOI: 10.1109/SBES.2011.28. –
Este artigo apresentou a idéia da utilização de uma abordagem
leve para teste de comportamento excepcional e a ferramenta
JUnitE.
8.2 Trabalhos futuros
Propomos algumas sugestões de trabalhos futuros. Por exemplo,
evoluções em nossa abordagem seriam interessantes como: (i) geração
totalmente automatizada dos casos de teste baseados nas regras de design,
talvez utilizando técnicas de análise estática para suportar o preenchimento dos
templates dos casos de teste; (ii) associação entre cada regra de design e um
teste específico, talvez utilizando anotações no teste e atributos no arquivo xml
para signalers e handlers, para permitir que a ferramenta também verifique se o
signaler realmente lançou a exceção definida na regra de design excepcional;
(iii) construção de um plugin para permitir uma melhor integração com a IDE
Eclipse [Eclipse 2012] e consequentemente uma utilização mais ampla da
ferramenta nos ambientes de desenvolvimento reais.
119
Além disso, visto que os resultados do estudo avaliativo quantitativo não
deram indícios estatísticos suficientes de diferenças entre as abordagens
baseadas nas ferramentas VITTAE e JUnitE – muito provavelmente devido a
pequena quantidade de sujeitos e simplicidade dos sistemas-alvo, mas a
avaliação qualitativa mostrou diferenças, seria interessante a realização de um
estudo avaliativo mais aprofundado em um sistema real, permitindo uma análise
estatística mais relevante.
120
Referências
[Accioly 2012] Accioly P. Comparing Testing Strategies for Software Product
Line. Dissertação (Mestrado) - UFPE, Recife. 2012
[Alencar et al. 2010] Alencar F., Castro J., Lucena M., Santos E., Silva C.,
Araújo J. and Moreira A. Towards modular i* models. In SAC '10: Proceedings
of the 2010 ACM Symposium on Applied Computing, pages 292-297, New
York, NY, USA, 2010. ACM
[Alencar et al. 2010] Alencar F., Castro J., Lucena M., Santos E., Silva C.,
Araújo J. and Moreira A. Towards modular i* models. In SAC '10: Proceedings
of the 2010 ACM Symposium on Applied Computing, pages 292-297, New
York, NY, USA, 2010. ACM
[Allen 2001] Allen, Eric. Bug Patterns: An introduction, 1 Feb. 2001.
Disponível
em:
http://www.ibm.com/developerworks/java/library/j-
diag1/index.html . Acesso em 18/04/2012
[Anderson and Lee 1981] ANDERSON, T.; LEE, P. A. Fault tolerance principles and practice. Englewood Cliffs, Prentice-Hall, 1981.
[Aranha et al. 2009] ARANHA, E.; FERRAZ, C.; BORBA, P. Projeto de
Experimentos em Engenharia de Software. In: Simpósio Brasileiro de
Engenharia de Software, 2009.
[AspectJ
2012]
AspectJ
Project.
Disponível
em:
http://www.eclipse.org/aspectj/. Acessado em 12 de Março de 2012.
[Avizienis 1997] Avizienis A. Toward Systematic Design of Fault-Tolerant
Systems. Computer, 30(4):5158 , 1997.
[Avizienis 1998] AVIZIENIS, A. Infraestructure-based design of fault-tolerant
systems. In: Proceedings of the IFIP International Workshop on Dependable
Computing and its Applications. DCIA 98, Johannesburg, South Africa,
January 12-14,1998. p. 51-69.
[Avizienis et al 2004] A. Avizienis, J.-C. Laprie, B. Randell, and C. Landwehr,
"Basic Concepts and Taxonomy of Dependable and Secure Computing,"
121
IEEE Transactions on Dependable and Secure Computing, vol. 1, pp. 11-33,
2004.
[Baldwin and Clark 1999] Baldwin, Carliss Y. e Clark, Carliss Y. Design
Rules: The Power of Modularity. Volume 1. MIT Press, Cambridge, MA, USA,
1999.
[Barbosa et al. 2000] BARBOSA, E.; MALDONADO, J.C.; VINCENZI, A.M.R.;
DELAMARO, M.E; SOUZA, S.R.S. e JINO, M.. “Introdução ao Teste de
Software. XIV Simpósio Brasileiro de Engenharia de Software”, 2000.
[Basili 1995] Basili, V.R. “Applying the GQM paradigma in the experience
factory”, in FENTON, N., WHITY, R., and IIZUKA, Y. (Eds.): “Software Quality
Assurance and Measurement” (Thomson Computer Press, London 1995), pp.
23-37.
[Basili, Caldiera and Rombach 1994] Victor R. Basili, Gianluigi Caldiera, and
H. Dieter Rombach. The Goal Question Metric approach. In Encyclopedia of
Software Engineering, pages 528–532. John Wiley and Sons, 1994.
[Beck 2003] Beck, K. Test-Driven Development by Example, Addison Wesley
- Vaseem, 2003
[Beck and Andres 2004] Beck K. and Andres C. Extreme Programming
Explained: Embrace Change (2nd Edition). Addison-Wesley Professional,
2004.
[Belady 1981] Belady, L. (1981). Foreword. In Software Design: Methods and
Techniques (L.J. Peters, author). Yourdon Press
[Bianchi et al. 2001] Alessandro Bianchi, Danilo Caivano, Filippo Lanubile,
and Giuseppe Visaggio. 2001. Evaluating Software Degradation through
Entropy. In Proceedings of the 7th International Symposium on Software
Metrics (METRICS '01). IEEE Computer Society, Washington, DC, USA,
210-.
[Box, Hunter and Hunter 2005] George E. P. Box, J. Stuart Hunter, and
William G. Hunter. Statistics for experimenters : design, innovation, and
discovery. Wiley-Interscience, 2005.
122
[Brunet, Neto and Figueredo 2009] Brunet, J., Guerredo, D. and Figueiredo
J. (2009). “Design Tests: An approach to programmatically check your code
against design rules”, In 31st International Conference on Software
Engineering (ICSE 2009), New Ideas and Emerging Results.
[Cabral and Marques 2007] Cabral, B. and Marques, P. Exception Handling:
A Field Study in Java and .NET. In ECOOP 2007 - 21st European Conference
Object-Oriented Programming, vol. 4609 of Lecture Notes in Computer
Science, pp. 151-175. Springer. 2007.
[Cacho et al. 2008] Nelio Cacho, Fernando Castor Filho, Alessandro Garcia
e Eduardo Figueiredo. 2008. EJFlow: taming exceptional control flows in
aspect-oriented programming. In Proceedings of the 7th international
conference on Aspect-oriented software development (AOSD 2008). Páginas
72-83.
[Cactus 2012] The Jakarta Foundation. Cactus, a Thorn in Your Bug’s Side.
Disponível em: http://jakarta.apache.org/cactus. Acessado em 19/03/2012
[Castor et al. 2006] Castor Filho, F.; Cacho, N.; Figueiredo, E.; Maranhão, R.;
Garcia, A. and Rubira, C.: Exceptions and aspects: the devil is in the details.
In Proc. SIGSOFT FSE 2006, 152-162.
[Castor, Garcia and Rubira 2007] Castor Filho, F., Garcia, A., Rubira, C.,
Extracting Error Handling to Aspects: A Cookbook. In: Proceedings of the
IEEE International Conference on Software Maitenance (ICSM), Paris,
France, 2007, p.134-143.
[Chang et al 2001] Chang, B.M. et al. Interprocedural exception analysis for
java. In Proceedings of 16th ACM SAC, pp. 620-625, 2001.
[Chess and West 2007] B. Chess and J. West, Secure Programming with
Static Analysis, 1st ed. Addison-Wesley Professional, Jul. 2007.
[Clements and Northrop 2002] Clements, P. and Northrop, L. Software
Product Lines: Practices and Patterns. Addison-Wesley, 2002.
[Coelho et al 2008a] Coelho R et al. “Assessing the impacts of Aspects on
Exception Flows: An Exploratory Study”. In Proc. 22 nd ECOOP, 207-234,
2008
123
[Coelho et al. 2008b] R. Coelho, A. Rashid, U. Kulesza, A. von Staa, C.
Lucena, and J. Noble. Exception handling bug patterns in aspect oriented
programs. In Proceedings of the 15th Conference on Pattern Languages of
Programs, pages 1–19, Chicago, USA, October 2008
[Coelho et al. 2008c] Coelho, R, Awais, R., Staa, A.v., Noble, J., Kulesza, U.,
Lucena, C., A Catalogue of Bug Patterns for Exception Handling in AspectOriented Programs, Conference on Pattern Languages of Programs
(PLoP’08), 2008.
[Collofello 1988] Collofello J. S., “Introduction to Software Verification and
Validation”,
SEI-CM-13-1.1,Software
Engineering
Institute,
Pittsburgh,P.A.,USA.
[Craig and Jaskiel 2002] CRAIG, R.D., JASKIEL, S. P., “Systematic Software
Testing”, Artech House Publishers, Boston, 2002.
[Cristian 1982] Cristian, F. Exception handling and software fault
tolerance.IEEE Trans. Comput. 31(6):531540, 1982.
[Cristian 1989] F. Cristian, “Exception Handling,” in Dependability of Resilient
Computers (ed. T. Anderson), Blackwell Scientific Publications, pp.68-97,
1989.
[Cristian 1994] Cristian, F. Exception Handling and Tolerance of Software
Faults. In Lyu, M.R. (ed.): Software Fault Tolerance. Wiley, 81-108, 1994.
[Csallner and Smaragdakis 2005] C. Csallner and Y. Smaragdakis. Check’n
Crash: Combining Static Checking and Testing. In Proceedings of ICSE’2005
(27th International Conference on Software Engineering). ACM, May 2005.
[Csallner and Smaragdakis 2006] C. Csallner and Y. Smaragdakis. DSDcrasher: Ahybrid analysis tool for bug finding. In proceedings of the
International Symposium on Software Testing and Analysis (ISSTA), 2006.
[Di Bernardo et al. 2011] Di Bernardo, R., Sales Júnior, R., Castor, F. Coelho,
R., Cacho, N., Soares S. Agile Testing of Exceptional Behavior . 25th
Brazilian Symposium on Software Engineering (SBES 2011). São Paulo,
September 28-30, 2011 DOI: 10.1109/SBES.2011.28.
124
[Eclipse 2012] Eclipse Project. Disponível em: http://www.eclipse.org.
Acessado em 12 de Março de 2012.
[Fahndrich et al. 1998] Fahndrich, M. et al. Tracking down exceptions in
standard ML. Technical Report CSD-98-996, University of California,
Berkeley, 1998.
[Fewster and Graham 1999] Mark Fewster & Dorothy Graham (1999).
Software Test Automation. ACM Press/Addison-Wesley. ISBN 978-0-20133140-0.
[Figueiredo et al. 2008] Figueiredo, E.; Cacho, N.; Sant'Anna, C.; Monteiro,
M.; Kulesza, U.; Garcia, A.; Soares, S.; Ferrari, F.; Khan, S.; Dantas,
F.; ,Evolving software product lines with aspects,"Software Engineering,
2008. ICSE'08. ACM/IEEE 30th International Conference on",,,261270,2008,IEEE
[Freeman and Craig 2001] Mcknnon., Freeman, S., and CRAIG, P.
EndoTesting:
Unit
Testing
with
Mock
Objects.
In:
EXTREME
PROGRAMMING EXAMINED, Beck, K. Addison Wesley, 2001. p. 287-301.
[Freese 2002] Freese, Tammo: EasyMock: Dynamic Mock Objects for JUnit.
In: MARCHESI, M. (Hrsg.) ; SUCCI, G. (Hrsg.): Proceedings of the 3nd
International Conference on Extreme Programming and Flexible Processes
in Software Engineering (XP2002), 2002.
[Freese and Tammo 2002] Freese, Tammo: EasyMock: Dynamic Mock
Objects for JUnit. In: MARCHESI, M. (Hrsg.) ; SUCCI, G. (Hrsg.):
Proceedings of the 3nd International Conference on Extreme Programming
and Flexible Processes in Software Engineering (XP2002), 2002.
[Fu and Ryder 2007] Fu, C. and Ryder, B. G. Exception-Chain Analysis:
Revealing Exception Handling Architecture in Java Server Applications. In
Proceedings of ICSE’2007, 230-239, 2007.
[Garcia et al. 2001] Garcia et al. A comparative Study of Exception Handling
Mechanisms for Building Dependable Object-Oriented Software. Journal of
Systems and Software, Elsevier, v. 59, n.6, Nov. 2001, p. 197-222.
125
[Garcia et al. 2011] Garcia, I.; Soares, E.; Cacho, N. Visualizando a Evolução
do Comportamento Excepcional em Aplicações Multi-linguagem com
eFlowMining.
[IEEE 1983] Hantler S. L. and J. C. King. “An Introduction to IEEE. Standard
Glossary of Software Engineering Proving the Correctness of Programs.”
ACM Com- Terminology. ANSI/IEEEStd729-1983, Institute of puting Surveys
8 Electrical and Electronics Engineers, 1983.
[Java
2012]
Java
Programing
Language.
Disponível
em:
http://www.java.com. Acessado em 10 de Dezembro de 2012.
[JMP 2012] Junit. Disponível em http://www.jmp.com/, acessado em 12 de
Dezembro de 2012
[JUnit 2012] Junit. Disponível em http://www.junit.org/, acessado em 12 de
Março de 2012
[Kienzle 2008] Kienzle, J. On exceptions and the software development life
cycle. In Proceedings of the 4th international workshop on Exception
handling. ACM, New York, NY, USA, 2008
[Kolawa and Huizinga 2007] Kolawa, Adam; Huizinga, Dorota (2007).
Automated Defect Prevention: Best Practices in Software Management.
Wiley-IEEE Computer Society Press. p. 74. ISBN 0-470-04212-5.
[Kruchten et al. 2006] Kruchten, P., Lago, P., and van Vliet, H. (2006).
Building up and reasoning about architectural knowledge. In Hofmeister, C.,
Crnkovic, I., and Reussner, R., editors QoSA, volume 4214 of Lecture Notes
in Computer Science, pages 43-58. Springer.
[Laddad 2003] Ramnivas Laddad. AspectJ in Actin: Practical Aspect-Oriented
Programming. Maning Publications, July 2003.
[Laprie 1985] J.C. Laprie. Dependable computing and fault tolerance:
concepts and terminology. In Proc. 15th IEEE Int. Symp. on Fault-Tolerant
Computing (FTCS-15), Ann Arbor, June 1985, pp. 2-11
126
[McCreery and Phil 2007] McCreery C. and Phil D. The Chi-Square Test.
Tutorial. Oxford Forum, Psychological Paper No. 2007-1, Magdalen College ,
Oxford, 2007
[McCune 2006] McCune, T. Exception-handling antipatterns, 2006. In
Java.Net.
Address:http://today.java.net/pub/a/today/2006/04/06/exceptionhandlingantipatterns.html
[Meudec 2001] Meudec C. ATGen: Automatic test data generation using
constraint logic programming and symbolic execution. Software Testing,
Verification and Reliability 2001; 11(2):81–96.
[Meyer 1996] Meyer B. Applying “Design by Contract”. Computer, 25(10):40–
51, 1992.
[Misra, Kumar and Kumar 2009] Misra, S. C.; Kumar, V.; Kumar, U. 2009.
Identifying some important success factors in adopting agile software
development practices. J. Syst.Softw. 82, 2009.
[Myers 1979] G. J. Myers. The art of software testing. 1979.
[Neto 2010] Neto, A. C. Especifying Design Rules in Aspect-Oriented
Systems, Tese de Doutorado, Recife, 2010
[Palmer and Felsing 2002] Palmer, S. R.; Felsing, J. M. A Practical Guide to
Feature-Driven Development. 1st ed., Prentice-Hall, 2002.
[Parnas 1972] Parnas D. L. On the criteria To be used in Decomposing
System into Modules. Commun, ACM, 15(12):1053-1058, 1972
[Parnas 1994] David Lorge Parnas. 1994. Software aging. In Proceedings of
the 16th international conference on Software enginealdering (ICSE '94).
IEEE Computer Society Press, Los Alamitos, CA, USA, 279-287.
[Partington 2012] Partington , V. Middleware integration testing with JUnit,
Maven
and
VMWare.
Disponível
http://blog.xebia.com/2009/12/middleware-integration-testingmaven- and- vmware-part-1. Acessado em 19/03/2012
127
em:
with-
junit-
[Pressman 2005] PRESSMAN, R. S., “Software Engineering: A Practitioner’s
Approach”, McGraw-Hill, 6th ed, Nova York, NY, 2005.
[Ribeiro and Borba 2010] Ribeiro M. And Borba P. Towards feature
modularization. In SPLASH '10: Proceedings of the ACM international
conference companion on Object oriented programming systems languages
and applications companion (Doctoral Symposium), pages 225-226, New
York, NY, USA, 2010. ACM
[Robillard and Murphy 2003] Robillard, M. P. e Murphy G.C. Static Analysis
to support the evolution of exception structure in object oriented systems.
ACM TOSEM 12(2), 191-221, 2003
[Rocha et al. 2001] ROCHA, A. R. C., MALDONADO, J. C., WEBER, K. C. et
al., “Qualidade de software – Teoria e prática”, Prentice Hall, São Paulo,
2001.
[Rubira et al 2005] Rubira, C. M. F; de Lemos, R;Ferreira, R; Castor Filho, F.
Exception handling in the development of dependable component-based
systems, Softw. – Pract. and Exp. 35 (5), 195–236, 2005.
[Sakia 1992] R. M. Sakia. The box-cox transformation technique: A review.
Journal of the Royal Statistical Society. Series D (The Statistician), 41(2),
1992.
[Sales Júnior and Coelho 2011] SALES JUNIOR, R. ; COELHO, R. S. .
Preserving the Exception Handling Design Rules in Software Product Line
Context: A Practical Approach. In: I Workshop on Exception Handling in
Contemporary Software Systems (EHCOS 2011), 2011. Proceedings of I
Workshop on Exception Handling in Contemporary Software Systems
(EHCOS 2011),, 2011
[Sales Júnior, Coelho and Lustosa Neto 2010] SALES JUNIOR, R.; COELHO,
R. S. ; LUSTOSA NETO, V. .Exception-Aware AO Refactoring. In: IV Latin
American Workshop on Aspect Oriented Programming, 2010, Salvador.
Anais do CBSoft, 2010.
128
[Schaefer and Bundy 1993] Carl F. Schaefer and Gary N. Bundy. Static
analysis of exception handling in Ada. Software—Practice and Experience,
23(10):1157–1174, October 1993.
[Sha, Gorg and Harrold 2008] Shah, H.; Gorg, C. and Harrold, M. J.
Visualization
of
exception
handling constructs
to
support program
understanding. In Proceedings of the 4th ACM symposium on Software
visualization, 2008, 19-28.
[Shah, Gerg and Harrold 2008] Shah, H.; Gerg, C. and Harrold, M. J. Why do
developers neglect exception handling?. In Proceedings of the 4th
international workshop on Exception handling (WEH ’08). ACM, New York,
NY, USA, 62-68, 2008
[Shapiro and Wilk 1965] S. and Wilk M. B. (1965) “An analysis of Variance
Test for Normality (Complete Samples)”, Biometrika, 52, 591-611
[Shui, Mustafiz and Kienzle 2006] Shui, A.; Mustafiz, S., Kienzle, J.:
Exception-Aware Requirements Elicitation with Use Cases. Advanced Topics
in Exception Handling Techniques. Springer, 2006, 221-242.
[Silva and Castor 2013] Thiago Silva and Fernando Castor. New Exception
Interfaces for Java-Like Languages. In Proceedings of the 28th ACM
Symposium on Applied Computing (SAC'2013). Coimbra, Portugal, March
2013. Accepted for publication.
[Sinha and Harrold 2000] Sinha, S., and Harrold, M. J. Analysis and testing of
programs with exception handling constructs. IEEE Transac- tions on
Software Engineering 26(9), 849-871, 2000.
[Sinha and Harrold 2004] Sinha, S., and Harrold, M. J. Automated support for
development, maintenance, and testing in the presence of implicit control
flow. In Proceedings of the 26th International Conference on Software
Engineering, 336-345, 2004.
[Sipser 2005] Sipser, M. Introduction to the Theory of Computation, Second
Edition. New York, NY: Course Technology, 2005.
[Sommerville 2006] Ian Sommerville, Software Engineering, 8a. Edição,
Addison-Wesley, 2006
129
[Sotirov 2005] Sotirov, A.I. (2005). Automatic Vulnerability Detection Using
Static Source-Code Analysis. Ph.D. thesis, University of Alabama.
[Stroustrup et al 2001] B. Stroustrup, A. Koenig, and B. Moo: The C++
Programming Language. Encyclopedia of Software Engineering. Second
edition. Wiley. 2001.
[Terra and Valente 2008] Ricardo Terra; Marco Tulio Valente. Towards a
Dependency Constraint Language to Manage Software Architectures. 2nd
European Conference on Software Architecture (ECSA), LNCS 5292, p. 256263, Springer-Verlag, 2008.
[Torres et al. 2010] Torres M., Kulesza U., Braga R., Masiero P., Pires P.,
Delicato F., Cirilo E., Batista T., Teixeira L., Borba P, and Lucena C. Estudo
comparativo de ferramentas de derivacao dirigidas por modelos: Resultados
preliminares. In Primeiro Workshop Brasileiro de Desenvolvimento Dirigido
por Modelos (WBDDM'2010), Workshop do CBSoft 2010, 2010.
[Turing 1936] Alan Turing. On computable numbers, with an application to the
Entscheidungsproblem, Proceedings of the London Mathematical Society,
Series 2, 42 (1936), pp 230–265.
[van Gurp and Bosh 2002] Jilles van Gurp, Jan Bosch, Design Erosion:
Problems & Causes, Journal of Systems & Software, 61(2), pp. 105-119,
Elsevier, March 2002.
[Wohlin et al 2000] Claes Wohlin, Per Runeson, Martin Hösrt, Magnus C.
Ohlsson, Björn Regnell, and Anders Wesslén. Experimentation in Software
Engineering. Kluwer Academic Publishers, 2000
130
Apêndice A – Artigos Publicados
A seguir são apresentados os artigos publicados pelo autor, cuja evolução
resultou na abordagem proposta neste trabalho.
131
Exception-Aware AO Refactoring
Ricardo J. Sales Júnior
Roberta Coelho
Vicente Pires Lustosa Neto
Departamento de Informática e
Matemática Aplicada (DIMAp)
UFRN, Brasil
Departamento de Informática e
Matemática Aplicada (DIMAp)
UFRN, Brasil
Departamento de Informática e
Matemática Aplicada (DIMAp)
UFRN, Brasil
[email protected]
[email protected]
[email protected]
ABSTRACT
AO refactoring is the process of reorganizing the internal
structure of code by the identification of crosscutting concerns
and representation of such concerns as aspects. As any other
refactoring process, the AO refactoring should preserve the
external behavior of the system. However, we have observed that
current AO refactoring approaches may lead to a set of flaws on
the exception handling code (e.g., uncaught exceptions,
exceptions mistakenly handled), and consequently does not
preserve the exception handling behavior of the refactored
system. Therefore, this paper proposed an extension to the
existing AO refactoring approaches, which enables the developer
to preserve the exception handling behavior of the refactored
system. This approach is based on the definition of exception
handling contracts and a set of test cases to dynamically check
such contracts. Preliminary results show that our approach helps
to detect the flaws that can happen on the exception handling
code of aspectized systems.
Categories and Subject Descriptors
D.1.5 [Software Engineering]: Programming Techniques –
General; D.2.17 [Software Engineering]: Software Construction
- Programming paradigms.
General Terms
Reliability and Languages.
Keywords
Exception handling, aspect-oriented languages, refactoring.
1. INTRODUCTION
Many of the current aspect-oriented systems are generated from
an OO system version by applying a set of well defined aspect
oriented (AO) refactoring steps. Such AO refactoring techniques
[1,2,3,4,5,6] provide clear guidelines for: (i) detecting
crosscutting concerns, (ii) (optionally) preparing the code to be
refactored, and (ii) converting the crosscutting concerns into
aspects.
As any refactoring process, the AO refactoring should preserve
the system behavior after converting the crosscutting concerns to
aspects. However, we have observed that the current approaches
for AO refactoring do not preserve the full system behavior [7].
In a previous empirical study [7], in which we analyzed the
exception flow of three medium sized OO systems and its AO
versions (built through a well defined set of refactoring steps),
we observed a higher evidence of uncaught exceptions [8] and
exceptions mistakenly handled on the AO-refactored versions.
Such recurring flaws on the way exceptions handled inside the
AO versions are evidences that current AO refactoring
approaches did not preserve the exception handling behavior of
the refactored system.
In an exception-aware AO refactoring the developer should be
able to evaluate the consequences of including an aspect into the
system (e.g., where new handlers should be added), otherwise
the aspectized functionalities threaten the refactored system
dependability. In this paper we propose a way of extending
current AO refactoring approaches [1,2,3,4] in order into enable
exception-aware AO refactorings. To achieve such goal, we
provide: (i) clear guide-lines to help developers preserving the
exception handling behavior of the “aspectized systems”, and (ii)
a tool which automatically checks the exception handling
contracts during the execution of JUnit tests.
The remainder of this paper is organized as follows. Section 2
presents an illustrative example which emphasizes the need of
exception-aware AO refactoring approaches. Section 3 details
our approach for preserving the exception handling behavior
during the AO refactoring. Section 4 provides further discussions
and lessons learned. Finally, Section 5 presents some concluding
remarks.
2. MOTIVATING EXAMPLE
When performing an exception-aware AO refactoring a question
arises: Does the crosscutting concern to-be-aspectized signal any
exception? If the answer is yes, the developer needs to deal with
two equally important tasks: (i) enabling the aspect to signal the
exception signaled by the crosscutting concern; and (ii) creating
and Error Handling Aspect [9] to handle the exceptions signaled
by it. If the exception signaled by the crosscutting concern is a
checked exception, due to a limitation of AspectJ language this
exception should be softenized1 or a new unchecked exception with the same semantic of the checked one - should be created
and signaled by the aspect. In both scenarios, the new unchecked
exception or the softenized exception should be handled by the
Error Handling Aspect [9] related to the aspectized crosscutting
concern. Some characteristics of the exception handling code of
the base code, however, may prevent the Error Handling Aspects
of adequately handling the exceptions, as illustrated in the
motivating example below.
1
declare soft is an AspectJ specific construct. It is associated to a
pointcut and wraps any exception thrown on specific join points in a
SoftException, and re-throws it.
1
Figure 1. The Bank system design after AO refactoring.
This section presents a simple illustrative example to point out
some of the exception handling flaws that may arise during an
AO refactoring process. The system presented here is an
information system that supports the management of bank
accounts. Figure 1 presents the aspect-oriented architecture of
this information system (built through a well defined set of
refactoring steps defined at [4]), which is structured according to
the Layered architectural pattern and implement some concerns
as aspects:
•
the PerformanceMonitoring - responsible for
monitoring the performance of every Servlet request;
•
•
the NegativeValueOnOpCheck – which checks
whether the user tries to withdraw, credit or transfer
any negative value.
the NegativeValueOnOpHandler – an Error
Handling Aspect [09] that is responsible for handling
the any instance of the unchecked exception, called
NegativeValueOnOpException, signaled by the
NegativeValueOnOpCheck aspect.
Figure 2. The code snippet of the NegativeBalanceCheck.
One exception handling flaw that can arise during AO
refactoring is known as Exception Stealer bug pattern [10], and
happens when the aspect defined to handle an exception thrown
by a crosscutting concern cannot handle it, because the
exception is prematurely caught by an element on the base code.
In this example the NegativeValueOnOpHandler was created
to handle any instance of NegativeValueOnOpException
thrown by the NegativeValueOnOpCheck as detailed before.
However a catch-all clause defined on the base code will catch
(or “steal”) any instance of NegativeValueOnOpException
before it reaches the NegativeValueOnOpHandler as
illustrated in Figure 3.
NegativeValueOnOpHandler
catch ( TMException exc )
Bank method
catch ( Exception exc )
In this system no Error Handling Aspect was defined to handle
the exceptions thrown by the PerformanceMonitoring aspect,
because every exception thrown by the monitoring concern
should be handled inside the aspect that represents it. In order
words, the monitoring exceptions should not disturb the
exception flows of the system been monitored. Figure 2 presents
a code snippet of NegativeValueOnOpCheck aspect.
NegativeValueOnOpCheck
Advice
a1
Accout method
EX
EX
Legend :
method
advice
advice code
exception propagation
method call
EX
EX
crosscuts
exception instance
public aspect NegativeValueOnOpCheck {
//intercepts withdraw,credit and transfer met
pointcut valueReceiversOps = ...;
before (double value) :
execution(valueReceiversOps) && args(value)
{
if (value < 0){
throw new NegativeValueOnOpException();
}
}
Figure 3. Schematic view of the Exception Stealer bug pattern that
happens in the Bank system.
Figure 4 illustrates the code snippet of Bank class which contains
the
catch-all
clause
that
prevents
the
NegativeValueOnOpHandler from handling any instance of
NegativeValueOnOpException.
}
2
1. public class Bank {
2. ...
3. public credit(Account c, double val) {
4.
try{
5.
// method body
6.
} catch (Exception){
7.
// handles the exception.
8.
}
9. }
Figure 4. A Code snippet of the the Bank class.
We can observe that although a similar problem may happen in
OO programs (i.e., methods may mistakenly handle exceptions
designed to be handled elsewhere), this problem is strengthened
by the fact that some AO development approaches are based on
the obliviousness property. A way to prevent this bug pattern
would be to replace “catch all clauses” by specific catch clauses
(during AO refactoring). However, this solution is still very
fragile, since it depends on code conventions and nothing can
avoid a “catch all clause” to be added after any software
maintenance.
3. Preserving EH Behavior during AO
Refactoring
Refactoring is the process of changing a software system in such
a way that it improves the code’s internal structure, without
altering its external behavior [11]. In order to ensure such
behavior preserving, most literature on AO refactoring assumes
the presence of a test suite [1]. Such test suite should be
executed before and after each AO refactoring and in case any
refactoring affects the correct behavior of the system, the
developer will be warned. However, none of the AO refactoring
approaches propose the use of a test suite or any other
verification approach to check whether the exception handling
behavior was preserved after the AO refactoring, and then hence
trying to avoid the flaws described on the previous section.
The lack of such behavior preserving AO refactoring stimulated
the present work. Figure 5 presents an overview of the approach
proposed in this work to convert any AO refactoring approach
into an exception aware one, which preserves the exception
handling behavior of the original OO system.
1. Prepare the
Code do be
Refactored
3.1 The Exception Handling Contract Checker
According to previous sections, this work proposes an approach
based, firstly, on the definition of exception handling contracts.
In order to guarantee the fulfillment of such contract we have
implemented in AO solution (i.e., an inspector aspect and some
auxiliary Java classes) that reads a set of exception handling
contracts defined on an XML file and verifies whether or not the
contracts are obeyed during system executions. Figure 6 presents
the schema of contract.xml, the file in which the
exception handling contracts are defined.
<signaler signature=""/>
<exception type="">
<handler signature="">
</exception>
</signaler>
Figure 6. The exception handling contract.
As we can see, the contract has three main elements of interest:
the signaler describes the Class or Aspect that is responsible to
launch directly or indirectly the exception; every signaler can
signal one or more exceptions. Each exception element describes
the type of the Exception object that may be thrown inside the
signaler according to the contract. The handler associated to
each exception defines the element responsible for handling it.
Such contracts are verified during the execution of a test suite
that exercises the join points where the exceptions should be
signaled and handled. To perform the dynamic verification of the
exception handling contracts we have implemented the aspect
and the auxiliary classes as illustrated in Figure 7.
2. Apply Existing
AO Refactoring
Technique
Figure 7. Class Diagram of our Approach Verifier
4. Create
a Test Suite
3. Define
Exception Handling
Contracts
5. Run the Test Suite and
Check the Contracts
The AOPRefactorAspect intercepts all exception handling join
points of any class or aspect (except the ones within
ContractController and AOPRefactorAspect to avoid an infinite
looping), according with the join point below:
handlerJoinPoint(Throwable exception):
(handler(Throwable+)&&
!within(AOPRefactorAspect)&&
!within(ContractController)&&
args(exception)
Figure 5 The approach overview.
3
As we can see, this join point intercepts the handling of any
instance of Throwable and their subclasses. The advice
associated to this join point can then: (i) obtain the instance of
the exception being handled; (ii) the reference to the element that
is handling it; and the reference to the element that signaled the
exception. The code snippet bellow presents the AspectJ code
which enables the advice to obtain a reference to the handler
object.
by cc. The code snippet presented below illustrates the
application of such transformation on the code of credit methods
Defined on the Bank class (presented in Section 2). The same
transformation should also be performed on every join point
affected by the Error Handling Aspect.
1. public class Bank {
2. ...
3. public credit(...) {
4.
try{
5.
// method body
6.}catch (NegativeValueOnOperationException){
7.
//presents an error message.
8.
}
9.
catch (Exception){
9.
// logs any other exception.
10.
}
11. }
handler=thisJoinPoint.getSourceLocation().getW
ithinType();
This aspects has a ContractController auxiliary class that
perform two important operations: (i) loadContractByFile() that reads the contract.xml and builds the structure of the
Contract in memory (the contract contain series of
ContractItems, which are composed by a Signaler, the
Exceptions that can flow from it and the Handlers associated to
it); and (ii) verifyContract (Exception,Handler) that verifies if
the contract is being obeyed, or in other words it detects if the
exception thrown by a signaler is really being captured by any of
the handlers defined on contract. If the aspect detects a contract
violation
it
throws
an
AOPRefactorException.
AOPRefactorException
extends
junit.framework.AssertionFailedError, hence if any
exception handling contract is broken during JUnit tests a “red
bar” will warn the developer that the exception handling
behavior was not preserved.
3.2 Step by Step
This section illustrates step by step how our approach can be
added to any of the existing AO refactoring approaches in order
to avoid a set of recurring flaws on the exception handling code
of refactored systems. To show how it works, in next sections we
presents our approach applied on the motivating example
described previously.
3.2.1 Step 1 – Preparing the code to be Refactored
One of the major difficulties associated with AO refactoring is
the fact that the object-oriented code was not developed to be
aspectized, and, therefore, its structure sometimes difficult the
application of AO refactoring techniques. Few works proposes
transformations on the OO code, in order to allow a smooth AO
refactoring [6], however none of such transformations focus on
the exception handling code that deals with the exceptions to-bemoved to aspects.
We propose the transformation T1 (see Figure 8). Supposing that
cc¹ are statements that represent a crosscutting concern to be
aspectized, which may signal exception e¹ and that st² is a
concern from the base code that may signal exception e², and
both exceptions (e¹ e e²) extends from e.
Moreover, at this step the developer may also refer to the catalog
of best and worst practices related to the aspectization of
exception handling defined in [9].
3.2.2 Step2 – Apply one of the Existing AO Refactoring
Techniques
At this step we apply any one of the existing AO Refactoring
Techniques. In this case study we applied the well defined set of
refactoring steps defined in [4]. The listing bellow presents the
code of NegativeValueOnOpHandler, created during the AO
refactoring, responsible for handling any instance of
NegativeValueOpException
signaled
by
the
NegativeValueOp also created during this AO refactoring
process. The NegativeValueOnOpHandler intercepts with an
around advice the execution of Bank.credit in order to capture
any instance of NegativeValueOpException signaled by the
NegativeValueOnOpCheck.
public aspect NegativeValueOnOpHandler {
...
pointcut valueReceiversOps = ...;
void around (): call (valueReceiversOps){
try{
proceed();
}catch (NegativeValueOpException nvoe){
//treating exception
}
}
}
3.2.3 Step3- Defining the Exception Handling Contracts
The exception handling contracts, involving the crosscutting
concerns that needs to be preserved on the AO refactored version
of the back system are the following2:
<contract>
<signler signature=NegativeValueOnOpCheck">
<exception type="NegativeValueOpException">
<handler
signature="NegativeValueOnOpHandler"/>
</exception>
<contract>
(T1) Add specific handlers for the code to be aspectized:
try{cc¹ st st²}catch(e){} try{cc¹ st st²}catch(e¹){}catch(e²){}
Figure 8. Proposed OO code transformation,
This transformation reduces the risk of the Exception Stealer
problem, but cannot avoid it since the exception signaled by the
base code concerns can be a supertype of the exception signaled
2
The package names of each element involved in the contract
were suppressed for didactic reasons.
4
was in fact handled by the
Bank class. This defect can be corrected removing the catch-all
clause illustrated in Figure 4 by a set of specific handlers. After
performing this change, the test passes, green bar!
Another possible exception handling scenario happens when the
exception signaled by an aspect remains uncaught. When an
uncaught exception is detected by our approach the JUnit
Console presents a slightly different message informing that
exception was uncaught (see Figure 10).
NegativeValueOnOpHandler
3.2.4 Step4 - Designing Test Cases
Unit tests are a safe way to verify whether or not the refactoring
process preserves the system external behavior [12]. As the focus
of this approach is on the preservation of the exception handling
behavior, at this step the developer should define a test case that
exercises the code responsible for signaling and handling the
exception illustrated on the contract presented previously. The
listing bellow presents the code snippet of a JUnit test case that
exercises one of the points in code responsible for signaling the
exception and the point responsible for handling it.
package br.ufrn.gits.example.classes;
import junit.framework.TestCase;
public class EHBankTest extends TestCase {
public void testNegativeValOnOpEHContract()
{
Bank b = new Bank();
Account ac = new Account (“120-3”,0);
Bank.credit(ac, -100);
}
}
A similar test cases should created to exercise the other join
point affected by the NegativeValueOptCheck (i.e., withdraw
and transfer methods).
3.2.5 Step 5: Running Test Cases and Checking
Contracts
When the test case defined on the previous section is executed
the AOPRefactorAspect intercepts the execution of every
handler and checks whether or not the exception handling
contracts are obeyed. As we can see in Figure 9, if there is a
contract violation, JUnit reports a failure expressed as JUnit
famous “red bar” that means that something went wrong during
tests. JUnit Console also exhibits a message clarifying the
contract violation message.
Figure 9 illustrates the output of the JUnit test case defined
previously. As we can see, if there is a contract violation is
exhibited as a Failure, not an error, because JUnit Console shows
an AOPRefactoringException with a message clarifying the
contract violation.
Figure 9. JUnit Execution Screen
Figure 9 shows the JUnit output when the test case defined on
the previous section was executed. The JUnit console reveals that
the exception that was expected to be handled by
Figure 10. JUnit Execution Screen with exception uncaught
message
4. DISCUSSIONS
This section provides further discussion of issues and lessons we
have learned while using the approach proposed which enables
an exception aware refactoring.
AO Weaving x java.lang.Exception. Our first idea was to use
AspectJ inter-type declarations to declare a new attribute on
Exception Classes. This attribute would report the signler of an
exception. However, with current technology, aspects cannot be
woven into standard Java class libraries. According to Villazón,
Binder and Moret (2008) [15], weaver modifies method bodies
and adds dependencies to aspect classes, generating infinite
recursion, and consequently JVM crashes.
Multiple Exception Hierarchies. Another possibility evaluated
in this work was to create two (or more) exception hierarchies,
one for exceptions signaled by the base program, and the other(s)
for exceptions signaled by aspects. However, such solution does
not scale when we think on scenarios where aspects needs to
reuse java libraries which already signal several exceptions; in
this case we would need to catch and re-throw every exception
as an AO exception instance.
Test Case Generation. Badri, Badri and Bouque-Fortin (2009)
[14] presents an framework to automatic aspect tests based on
the model of JUnit, the AJUnit. An aspect cannot be tested alone,
because their behavior often depends on the woven context and
the rules defined on aspect. Because this, AJUnit generates statebased testing sequences covering an aspect(s)-class block of
code, and supports execution and verification of the generated
sequences. Posteriorly, we can use this framework to build new
test cases on this approach or adapt our test cases to its to
develop more appropriate test cases for verifying contracts
implemented with our AOP Refactor Exception Aware approach.
Testing Coverage. Wedyan and Gosh (2008) [13] propose a tool
for measuring join point coverage of test cases from two
perspectives: per advice, which measures the execution of the
5
advice at each join point it is woven into, and per class, which
measures the execution of all advices in each joinpoint in the
class. Would be interesting to verify with this tool the coverage
of our test cases set developed to verify the contracts.
Limitations of the Proposed Approach. The limitations of our
approach are the limitations that surround every strategy based
on the definition of test cases: the effectiveness of the approach
strongly depends on the ability of the developer to define test
cases that are able of exercising full exception handling
contracts.
[6]
[7]
[8]
5. CONCLUDING REMARKS
We have presented an approach to support the exception-aware
AO refactoring. This approach is based on the definition of
exception handling contracts (in an XML file) and the definition
of test cases to exercise the code on which such contracts should
be verified (i.e., the code on which exceptions should be signaled
and handled). In future work, we plan to improve the approach:
(i) using aspects in order to create Mock Objects to support the
test of exception handling code (mock objects may be used to
inject exceptions at specific points in the code), and also (ii)
generating partial Test Cases based on the pointcut information
of each crosscutting concern involved in an exception handling
contract.
[9]
[10]
[11]
[12]
6. REFERENCES
[1]
[2]
[3]
[4]
[5]
Deursen, A., Marin, M., and Moonen, L., A
SystematicAspect-Oriented Refactoring and Testing
Strategy, and its Application to JHotDraw. Technical Report
SEN-R0507, CWI, Amsterdam, 2005.
Marin M., Moonen, L., Deursen, A.V. Integrated
Crosscutting Concern Migration Strategy and its Application
to JHotDraw. In: Proceedings of the Source Code Analysis
and Manipulation (SCAM 2007), Paris, France, 2007,
p.101-110.
Monteiro M. P., Fernandes J. M., An illustrative example of
refactoring object-oriented source code with aspect-oriented
mechanisms. Software: Practice and Experience 38(4):
pp.361-396, 2008.
Soares, S., Borba, P., Laureano, E., Distribution and
Persistence as Aspects. In: Software Practice and
Experience, Wiley, vol. 36 (7), (2006) 711-759.
Marin, M., Moonen, L., Deursen, A.: An approach to aspect
refactoring based on crosscutting concern types. In:
Proceedings of the First International Workshop on the
Modeling and Analysis of Concerns in Software,
[13]
[14]
[15]
[16]
International Conference on Software Engineering, St.
Louis, USA (2005)
Valente, M. Malta, M., Domingues, S., Guidelines for
Enabling the Extraction of Aspects from Existing ObjectOriented Code. Journal of Object Technology 8(3): 101-119
(2009).
Coelho, R., Rashid, A., Garcia, A., Ferrari, F., Cacho, N.,
Kulesza, U., von Staa, A., Lucena, C., Assessing the Impact
of Aspects on Exception Flows: An Exploratory Study,
ECOOP 2008.
Jo, J., Chang, B., Yi, K., Choe, K. An Uncaught Exception
Analysis for Java. In: Journal of Systems and Software, vol.
72 (1), (2004) 59-69.
Castor Filho, F., Garcia, A., Rubira, C., Extracting Error
Handling to Aspects: A Cookbook. In: Proceedings of the
IEEE International Conference on Software Maitenance
(ICSM), Paris, France, 2007, p.134-143.
Coelho, R, Awais, R., Staa, A.v., Noble, J., Kulesza, U.,
Lucena, C., A Catalogue of Bug Patterns for Exception
Handling in Aspect-Oriented Programs, Conference on
Pattern Languages of Programs (PLoP’08), 2008.
Fowler, M., Beck, K., Brant, J., Opdyke, W. , and Roberts.
Refactoring, D. Improving the Design of Existing Code.
Addison-Wesley, 1999.
How Unit Testing and Refactoring Work Together http://www.artima.com/weblogs/viewpost.jsp?thread=15655
5
Wedyan, F., and Ghosh, S., A Joinpoint Coverage
Measurement Tool for Evaluating the Effectiveness of Test
Inputs for AspectJ Programs. Proc.of International
Symposium on Software Reliability Engineering, pages
207–212, 2008.
Badri, M., Badri, L., and Bourque-Fortin, M., Automated
State-Based Unit Testing for Aspect-Oriented Programs: A
Supporting Framework, Journal OF Object Technology, vol.
8, pp. 121-146, 2009.
Villazón, A., Binder, W, Moret, P., Aspect Weaving in
Standard Java Class Libraries. 6th International Symposium
on Principles and Practice of Programming in Java (PPPJ2008), Modena, Italy, September 2008
Exception
Handling
Anti-Patterns.
Site:
http://today.java.net/article/2006/04/04/exception-handlingantipatterns, accessed at: 04.07.2010
6
Preserving the Exception Handling Design Rules in Software Product Line Context:
A Practical Approach
Ricardo J. Sales Júnior
Roberta Coelho
Departamento de Informática e Matemática Aplicada
(DIMAp)
UFRN, Brasil
[email protected]
Departamento de Informática e Matemática Aplicada
(DIMAp)
UFRN, Brasil
[email protected]
Abstract— Checking the conformance between implementation
and design rules is an important activity to guarantee quality
on architecture and source code. To address the current needs
of dependable systems it is also important to define design
rules related to the exception handling behavior. The current
approaches to automatically check design rules, however, do
not provide suitable ways to define design rules related to the
exception handling policy of a system. This paper proposes a
practical approach to preserve the exception policy of a system
or a family of systems along its evolution, based on the
definition and automatic checking of exception handling design
rules, that regulates how exceptions flow inside the system –
which exceptions should flow and which elements are
responsible for signaling and handling them. This approach
automatically generates the partial code of JUnit tests to check
such rules, and use the aspect-oriented technique to support
such tests. The proposed approach was applied to define and
check the exception handling rules of a software product line.
Four different versions were evaluated (in both object-oriented
and aspect-oriented implementations) in order to evaluate
whether the exception handling policy was preserved during
SPL evolution. Our experience shows that the proposed
approach can be used to effectively detect violations on the
exception handling policy of a software product line during its
evolution.
Keywords: Design Rules, Exception Handling, Software
Product Line.
I.
INTRODUCTION
The highly dynamic and competitive market has
stimulated the creation of a new bread of applications,
structured as a family of similar systems that shares a
common architecture. Such system families, also called
product lines, provide benefits such as: a reduced time-tomarket - many products with small variations can be built;
and improvement on systems quality - the artifacts
developed once can be reused and tested many times.
The software product line (SPL) architecture addresses
the set of common and variable features of a family of
products; and based on this architecture, products can be
derived in a systematic way. As it is virtually impossible to
anticipate all possible variable and common features [1] of a
system family, it is often necessary to evolve the SPL
architecture to meet the new requirements. Such changes in
SPL architecture may impact the whole product line and lead
to failures that may be perceived in unmodified parts of the
systems. An important question arises on the evolution
scenarios of SPLs is the following: how to ensure that
changes on the SPL architecture will not negatively impact
the SPL features?
One way to define architectural standards that must be
obeyed during software development, and maintenance is to
define a set of design rules [2]. The design rules define
architectural patterns that must be strictly followed in all
phases of software life cycle.
Such design rules are usually defined and checked
manually. However, in large systems, it becomes infeasible
to manually check these rules. Works like [3], [4] present
approaches to define and automatically check the design
rules of systems. A common limitation of such approaches is
that they do not provide a suitable way to define design rules
related to the exception handling policy of a system.
The exception handling mechanism is one of the main
techniques used nowadays to support the development of
dependable systems. The exception handling policy
comprises a set of design rules that defines the system
elements responsible for signaling, handling and re-throwing
the exceptions. The system dependability relies, therefore, on
the obedience of such rules. Reasoning about the exception
handling design rules, looking for potential-faults on the
exception handling code, can quickly become unfeasible if
carried out manually – due to the complexity and the huge
number of exception paths to be followed. For this reason we
need tools to help developers (i) understanding the impact of
changes on the existing exception handling policy, and (ii)
finding bugs in the exceptional handling code during
maintenance tasks.
In this work we evaluate the use of exception handling
contracts [5] to specify the exception handling design rules
of a SPL and present an approach to automatically check
such rules through a set of automatically generated JUnit
tests. Aspect oriented programming is used to support the
check of the exception design rules during the execution of
automated tests. This work was motivated by a previous
study [5] on which we presented a JUnit extension to support
AO refactoring. In this previous work we presented the first
version of the exception handling contracts (see Section III)
on which signalers and handlers were represented only in
terms of classes/aspects. In the present work these elements
are represented in terms of methods and wild cards, hence
allowing a greater level of detail. In addition, the current
approach is based on the generation of the basic code of
JUnit tests and dynamic mock objects [14] to facilitate the
instantiation of the tests responsible for checking the
exception handling rules.
The remainder of this paper is organized as follows.
Section II presents a short background related to the
definition and checking of design rules. Section III presents
the proposed approach and how JUnit tests and the aspect
oriented technique can be used to concretize its ideas.
Section IV presents our experience of defining and checking
the exceptional design rules of a SLP, called Mobile Media
[8, 9, 10]. Section V provides further discussion of lessons
learned. Section VI presents our conclusions and directions
for future work. Due to space limitation, throughout this
article we assume that the reader is familiar with AOSD
terminology, such as: aspect, join point, pointcut, and advice.
II.
DEFINING AND CHEKING DESING RULES
Checking conformance between implementation and
design rules is an important activity to guarantee quality on
architecture and source code. One process widely used is
Architectural Assessments as presented by [18], which
consists of a systematic review of a system architecture by
creating scenarios and performing evaluations by
stakeholders, including architects and developers. The
problem with this approach and other similar is that, being a
manual process, it can lead the team to errors during the
analysis. Moreover, these processes are not scalable, since
analyzing a great number of classes may take a long time.
There have been several attempts to check conformance
between system and design rules. The work of [4] presents
the Design Tests, a framework that extends JUnit, providing
a set of methods for verifying the structure of the application
code, allowing making references to packages, hierarchy of
classes and so on. In this case, even the test cases are the
design rules. However, unlike our approach, it does not allow
the verification of design rules in aspect-oriented systems
and also lacks of commands for checking exceptional flows.
Other work is [3], which presents design rules that can be
verified in aspect-oriented systems. In the case of AO
systems, the design rules serve as interfaces between classes
and aspects, so that aspects can be developed without the
need to have knowledge of the internal structure of the class,
and at the same time, the class does not need to be aware of
the aspect. However, the support for verification of
exceptional flows is limited – it allows the definition of
design rules attached to exception signalers, related to
exception types that must be included or not in method
declarations, but does not define how they should be
handled.
III.
TAMING THE EXCEPTIONS IN SOFTWARE PRODUCT
LINE CONTEXT
This section presents an overview of the proposed
approach, presenting how to define and dynamically check
exception handling design rules. We represent the exception
handling design rules in the form of exceptional contracts as
illustrated in Figure 1.
<contract>
<signaler
signtre=“Logger.write*(String,String)”>
<exception type=“FileNotFoundException”>
<handler
signature=“FileNotFoundEH.*(String)”/>
</exception>
</signaler>
</contract>
Figure 1: Example of format of exceptional contracts
As we can see, the contract has three main elements of
interest: the signaler - which describes the method that is
responsible to launch an exception (instance a new or throw
some that occurs inside them); the exception – which
describes the Exception types that may be thrown inside the
signaler (the signaler may throw more than one exception);
and finally the handler - defines the element responsible for
handling each exception. The example presented in Figure 1
specifies that every instance of FileNotFoundException,
signaled inside Logger.write*(String,String) should be
handled at any method defined inside FileNotFoundEH
module (which can be a class or an aspect).
In order to check an exception handling contract we need
to stimulate the handling and the signaling codes. In order to
stimulate the handling code we can create a unit test that
calls the method responsible for handling a given exception.
In order to stimulate the signaler we can inject the exception
that is supposed to be handled – regardless the parameters
passed to it. In other words, such tests are responsible for
verifying whether handlers correctly catch the exceptions.
Since such tasks are costly to be performed manually, we
developed a tool to support the automatic generation of
partial JUnit test cases and mocks objects [12]. The mock
objects are a test design pattern useful to the unit test and
design of object-oriented software. The mock object
simulates the real objects for the purpose of testing. In our
approach the mocks stimulates signalers to launch exceptions
regardless of conditions inside the test cases.
Figure 2 shows an example of JUnit Test Case generated
by our tool. It is important to notice that these generated
testes still need to be completed (i.e., parameters need to be
added in order to really exercise specific points from
contract). In this example, it is necessary to add parameters
in “handler.treat” method call to stimulate the
“Logger.write” method call.
import …
public class TestOne extends LoggingTestCase {
public void testContract(){
/*please, insert arguments that stimulate */
/*signaler: Logger.write*(String,String) */
/*inside handler: FileNotFoundEH.treat(String)*/
FileNotFoundEH handler = new FileNotFoundEH();
handler.treat(parameters..);
}
}
Figure 2: JUnit Test Case Generated by the tool
When we combine mock objects with AOP, we can introduce the mock objects without making any change to the
core system [13]. Figure 3, presents a dynamic mock object,
implemented in AspectJ [17] that independently of
parameters of “Logger.write” method call, make such
method launch a “FileNotFoundException”. It is also
important to note that this MockAspect only comes into play,
if method “Logger.write” is executed inside flow
execution of the test. This is because actually our contract
only defines the binding between signalers and handlers, not
the exception scenarios where exceptions should be thrown.
Figure 4: Execution of JUnit test cases with support of the tool.
IV.
import …
@Aspect
public class MockAspectTest {
@Around(“execution(Logger.write*(String,String)
&& cflow(execution(* TestOne.*(..)))”){
public Object
launchException(ProceedingJoinPoint this)
throws FileNotFoundException
{
throw new FileNotFoundException();
}
}
}
Figure 3: Dynamic mock objects generated to launch
automatically the exception when the method is signaler is
invoked inside test case.
Figure 4 shows the execution trace of the JUnit extension
to support the execution of JUnit test cases for checking the
exception handling contracts.
CASE STUDY
The case study aimed to apply our approach on a system
closer to a real system than the previous analyzed – they
were very toy programs – analyzing scenarios not expected
and evaluating if the approach is efficient to find exceptions
with incorrect handling. Another objective of our case study
was to find the likely difficulties that would be found by
developers and then lift improvements and questions to be
analyzed in future works. Next sections present our case
study step by step.
i. Choose of system for implementing the approach
The first step of our case study was to choose the target
software product line. The SPL chosen was Mobile Media
[6], an SPL that implements mobile applications that
manipulate images, photos and other media. It has been used
in several empirical and case studies [8], [9] and [10], and
has the following peculiarities: (i) it has a reasonably welldefined exception handling policy; it contains a package with
nine application specific exceptions; (ii) it has optional and
alternative features; (iii)it is representative to mobile devices
domain; (iv) It has OO and AO subversions.
In this case study we have used two versions of both Java
and AspectJ [17] implementations of Mobile Media SPL.
Multiple versions were needed in order to verify the
behavior of exception handling contracts not only between
different versions but also between the OO and AO
implementations. Figure 5 and Figure 6 present an overview
of the OO and AO design of Mobile Media; both adopting
the Model-View-Controller architectural pattern.
ii. Code analysis for contract extraction
Firstly, we manually analyzed the exception handling
behavior of the first version (OO 1.0), and devised the
exception handling contracts. As a result we obtained a set of
View
MainUIMidlet
S
SMSScreen
PhotoViewScreen MediaListScreen PlayMediaScreen
E
E
E
E
CommandList
CommandListener
Controller
AbstractController
Legend:
E
Model
E
Exception Handling
C
Count views optional feature
F
C
PhotoView
Controller
MusicPlay
Controller
Media
Controller
E
E
S
MediaAccessor
AlbumData
E
E
F
Select favorites optional feature
S
Send SMS
VideoAccessor
PhotoAccessor
MusicAccessor
VideoAlbumData MusicAlbumData PhotoAlbumData
Figure 5: The OO design of Mobile Media
View
<<aspect>>
ScreenrEH
<<crosscuts>>
MainUIMidlet
<<crosscuts>>
<<crosscuts>>
SMSScreen
E
PhotoViewScreen MediaListScreen PlayMediaScreen
<<crosscuts>>
CommandList
<<aspect>>
SendSMSAspect
S
Controller
CommandListener
<<crosscuts>>
<<aspect>>
FavoritesAspect
<<aspect>>
CountViewsAspect
F
<<crosscuts>>
AbstractController
C
<<crosscuts>>
<<crosscuts>>
<<aspect>>
ModelEH
<<crosscuts>>
Media
Controller
Legend:
E
Exception Handling
C
Count views optional feature
F
Select favorites optional feature
S
Send SMS
<<aspect>>
ControllerEH
E
MusicPlay
Controller
PhotoView
Controller
<<crosscuts>>
Model
MediaAccessor
VideoAccessor
MusicAccessor
AlbumData
PhotoAccessor
VideoAlbumData MusicAlbumData PhotoAlbumData
Figure 6: The AO design of Mobile Media.
E
16 exceptional contracts. Table 1 illustrates some of the
contracts found.
Table 1: Exception Handling Contracts
ID
1
2
3
public class ImageAccessorloadAlbumsInvalidImage\
DataExceptionTest extends TestCase {
public void
testContractHandlergetAlbumNames0() {
/* Please, insert arguments that estimulate
signaler:ubc.midp.mobilephoto.core.ui.datamo
del.I mageAccessor.loadAlbums() */
/* inside handler:ubc.midp.mobilephoto.core.ui.
datamodel.AlbumData.getAlbumNames() */
Exception Handling Contract
<signaler signature="ubc.midp.mobilephoto.core.
ui.datamodel.AlbumData.deleteImage(java.lang.String,ja
va.lang.String)">
<exception type="lancs.midp.mobilephoto.lib.
exceptions.ImageNotFoundException">
<handler signature="ubc.midp.mobile
photo.core.ui.controller.BaseController.handleCommand
(javax.microedition.lcdui.Command,javax.microedition.l
cdui.Displayable)"/>
</exception>
</signaler>
<signaler
signature="ubc.midp.mobilephoto.core.ui
.datamodel.ImageAccessor.loadAlbums()">
<exception type="lancs.midp.mobilephoto.lib
.exceptions.InvalidImageDataException">
<handler signature="ubc.midp.mobilephoto
.core.ui.datamodel.AlbumData.getAlbumNames()"/>
</exception>
</signaler>
<signaler signature="ubc.midp.mobilephoto.core.ui.dat
amodel.AlbumData.getImageNames(java.lang.String)">
<exception type="lancs.midp.mobilephoto.lib
.exceptions.UnavailablePhotoAlbumException">
<handler signature="ubc.midp.mobilephoto
.core.ui.controller.BaseController.showImageList(java.la
ng.String)"/>
</exception>
</signaler>
iii. Generation of test cases and their dynamic mock
objects
Such contracts are used as input to the automatic
generation of aspectual mocks and the partial code of JUnit
test cases - to be completed with parameters to stimulate the
exception signalers. The test cases and mocks were then
generated for the 16 contracts, and based on the exception
handling code analysis described previously we have
completed the partial test cases. Figure 7 presents the code of
the test case generated to contract 2 presented in Table 1 1.
1
imports …
For the sake of simplicity and due to space limitations we
suppressed the import clauses.
AlbumData handler = new AlbumData();
handler.getAlbumNames();
}
}
Figure 7: JUnit Test Case Generated by the Tool
In order to stimulate the exception signaling the aspect
presented in Figure 8 was generated. This aspect intercepts
the execution of loadAlbums() method, in the context of a
test case, and forces the method to signal an instance of
InvalidImageDataException.
import …
@Aspect public class
ImageAccessorloadAlbumsInvalidImageDataExceptionT
estMockTestAspect {
@Around("execution (* ubc.midp.mobilephoto.
core.ui.datamodel.ImageAccessor.loadAlbums()) &&
cflow(execution(* ImageAccessorloadAlbumsInvalid
ImageDataExceptionTest.*(..)))")
public ObjectlaunchInvalidImageDataException
(ProceedingJoinPoint thisJoinPoint)
throws InvalidImageDataException {
throw new lancs.midp.mobilephoto.lib.\
exceptions.InvalidImageDataException();
}
}
Figure 8: Dynamic mock objects generated by the tool.
In some scenarios, however, it is not so simple to
stimulate the signaling and handling code. In such scenarios
we had to define supporting mock objects (using Easy Mock
framework [14]) to support the unit testing of exception
handling contracts. Figure 9 illustrates the unit test created to
check contract 3.
Table 2: Experiments Results
imports …
public class
AlbumDatadeleteImageImageNotFound\
ExceptionTest extends TestCase {
public void
testContractHandlerhandleCommand0()
{
Command comando =
EasyMock.createMock(Command.class);
EasyMock.expect(comando.getLabel()).
andReturn("Delete").anyTimes();
EasyMock.replay(comando);
BaseController handler = new
BaseController(new MainUIMidlet(),new
AlbumData());
handler.handleCommand(comando, null);
}
Figure 9: JUnit Test Case Generated by the tool and Mock
Objects created to support the unit testing
In such test case, in order to instantiate the handler code
we needed to create auxiliary objects (presented in gray in
Figure 9), which should be passed as parameters, but did not
affect the way the exception flow inside the handler code.
This example illustrates a scenario in which it would very
complicated to try to automatically generate a complete test
case.
iv. Test Execution and analysis
After executing the test cases on 4 Mobile Media
versions (two OO versions and two AO versions) we
obtained the obtained results presented in Table 2.
As we can observe, from version v1.0 OO to v1.0 AO
only a single test case passed, one test case failed because
exception was not handled, 7 test cases failed because the
exception under test was transformed to another exception
type (i.e., SoftException - an specific type of not checked
RuntimeException in AspectJ) before been caught, and
seven test cases failed because the exception was handled by
an aspect instead of the handler defined in the contract.
We could observe that the contract tests failed because of
two main reasons: (i) the exception handling rules were
actually broken on the new system version; and (ii) other
exception handling design rules needed to be updated to
incorporate the architecture evolution. In this case, eight
contract items were actually broken; and seven contracts
needed to be updated due to changes on the design rules – in
such cases the exceptions should be handled by aspects on
the new system version.
We can also make a comparison between the OO
versions. Although, among such versions has been no direct
breaches of contract, there were small changes that would
require the update of the exceptional contracts.
Comparing the AO versions, between v1, v2 and v3 AO
there was no direct broken contract. We have noticed that
some contracts were no longer applicable. We realize that
some of these contracts need to be updated – because a
change of strategy for handling exceptions – especially in
version AO v4.
From what we have observed, the approach was efficient
in finding faults in design rules of exception handling,
including scenarios on which LPS evolves – through the
addition and removal of features. When the features were
represented using aspects, 50% of executed tests reported
failures. Another interesting initial observation was that the
semi-automatic generation of tests and mocks were quite
useful, easing the costly test case creation task.
V.
DISCUSSION
This section provides further discussion and lessons we
have learned while using the proposed approach which
enables to preserve the exception handling design rules in
Software Product Line context.
Code Testability. During the case study we noticed that
some tests had raised issues of testability, i.e, it would be
necessary to refactor the application code to be able to
perform automated testing without a major difficulty in
implementing them. The work of [11] lists the main
problems that cause poor testability and presents a tool that
measures the level of code testability, and recommends
necessary modifications.
Test Case Generation. As shown in section IV.iii,
actually our contract only defines the binding between
signalers and handlers, not the exception scenarios where
exceptions should be thrown. This implies in the fact that the
tests generated by our tool are still incomplete, depending on
the skill of the tester to complete them with data that
stimulate the execution. Hence, we can improve our
contracts (design rules) to include rules to binding between
signalers and handlers.
Dealing with the Test Explosion Problem. Automated
tests have been widely used as a supporting mechanism
during software development and maintenance activities. It
improves the confidence on software releases as it seeks to
uncover regression bugs, and serves as a live documentation
which is very useful when evolving systems. However, the
automated tests usually do not take into account the
exceptional scenarios (i.e., scenarios related to exceptions
occurrences and handling). They focus mainly on testing the
“normal” program behavior. The reason is twofold. Since the
exception handling code is not the primary concern to be
implemented, it does not receive much attention during the
verification phase. Moreover, testing the exception handling
code is inherently difficult, since it is tricky to provoke all
exceptions during tests, and the large number of different
exceptions that can happen in a system may lead to test-case
explosion problems [16]. In our approach we deal with the
test case explosion problem when we allow the developer to
define compartments of varying granularities to be used on
the exception handling contracts, and automatically
generating partial JUnit tests and dynamic mock objects to
stimulate the handling and the signaling codes.
VI.
CONCLUDING REMARKS
We have presented a practical approach to support the
definition and checking of exception handling design rules in
the software product line context. Such rules are checked
during the execution of JUnit test cases that are partially
generated by a supporting tool. Besites JUnit test cases,
dynamic mock objects are also generated to stimulate both
the handler and the signaler code.
This approach has been used in the product line called
Mobile Media. In these case study, the approach was used to
define and check exception handling rules. We could benefit
from a set of semi-automatic tests that could run constantly,
improving the confidence in the system and supporting the
maintainability tasks. Although the automatic tests could not
assure the absence of faults in the tested systems, they could
guarantee that whenever the exception handling rules defined
were preserved on the exercised scenarios. Our preliminary
results have shown that the proposed approach can
effectively uncover bugs on the exception handling code.
Although the proposed tool depends on a tool
implementation based on Java and AspectJ[17] its
underlying concepts can be applied to define and check
exception handling design rules of any SPL, given that the
language the system is implemented in can be extended to
support aspect-oriented concepts. In future work, we plan to
improve the generative approach: generating dynamic mock
objects and tests of a higher complexity. Moreover, we are
already trying to replicate the case study in other SPLs (such
the industrial J2ME game software product line used in
[15]), in order to refine our findings: detailing the kinds of
scenarios on which contracts should be preserved, removed
or updated; and improving the way the exception handling
contracts are defined.
REFERENCES
[1]
[2]
[3]
[4]
[5]
[6]
[7]
Jacobson I., Griss M. and Jonsson Patrik. “Software Reuse:
Architecture, Process and Organization for Business Success”,
Adisson Wesley.J. Clerk Maxwell, A Treatise on Electricity and
Magnetism, 3rd ed., vol. 2. Oxford: Clarendon, pp.68–73, 1997
Baldwin C. and Clark K. “Design Rules: The Power of Modularity”,
MIT Press, 2000
Neto, A. “Specifying Design Rules in Aspect-Oriented Systems”,
Dissertação, Mestrado, UFPE, Recife, 2007
Brunet, J., Guerredo, D. and Figueiredo J. “Design Tests: An
approach to programmatically check your code against design rules”,
In 31st International Conference on Software Engineering (ICSE
2009), New Ideas and Emerging Results, 2009
Sales, R., Coelho, R. and Neto, V. “Exception-Aware AO
Refactoring”, In: Proceedings of Latin American Workshop on
Aspect-Oriented Software Development (LA-WASP‟10), Salvador,
Brazil, 2010
Figueiredo, E et al. “Evolving Software Product Lines with Aspects:
An empirical study on design stability”, ICSE‟08, Leipzig, Germany,
2008.
Trujillo, S., Batory D. and Diaz O. “Feature Refactoring a MultiRepresentation Program into a Product Line”, In Proceedings of the
5th international conference on Generative programming and
component engineering, Portland, Oregon, USA, 2006
[8] Ribeiro M. And Borba P. Towards feature modularization. In
SPLASH '10: Proceedings of the ACM international conference
companion on Object oriented programming systems languages and
applications companion (Doctoral Symposium), pages 225-226, New
York, NY, USA, 2010. ACM
[9] Torres M., Kulesza U., Braga R., Masiero P., Pires P., Delicato F.,
Cirilo E., Batista T., Teixeira L., Borba P, and Lucena C. Estudo
comparativo de ferramentas de derivacao dirigidas por modelos:
Resultados preliminares. In Primeiro Workshop Brasileiro de
Desenvolvimento Dirigido por Modelos (WBDDM'2010), Workshop
do CBSoft 2010, 2010.
[10] Alencar F., Castro J., Lucena M., Santos E., Silva C., Araújo J. and
Moreira A. Towards modular i* models. In SAC '10: Proceedings of
the 2010 ACM Symposium on Applied Computing, pages 292-297,
New York, NY, USA, 2010. ACM
[11] Misko, H. Testability explorer: using byte-code analysis to engineer
lasting social changes in an organization's software development
process. Companion to the 23rd ACM SIGPLAN conference on
Object-oriented programming systems languages and applications,
Nashville, TN, USA, 2008.
[12] Mcknnon., Freeman, S., and CRAIG, P. EndoTesting: Unit Testing
with Mock Objects. In: EXTREME PROGRAMMING EXAMINED,
Beck, K. Addison Wesley, 2001. p. 287-301.
[13] Monk, S.; Hall, S. Virtual Mock Objects using AspectJ with JUnit.
XProgramming.com, 2002.
[14] Freese, Tammo: EasyMock: Dynamic Mock Objects for JUnit. In:
MARCHESI, M. (Hrsg.) ; SUCCI, G. (Hrsg.): Proceedings of the 3nd
International Conference on Extreme Programming and Flexible
Processes in Software Engineering (XP2002), 2002.
[15] Coelho, R., Alves, A., Kulesza, U., Costa, A., Staa, A., Lucena, C.,
Borba, P. “ On Testing Crosscutting Features Using Extension Join
Points ”, 3rd Workshop on Product Line Testing, SPLiT 2006.
[16] Myers, G.J. The Art of Software Testing. New York: John Wiley &
Sons, 2004.
[17] Kiczales, G., Hilsdale, E., Hugunin, J., Kersten, M., Palm, J., and
Griswold, W., “Getting Started with AspectJ”, Communication of the
ACM, 44(10), 2001, pp. 59-65.
[18] Maccari A. Experiences in assessing product family software
architecture for evolution. In: Proceedings of the 24th International
Conference on Software Engineering.ICSE 02, 2002
Agile Testing of Exceptional Behavior
Rafael Di Bernardo∗, Ricardo Sales Jr.† , Fernando Castor∗ , Roberta Coelho† , Nélio Cacho† , Sérgio Soares∗
∗ Informatics Center, Federal University of Pernambuco
Recife, Brazil
† Informatics and Applied Mathematics Department, Federal University of Rio Grande do Norte
Natal, Brazil
Email: [email protected], [email protected], [email protected],
[email protected], [email protected], [email protected]
Abstract—Many of the problems found in the use of exception handling are caused by the lack of testing and a
priori design of the exceptional behavior. As a consequence,
exceptions flow in unforeseen ways during the execution of
a software system, having a negative impact on reliability.
This paper presents an agile approach to test the exceptional
behavior of a system. It supports developers in checking
whether exceptions, at runtime, travel through the expected
paths. It is agile because tests are written without the need
for extra documentation and are, themselves, considered live
documentation. We have evaluated our approach by applying
it to different versions of two production quality Java open
source applications (i.e., aTunes and JEdit). Using the proposed
approach we could find twelve bugs - eight of them previously
unknown by the open source projects. In addition, from the
viewpoint of automated tests as documentation artifacts, the
proposed approach pointed out several differences between
versions of the two target systems. We have implemented the
proposed approach as an extension of the JUnit framework.
Keywords: exception handling, testing, exceptional behavior, aspect-oriented programming
I. I NTRODUCTION
Modern software systems are usually composed by a
collection of distributed components that must deal with
inputs from a variety of sources, execute in diverse environments, while addressing strict dependability requirements. In order to address such dependability requirements
several techniques can be applied. One of the most used
techniques, which are embedded in mainstream programming languages, are the Exception Handling mechanisms
[7]. These mechanisms help developers to build robust
applications by separating exceptional behavior from the
normal control flow [13]. They ensure system modularity
in the presence of errors by offering abstractions for: (i)
representing erroneous situations of system modules as
exceptions, (ii) encapsulating exception handling activities
into handlers, (iii) defining parts of the system modules as
protected regions for handling exception occurrences, (iv)
associating these protected regions with handlers, and (v)
explicitly specifying exceptional interfaces of modules.
If on the one hand, a large amount of code on modern
application systems is dedicated to error detection and
handling[23], [24], [9], on the other hand, dealing with
manifestations of errors/exceptions at different stages of
development (e.g., requirements elicitation, design tasks)
has received little attention [17], [22]. Developers tend to
give attention to the normal behavior of applications, not
concentrating on the design the exceptional activity[25].
They usually only deal with error detection and handling
in the implementation phase [7]. As consequence, they
do not properly use the exception handling mechanisms,
compromising system dependability.
Due to the lack of attention along the phases of software
development, the exception handling constructs are usually
fault prone [1]. In other words, the code that was firstly
designed to improve the system robustness becomes a source
of faults. In order to discover such faults, on the exception
handling code, approaches based on static analysis have
been proposed. Such approaches are based on tools which
discover the paths that exceptions take from signalers (i.e.,
elements that throw exceptions) to handlers (i.e., elements
responsible for catching them) [14], [6], [16], [5]. However,
due to the limitations inherent to static analysis approaches
combined with the characteristics of modern languages (such
as inheritance, polymorphism and virtual calls such) such
approaches usually report many false positives [28]. Hence,
manual steps are usually required to verify whether the
detected exception paths can actually happen. Such manual
steps can make such approaches prohibitively expensive.
Moreover, another limitation of such approaches is the
fact that they can only be used to detect faults on the
exception handling code after the system was implemented.
Few development approaches have addressed this issue, such
as [17], [22], but they are costly and heavily based on
documentation, imposing an overhead that can be impeditive
to their use. Experiences [21] have shown that lightweight
agile methodologies have been successfully used on the
development of modern applications. Such methodologies
are highly based on test automation and little documentation;
in such agile development approaches automated tests serve
as live documentation. However, current agile methodologies
do not explicitly take into account the testing of exceptional
behavior.
In this paper we propose an agile approach to specify
and test the exceptional behavior of a system. In particular,
we are interested in testing whether the paths traveled by
exceptions, from the points where they are thrown to their
destinations, are correct. We have implemented this approach
as an extension to the well-known JUnit framework. Tests
for the exceptional behavior, called as exceptional tests from
now on, are specified as regular JUnit tests with a small
amount of extra information. An exceptional test can be
used to specify possible exception paths and compare them
with the paths that exceptions may travel at runtime. Our
approach can be used to write tests: (i) before the system
has been implemented using the concept of ”test-first”
development; or (ii) in latter phases, to help in maintenance
activities in existing systems. We have applied the proposed
approach to production systems written in Java and have
uncovered twelve bugs, eight of them previously unknown.
The rest of the paper is organized as follows. Section II
presents a motivating example. The example explains why
JUnit, the most popular approach for the construction of
automated tests cases, fails to capture exception propagation
bugs. handling errors. Section V briefly describes related
work. Section III presents our proposed approach. Section IV
presents a preliminary evaluation and the last section rounds
the paper.
II. A M OTIVATING E XAMPLE
In this section we present a simple illustrative example to
point out some limitations of pure JUnit Test Cases to detect
exception handling errors. Consider a layered information
system composed by three layers: Data, Business, and GUI
(Figure 1). An exception handling policy that should be
obeyed in this application is the following: the exceptions
signaled by Data Access Objects (defined on the Data layer)
represent problems in attempts to access a database. They
are subtypes of DAOException. These exceptions should
be handled by Servlets defined on the GUI layer. In order
to check whether this policy is obeyed, the developer could
try to define a JUnit test. The following code snippet is a
possible implementation for the test.
public void testDAOEHPolicy (){
Servlet s1 = new Servlet();
s1.service();
}
If no instance of DAOException escapes from this
test method, the tester may think that it was adequately
handled by the Servlet. However, two faults may happen, which cannot be detected by this test case: (i) the
instance of DAOException may be mistakenly caught by
the Facade (an intermediate element between the signaler
and the intended handler); (ii) the DAO may not signal
the exception. In addition, a developer might want to test
whether a method signals the exception when an erroneous
condition is detected. JUnit supports the specification of
test cases that expect an exception using the expected
annotation1:
@Test(expected=DAOException.class)
public void testDAOEHPolicy (){
Servlet s1 = new Servlet();
s1.service();
}
In this case, the test will pass whenever it ends by signaling an exception of type DAOException. If an instance of
DAOException is mistakenly captured by another catch
block and no other exception is signaled, the test will fail, as
expected. The same occurs if DAOException is not signaled. This approach is still limited, however, because it can
hide subtle bugs. For example, if a component from the Data
layer signals an exception representing a programming error,
such as a null dereference (NullPointerException),
but that exception is captured by a generic handler, e.g.,
a catch clause for type Exception, which, in turn,
signals DAOException, the test will still pass but the
programming error will be hidden. In fact, we have detected
this kind of problem in existing applications (Section IV).
Examination of the bug report systems of these applications
reveal that they are considered bugs.
The JUnit framework does not provide a way of checking
whether an intermediate element mistakenly handles the exception. This is understandable, since JUnit aims to support
unit testing. Notwithstanding, when it comes to exceptions,
unexpected global exception propagation can be a great
source of errors [14], [16]. This is particularly noticeable
for languages like Java and C#, where exceptions are objects
that can be captured by type subsumption [16], that is, an
exception handler for a type T captures exceptions of any
type T 0 that is a subtype of T . On the other hand, JUnit is the
de facto standard for agile automated testing and has been
extended in some scenarios to handle broader-scoped (e.g.,
integration) tests [4], [26]. Moreover, we believe that exception handling should be taken into account throughout all
the phases of software development [17] and that test-driven
development, in particular, and agile methods, in general,
provide better support for this idea than a documentationbased development process. As a consequence, we consider
that the JUnit approach to testing should be employed in
other contexts, not only unit testing of normal behavior.
The bottom line is that there is a need of a testing
approach to ease the development of exception handling test
cases in the following ways: (i) the developer should be able
of defining the complete or a partial exception path to be
automatically checked during tests and deviations from this
path should be considered errors; (ii) the way exception tests
are defined should be similar to the ones already defined by
developers using JUnit - to reduce the learning curve; and
finally (iii) such tests should be used as live documentation
1 http://junit.sourceforge.net/doc/faq/faq.htm#tests
8
Figure 2.
Figure 1.
Layered information system architecture.
concerning the exception flows of a program.
III. T ESTING THE E XCEPTIONAL B EHAVIOR
We have devised an agile approach to test exceptional
behavior of a system which allows the global reasoning
of the exception flows of an application, in another words,
it allows the developer to specify and examine the paths
traveled by exceptions from the points where they are thrown
to their destinations.
A. Proposed Approach
The central idea is to establish the paths whereby the most
important exceptions thrown must traverse. Exception paths
are defined by means of semi-automated tests and runtime
monitoring. Our approach supports the definition of tests
cases to be built before the system is implemented or used
as regression tests.
The exceptional behavior must be designed to conform
with the test cases. Testers can specify what exceptions
should be raised and how they flow among system components, in terms of the methods through which they pass and
the order in which this occurs. This specification is part of
the test implementation and, therefore, must be maintained
by developers (otherwise, the tests will not pass), unlike
documentation-based solutions which can remain outdated.
At runtime, the paths taken by exceptions are recorded and
compared with the expected paths, specified in the tests.
Figure 2 illustrates the components of the proposed
approach. In the figure, rectangles with rounded corners
indicate activities, a lozenge denotes alternatives, and the remaining boxes represent artifacts. Continuous lines indicate
ordering between activities and dashed lines indicate artifact
dependences. First of all, the most important exception paths
Overview of the proposed approach.
in the application must be selected (Section 3-B). These
paths are the ones traversed by exceptions that represent
application-specific errors and situations where, due to external factors, the system is unable to provide its intended
service. Since, in the early stages of development, very little
information about exception paths might be available, in
our approach, it is possible to specify partial paths that can
later be complemented with additional information. After
that, test cases are written for all the paths devised in the
first activity. We often write one test case per exception
path, but it is possible to write a single test case to test
a number of paths associated with the same exception. Test
cases are then grouped in a test suite and executed against
the system implementation. If all the tests pass, we consider
that the exceptional behavior of the system adheres to its
specification. Otherwise, inconsistencies were found. An
inconsistency might stem from maintenance activities that
changed exception propagation paths in a non-harmful way
or from bugs. After the inconsistency is fixed, tests are run
again and this cycle is repeated until the tests pass.
The specification of the expected path can use minimum
or exact path semantics. Figure 3 illustrates the difference.
The left hand side of the figure depicts an actual exception
path where an exception was raised by method d and handled
in method a. In the case of minimum path semantics, all
elements of the specified path must be part of the actual
exception path, in the order in which they were specified.
However, the actual path may contain more elements than
the specified path. Figure 3-B presents examples of specified
minimum exception paths that match the presented actual
path. Figure 3-C presents some invalid minimum paths. For
exact path semantics, actual and specified path must be
identical.
B. Test Criteria
Data test for a program P is an element of the input
domain of P. When testing program P, it is necessary to
select a few points of the domain D (P). Ideally the test
should be performed for all elements of D(P). This action
Figure 4.
Figure 3.
Exception type remapping.
Minimal and full path definition.
is quite costly, so it is ideally important to select a reduced
set of D(P) with high probability of occurrence errors[12].
Certain criteria must be taken into account when prioritizing the specification of test cases. In the following we
list some potential candidate to an unexpected behavior of
exception handling mechanism:
• Try-catch block that does not throw exception anymore.
• Handlers with generic exception type that remaps the
caught exception into another exception type.
• Blocks that raise different exception types.
• Blocks that catches a large number of exception types.
C. Framework
We have extended the JUnit framework to support the
proposed approach. This extension allows the definition of
expected exception paths within a test case, in terms of
raising, intermediate, and handling sites, and the exception
that is raised for Java programs. Figure 5 presents a real test
case example.
The expected path is an array of Strings (lines 1217) that follows some conventions to indicate raising site
(line 14), handling site (line 16), etc. The order of the
elements in this array indicate the ordering of the exception path, starting from the exception and the raising site
(always the first two items), all the way to the handling
site. All the methods that appear in the following, e.g.,
exception(), and handlingSite(), are part of the
our extension to JUnit. The exception path must be set
by invoking setExceptionPath() (line 20) before test
case execution.
The testEHFlowDAOServlet() method (line 9) implements the actual test. It sets up any components that
must be running before the test and executes the test. As in
any functional testing approach, the test is responsible for
triggering the error condition that will result in the expected
exception.
It is possible to redefine the behavior of the test result, for
example, to explicitly prohibit an exception from traveling
a certain path, by overriding the result() method.
//Super class method, optional override.
//Use to change the test result criteria.
public void result() {
//Eg: If an specific exception occurs
//test case should be fail
assertTrue(!verifyResults());
}
The implemented extension allows the generation of a log
file after tests execution. This file contains all the exception
flow information produced by test case execution. This
information is important because with it is possible to verify
what has changed from one version to another one. The
symbol # represents the exception type, the subsequent line
represents where the exception was thrown, and after that,
we have the intermediate sites. The symbol @ represents the
catch site.
#pkg.MyException
pkg.raisingSite
pkg.intermediateSite1
pkg.intermediateSite2
@pkg.catchingSite
This extension also allows testing exceptions remap one
exception type into another, see one example at figure 4.This
figure shows the execution of a(),b(), c() in this order.
Method c() raise an IOException and propagate to
method b(). Method b() catch this exception and remap
it into a MyException that is caught by method a().
Unfortunately, Junit does not support the definition of test
cases to remap exception types.
D. Monitoring exceptions
We use Aspect Oriented Programming [10] to monitor
exception propagation during test execution. The following
code snippet captures the exception object. With this object
and the local where the advice affected the code, it is
possible to define the path traversed by the exception.
1- pointcut callexecutionGeneral():
2handler(Throwable+) ;
3- before(Throwable e) :
4callexecutionGeneral() && args(e) {
5//Before handling Throwable or
6//subtypes,exception object is
7//received without any code changes.
8- }
1 - public class MyTestCase extends JuntETestCase {
23//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 - public void testEHFlowDAOServlet() {
1011//Specifies the desired exception path
12String[] trace = new String[]{
13exception("java.io.IOException"),
14raiseSite("MyDAO.insertData "),
15intermediateSite("myFacade.insertData"),
16handlingSite("myServlet.service")
17};
1819//Sets the exception path
20super.setExceptionPath(trace);
2122//Calls the element that should handle
23//the exception
24myServlet.service();
25}
26-}
Figure 5.
Test Case sample
There are situations where an exception is thrown but
there is no handler available for handle this exception. For
these cases another aspect is used:
1- pointcut callexecutionGeneralUnhandled():
2call(* *.*(..));
3- after() throwing(Throwable e) :
4callexecutionGeneralUnhandled() {
5//For each throwing exception,
6//this aspectsend the method
7//name and the exception to
8// the junit extension.
9- }
These aspects are used to verify the adherence of the
expected path to the actual one. If the actual and expected
paths match, according to the specified semantics, the test
passes.
E. Force exception
In some cases, it is difficult to reproduce the conditions
that trigger the raising of an exception. This is often the
case when an error stems from external factors, such
as I/O operations. It is possible to force an exception
in a desired place at the exceptional path by using the
@ForceException annotation in the test case definition
(figure 5 lines 2 to 6). This annotation example (lines 2-6)
enable the raise of java.io.IOException when the
PictureExporter.doExport(AudioFile,File)
method is executed.
To force an exception we have developed a preprocessor
that automatically generates exceptional aspect based on
the test case annotation. This aspect forces the throwing
of an exception. Developers only need to weave it [10]
and do not need to have any familiarity with AOP. The
following snippet code shows a sample aspect generated by
the preprocessor.
1- public aspect MyTC {
2pointcut tc():
3execution(void PictureExporter.
4-doExport(AudioFile,File));
5void around() throws
6java.io.IOException :tc() {
7throw new java.io.IOException();
8}
9- }
This aspect replaces the implementation of
PictureExporter.doExport(AudioFile,File)
method by the throwing of an IOException.
IV. E VALUATION
We have evaluated the proposed approach by applying it
in two real systems. Our idea was to simulate the creation
and execution of tests to a system throughout its development. We looked for production quality Java open source
applications with a large number of exception handlers and
several versions available. Based on these criteria we have
selected two open-source, production quality target system
with multiple versions and devised test cases for one of its
initial, stable versions.
We then applied the same test cases, refactoring them to
account for changed class named, deleted elements, etc., to
later versions of the same system. We believe this is one
of the potential usage scenarios of the proposed approach.
For this evaluation we are interested only in errors related
to exception handling mechanism like wrong exception
propagation, unhandled exceptions, etc. Another kinds of
errors are disregarded in this study.
The first target system was aTunes2 , aTunes is a powerful,
full-featured, cross-platform player and manager, with audio
cd rip frontend ”. This application has between 20K (initial
version) and 50K (last evaluated version) lines of code, and
has thirty one available versions. In spite of its relatively
small size, aTunes has a large user base (approx. 4.8K
weekly downloads at sourceforge.net). For this system we
have created 17 test cases and found 4 bugs over 6 analyzed
versions.
The second target system was jEdit3 system. jEdit is a
programmer’s text editor written in Java. It uses the Swing
toolkit for the GUI and can be configured as a rather
powerful IDE through the use of its plug-in architecture”.
Our intention with this system was evaluate the proposed
approach with a larger and more complex system. The last
jEdit version is bigger than 100k lines of code. Altogether 15
test cases were created, 8 bugs found and 5 version analyzed.
2 http://www.atunes.org/
3 http://www.jedit.org/
TC01
TC02
TC03
TC04
TC05
TC06
TC07
TC08
TC09
TC10
TC11
TC12
TC13
TC14
TC15
TC16
TC17
aTunes 1.5
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
NA
PASSED
NA
NA
NA
aTunes 1.6
PASSED
PASSED
PASSED
PASSED
PASSED-CALL
PASSED-CALL
PASSED
PASSED-CALL
PASSED
PASSED
PASSED
PASSED
NA
PASSED
NA
NA
NA
aTunes 1.9
PASSED-PKG
PASSED
PASSED-PKG
PASSED-CALL
NA
NA
PASSED-PKG
NA
NA
PASSED
FAIL
PASSED
PASSED
PASSED-CALL
PASSED
PASSED
FAILED
aTunes 1.10
FAILED
PASSED
PASSED
PASSED
NA
NA
PASSED
NA
NA
FAILED
PASSED.
PASSED
PASSED-CALL
PASSED
PASSED-PKG
PASSED
FAILED
aTunes 1.12
FAILED
PASSED
PASSED
PASSED
NA
NA
PASSED
NA
NA
FAILED
PASSED.
PASSED
NA
PASSED
NA
PASSED-CALL
FAILED
aTunes 1.13
FAILED
PASSED-PKG
PASSED-PKG
PASSED
NA
NA
PASSED-PKG
NA
NA
FAILED
PASSED
PASSED
NA
PASSED-CALL
NA
PASSED
FAILED
Table I
TC01
TC02
TC03
TC04
TC05
TC06
TC07
TC08
TC09
TC10
TC11
TC12
TC13
TC14
TC15
jEdit 4.0
PASSED
FAIL
PASSED
FAIL
PASSED
PASSED
FAIL
PASSED-PKG
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
jEdit 4.1
PASSED
FAIL
PASSED
FAIL
PASSED
PASSED
FAIL
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED
PASSED-CALL
PASSED
jEdit 4.2
PASSED
FAIL
FAIL
NA
FAIL
NA
NA
PASSED
PASSED
FAIL
FAIL
PASSED
PASSED
FAIL
FAIL
jEdit 4.3
PASSED
FAIL
NA
NA
PASSED-PKG
PASSED
NA
PASSED
NA
FAIL
FAIL
PASSED
PASSED-PKG
FAIL
FAIL
jEdit 4.3.2
PASSED-PKG
FAIL
NA
NA
PASSED-CALL
PASSED
NA
PASSED
NA
FAIL
FAIL
PASSED
PASSED
FAIL
FAIL
Table II
For both systems, some intermediate versions were not
tested due to minor changes in the exception handling code.
Table I summarizes test results for aTunes and table II for
jEdit systems. This table shows tests that passed without
restrictions (PASSED), with changing of intermediate sites
(PASSED-CALL), with changing of method or package
names (PASSED-PKG). In the latter two, test cases must
be updated in order to reflect the correct exceptional flows.
A. aTunes evaluation
The first step for this evaluation was the choice of which
version should be chosen as base version. We have started
with version 1.5 based on the stability of the code base
(there were major changes between 1.3 and 1.4). We then
proceeded to specify the expected exception paths. We have
created one test case for each exception path that has at
least two nodes that are internal to the application (none
is in an external library). The test cases made initially for
version 1.5 served as basis for the other versions. Because
of code changes between versions, some test cases had to be
redefined and new ones were created. These changes were
triggered by changes in the implementation of exceptional
behavior or changes in package and method names. In some
cases public methods and some classes were removed. In
these cases, we either modified or removed test cases.
We tested versions 1.5, 1.6, 1.9, 1.10, 1.12, and 1.13 of
aTunes.
In version 1.9, we found the first bug. Test
case TC11 that should result in raising of
java.io.FileNotFoundException,
exception
java.lang.NullPointerException was raised
instead. After manual inspection, we found that
AudioFilePictureUtils.savePictureToFile()
method always returns null object. This issue was fixed
in later versions of aTunes system.
In version 1.10, we found a situation where a handler for
java.lang.Exception was left in the system implementation but the exception that it originally captured did not
exist anymore (test case TC01 and TC10). This is a potential
source of bugs[16] for future versions of the system. By
inspecting the aTunes bug database4 , we discovered that
neither of these problems had been previously reported.
In addition, we found two bugs in subsequent versions
that had been previously reported. Since version 1.9 until
1.13, test case TC17 revealed unhandled exception. In the
execution flow started by test case startup, one method
returns a null object. This object is accessed generating
NullPointerException. Since the exception is not
handled, forces the closing of its execution. Code above
shows the issue. Method savePictureToFile() (line
1) call getInsidePicture()(line 10) that return null
(line 15). The null object is received by image (line 5)
after that image.getImage() is called (line 7) causing
the exception.
1 private static void savePictureToFile()
2
throws ... {
4
5
ImageIcon image = getInsidePicture(song);
6
7
PictureExporter.savePicture(image.getImage(),
8
file.getAbsolutePath());
9 }
10 private static ImageIcon getInsidePicture
11
(AudioFile file) {
12
FileInputStream stream = null;
13
14
try {
15
return null;
16
} catch (Exception e) {
17
return null;
18
} finally {
19
ClosingUtils.close(stream);
20
}
21 }
1-String [] trace = new String [] {
2- exception("bsh.EvalError"),
3- raiseSite("bsh.Name.toClass"),
4- intermediateSite("bsh.BSHAmbiguousName.toClass"),
5- intermediateSite("bsh.BSHType.getType"),
6- intermediateSite("bsh."+
"BSHTypedVariableDeclaration.eval"),
7- intermediateSite("bsh.Interpreter.eval"),
8- intermediateSite ("bsh.Interpreter.source"),
9- catchSite ("bsh.Interpreter.main")};
10-}
But in test case executing bsh.TargetError exception was thrown. The following log shows the executed
exception path:
#Bsh.TargetError
bsh.Interpreter.eval
bsh.Interpreter.source
@bsh.Interpreter.main
The fifth error was found at TC10 version 4.2. An
ClassCastException was thrown. The next error
(TC11) occurs because an EvalError exception was expected (line 2) but TargetError (6 lines) was thrown on
test case execution.
1- String [] trace = new String []{
2exception(bsh.EvalError "),
3raiseSite (bsh.Interpreter.eval"),
4catchSite (test.TC11.startup ")
5- };
Log of TC11 version 4.2:
6- #bsh.TargetError
7- bsh.Interpreter.eval
8- test.TC11.startup
B. jEdit evaluation
We decided to evaluate a second system, larger and
with more code related to exception handling. Initially we
specify tests for jEdit 4.0 final version. Altogether 15 test
cases were created and 8 bugs was found over 5 evaluated
versions (4.0, 4.1, 4.2, 4.3 and 4.3.1). Two test cases were
created based on bugs previously reported 5 . After test case
specification, all test cases was runned. The first bug was
found in test case TC02. This bug was caused by throwing
of NullPointerException exception.This error had
been reported previously at jEdit bug database. After the
execution of TC02, we update the test specification so, if
in the next version the same error occurs, this test should
fail. In all other versions evaluated, we observe, at TC02, the
4 http://sourceforge.net/tracker/?atid=821812&group
5 http://sourceforge.net/tracker/?group
occurrence of same issue. The second bug, at TC04, is due to
occurrence of ArrayIndexOutOfBoundsException.
Similarly to the first, it was previously known. This bug was
observed up to jEdit 4.1, after this, the target test code was
removed. The third bug was found in TC07. EvalError
exception was expected but ClassCastException was
thrown. The fourth error was found in jEdit 4.2. At TC05
the design path was:
id=161929
id=588&atid=100588
The last two errors were found in TC14 and TC15. The
running of both test cases ending with the raising of ClassCastException. The approach proved effective for finding
bugs related to the usage of exception handling mechanisms.
Altogether 32 test cases were created, and 37.5% of it have
been failed. Is also effective to indicate code changes. 12.4%
of valid tests performed, indicate code changes related to
exceptional flows modifications like method/package names
or by the addition/subtraction of intermediate sites. These
changes do not mean that test case have failed once they help
developers to understand code evolution. In this case, force
the updated of the test case, the documentation is always up
to date. The definition is simple, with two steps (definition
of exceptional flows and startup) its possible to create a test
case.
V. R ELATED W ORK
In this section, we present a set of research work directly
related to our own. They are organized in three categories:
(i) static verification approaches for exception handling code
base; (ii) testing approaches for the exception handling code
base; (iii) development processes that integrate exceptions
into the entire software development life cycle.
A. Static Verification Approaches
In the literature, several papers propose solutions based on
static analysis that allow exception control flow analysis[6],
[16], [5], [8], [14]. Exception control flow analysis is a
type of data flow analysis which estimates the paths that
exceptions will travel at runtime. Chang et al. [5] use
static analysis to estimate the exception flows to detect
unnecessary or general exception specifications (throws
clauses) or handlers in Java programs. Fähndrich et al.
[6] have developed a toolkit to discover uncaught exceptions
in ML programs using constraint-based program analysis.
Robillard and Murphy proposed a design approach for
simplifying the exception structure in Java programs [16]
and developed the JEX tool. This tool extracts information
about the flow of exceptions, providing a view of the actual
exception types that might arise at different program points
and of the handlers that are present. Fu and Ryder [8]
proposed the idea of exception chain analysis as a means
to narrow down the results produced by tools such as JEX.
Exception chain analysis attempts to reduce the number of
reported exception propagation paths by merging exception
paths that are related. Moreover, few more other tools were
created in order to facilitate understanding of exceptional
flow analysis. JEXVIS [19] uses the textual information to
generate an interactive visualization of exception structure.
EXTEST [8] presents a tree view of exception handlers, their
triggers, and their propagation paths. EXPO [20] computes
exception-handling statistics and visualizes the context of
throw-catch pairs as a flow graph. The ENHANCE tool [11]
presents four different views of exceptions in a system. The
main limitation of static analysis approaches, as well as the
associated visualization tools, is that they support discovery
but not enforcement of exception paths. If developers employ
such a tool to understand the exception structure of a system
and latter the system is modified, a large amount of effort
might be spent discovering how these changes affected
exception paths. At the same time, due to factors such as
inheritance and polymorphism, these tools often report many
false positives. Finally, static analysis is only useful when
the system has already been implemented. Therefore, they
cannot help in specification and design activities.
B. Testing Approaches
JUnit framework6, a widely used testing framework, allows developers to check whether a piece of code throws a
6 http://www.junit.org/
given exception during the execution of a test case. However,
it does not offer any construct to enable a developer to
check either a specific exception path occurred during the
execution of a test case, or whether a specific handler catches
a given exception. Sales et al. [18], [27]propose an approach
to support the definition and test of what they call exception
handling contracts. An exception handling contract specifies
the elements responsible for signaling exceptions and the
elements responsible for handling them. In their approach
JUnit tests are partially generated from such contracts. Our
proposed approach differs from this previous work as we can
specify not only the location where the program throws an
exception and where it will be treated. This approach takes
into account the fact that the overall effect of an exceptional
occurrence should consider the intermediate levels on which
the exception may propagated before reaching its handler.
Hence, our approach enables the specification on which intermediate elements should be present on an exception path.
Moreover, we added new constructs to JUnit framework in
order to allow the definition and checking of the exception
path on a test method body.
C. Development Processes
Few approaches have been proposed aiming at the definition of the system error handling behavior throughout
all the software development activities prior to implementation. Some of them incorporate exception handling-related
activities into existing software development methodologies [17], while others have targeted specific development
activities [2]. All of them, however, have a strong emphasis
in producing documentation about the exceptional behavior.
In our approach we consider such characteristic to be their
greatest limitation. It is well-known that developers often
do not keep documentation up-to-date with the system
implementation [15]. For that reason our approach relies
on the definition of automatic tests in order to specify the
behavior of the exception handling code, which will serve
as a live documentation.
VI. C ONCLUSIONS
AND
F UTURE W ORK
Software engineers are faced with the challenging task of
developing, debugging, and maintaining software systems
that contain exception handling constructs. Dealing with
exceptions since the start of software development allows
for the software engineers to produce more robust exception
handling code. But in some cases, we are faced with existing
systems that require maintenance. Exceptions complicate
programs particularly because they require a global view of
the system – to understand how exceptions affect program
execution, it is not enough to look at specific points in
the program’s source code. It is important to determine
which exceptions flow to a point in a program and where
they come from. Unfortunately, existing exception handling
mechanisms have a local focus.
For this, we have proposed an agile approach we have
proposed a new agile approach to test exceptional behavior.
To support this approach, we developed an extension of the
JUnit framework 7 . We have shown the proposed testing
approach helped us to uncover a number of bugs in two
medium-sized, production systems.
We foresee three lines of future work. First, we intend to
conduct an additional case study, to gather more evidence
about the benefits that our approach brings to maintenance
activities. Second, we would like to conduct a controlled
experiment with two groups of developers building a system
from scratch, one of them using our testing approach and
the other one not using it. Finally, we would like to evaluate
how test cases for the exceptional behavior can complement
existing static analysis techniques.
R EFERENCES
[1] Avizienis A. Toward Systematic Design of Fault-Tolerant Systems. Computer, 30(4):5158 , 1997.
[2] Aaron Shui, Sadaf Mustafiz, Jorg Kienzle: Exception-Aware
Requirements Elicitation with Use Cases. Advanced Topics in
Exception Handling Techniques. Springer, 2006, 221-242.
[3] Alessandro F. Garcia, Ceceiia M.F. Rubira, Alexander Romanovsky, and Jie Xu. 2001. A Comparitive study of exception
handling mechanisms for building dependable object-oriented
software. J. Syst. Softw. 59(2), 197-222, 2001.
[4] The Jakarta Foundation. Cactus, a Thorn in Your Bug’s Side.
http://jakarta.apache.org/cactus/. Last visit: April 18th, 2011.
[5] Chang, B.-M. et al. Interprocedural exception analysis for java.
In Proceedings of 16th ACM SAC, pp. 620-625, 2001.
[6] Fahndrich, M. et al. Tracking down exceptions in standard
ML. Technical Report CSD-98-996, University of California,
Berkeley, 1998.
[7] F. Cristian. Exception handling and software fault tolerance.IEEE Trans. Comput. 31(6):531540, 1982.
[8] Fu, C. and Ryder, B. G. Exception-Chain Analysis: Revealing
Exception Handling Architecture in Java Server Applications.
In Proceedings of ICSE’2007, 230-239, 2007.
[9] Fernando Castor Filho, Nélio Cacho, Eduardo Figueiredo,
Raquel Maranhão, Alessandro Garcia and Cecı́lia M. F. Rubira:
Exceptions and aspects: the devil is in the details. In Proc.
SIGSOFT FSE 2006, 152-162.
[10] G Kiczales, E Hilsdale, J Hugunin, M Kersten, J Palm, and
W G Griswold. Aspect-oriented programming with aspectj. In
In Proc. 15th ECOOP. Springer-Verlag, 2001.
[11] Hina Shah, Carsten Gorg, and Mary Jean Harrold. Visualization of exception handling constructs to support program
understanding. In Proceedings of the 4th ACM symposium on
Software visualization, 2008, 19-28.
7 The jUnitE extension and tests used in this evaluation is available at:
https://sites.google.com/a/cin.ufpe.br/castor/jUnitE
[12] Márcio Delemaro, Jose Maldonado, Mario Jino. Introdução
ao Teste de Software. Rio de Janeiro, Elsevier 2007.
[13] Parnas, D. L. and Wurges, H. Response to undesired events
in software systems. In Proceedings of the 2nd ICSE, 1976,
437-446, 1976.
[14] Roberta Coelho, Awais Rashid, Alessandro Garcia, Fabiano
Ferrari, Nelio Cacho, Uira Kulesza, and Arndt Staa, Carlos
Lucena. 2008. Assessing the Impact of Aspects on Exception
Flows: An Exploratory Study. In Proc. 22nd ECOOP, 207-234,
2008.
[15] Kent Beck and Cynthia Andres. Extreme Programming Explained: Embrace Change (2nd Edition). Addison-Wesley Professional, 2004.
[16] Robillard, M. P. and Murphy, G. C. Static analysis to support
the evolution of exception structure in object-oriented systems.
ACM TOSEM 12(2), 191-221, 2003.
[17] C. M. F. Rubira, R. de Lemos, G. Ferreira, F. Castor Filho, Exception handling in the development of dependable componentbased systems, Softw. – Pract. and Exp. 35 (5), 195–236, 2005.
[18] Sales Junior, R. ; Coelho, R. S. ; Lustosa Neto, V. . ExceptionAware AO Refactoring. In: IV Latin American Workshop
on Aspect Oriented Programming, 2010, Salvador. Anais do
CBSoft, 2010.
[19] Sinha, S., and Harrold, M. J. Analysis and testing of programs
with exception handling constructs. IEEE Transac- tions on
Software Engineering 26(9), 849-871, 2000.
[20] Sinha, S., and Harrold, M. J. Automated support for development, maintenance, and testing in the presence of implicit
control flow. In Proceedings of the 26th International Conference on Software Engineering, 336-345, 2004.
[21] Subhas Chandra Misra, Vinod Kumar, and Uma Kumar. 2009.
Identifying some important success factors in adopting agile
software development practices. J. Syst. Softw. 82, 2009.
[22] Jorg Kienzle. 2008. On exceptions and the software development life cycle. In Proceedings of the 4th international
workshop on Exception handling. ACM, New York, NY, USA,
2008
[23] F. Cristian. Exception Handling and Tolerance of Software
Faults. In Lyu, M.R. (ed.): Software Fault Tolerance. Wiley,
81-108, 1994.
[24] Cabral, B. and Marques, P. Exception Handling: A Field
Study in Java and .NET. In ECOOP 2007 - 21st European
Conference Object-Oriented Programming, vol. 4609 of Lecture Notes in Computer Science, pp. 151-175. Springer. 2007.
[25] Hina Shah, Carsten Gerg, and Mary Jean Harrold. Why do
developers neglect exception handling?. In Proceedings of the
4th international workshop on Exception handling (WEH ’08).
ACM, New York, NY, USA, 62-68, 2008
[26] Vincent
Partington.
Middleware
integration
testing
with
JUnit,
Maven
and
VMware.
http://blog.xebia.com/2009/12/middleware-integration-testingwith-junit-maven-and-vmware-part-1/. Last visit: April 18th
2011.
[27] Sales Junior, R., Coelho, R. S., Preserving the Exception
Handling Design Rules in Software Product Line Context: A
Practical Approach, First Workshop on Exception Handling on
Contemporary Systems (EHCoS), April, 2011.
[28] SINHA, S. AND HARROLD, M. J. Criteria for testing
exception-handling constructs in Java programs. In Proceedings of the International Conference on Software Maintenance.
Oxford, England, UK, 1999.
Apêndice B – Regras de Design excepcionais da Mobile
Media
.
A seguir são apresentados as regras de design excepcionais
encontradas no estudo de caso preliminar envolvendo a MobileMedia.
Signaler
Exception
Handler
ubc.midp.mobilephoto.core.ui.data lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
model.AlbumData.deleteImage(Strin lib.exceptions.ImageNot datamodel.AlbumData.deleteIm
g, String)
FoundException
age(String, String)
ubc.midp.mobilephoto.core.ui.data
model.ImageAccessor.loadAlbums()
lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
lib.exceptions.InvalidIma datamodel.AlbumData.getAlbu
geDataException
mNames()
ubc.midp.mobilephoto.core.ui.data lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
model.ImageAccessor.loadImageDat lib.exceptions.InvalidIma datamodel.AlbumData.getImag
aFromRMS(String)
geDataException
eNames(String)
ubc.midp.mobilephoto.core.ui.data
model.ImageAccessor.resetImageRe
cordStore()
lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
lib.exceptions.InvalidIma datamodel.AlbumData.resetIma
geDataException
geData()
ubc.midp.mobilephoto.core.ui.data
model.ImageAccessor.deleteSingleI
mageFromRMS(String, String)
lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
lib.exceptions.NullAlbum datamodel.AlbumData.deleteIm
DataReference
age(String, String)
ubc.midp.mobilephoto.core.ui.
ubc.midp.mobilephoto.core.ui.data lancs.midp.mobilephoto. datamodel.AlbumData.getImag
model.ImageAccessor.getImageInfo( lib.exceptions.NullAlbum eFromRecordStore(String,
String)
DataReference
String)
ubc.midp.mobilephoto.core.ui.data
model.AlbumData.deletePhotoAlbu
m(String)
ubc.midp.mobilephoto.core.ui.c
ontroller.BaseController.handle
lancs.midp.mobilephoto. Command(javax.microedition.lc
lib.exceptions.Persistenc dui.Command,javax.microeditio
eMechanismException
n.lcdui.Displayable)
132
ubc.midp.mobilephoto.core.ui.data
model.ImageAccessor.loadAlbums()
lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.
lib.exceptions.Persistenc datamodel.AlbumData.getAlbu
eMechanismException
mNames()
ubc.midp.mobilephoto.core.ui.data lancs.midp.mobilephoto. ubc.midp.mobilephoto.core.ui.c
model.AlbumData.getImageNames(S lib.exceptions.Unavailabl ontroller.BaseController.showI
tring)
ePhotoAlbumException mageList(String)
ubc.midp.mobilephoto.core.ui.
javax.microedition.rms.RecordStore. javax.microedition.rms.R datamodel.ImageAccessor.delet
deleteRecordStore(String)
ecordStoreException
ePhotoAlbum(String)
ubc.midp.mobilephoto.core.ui.
javax.microedition.rms.RecordStore. javax.microedition.rms.R datamodel.ImageAccessor.addI
openRecordStore(String, boolean)
ecordStoreException
mageData(String, String, String)
ubc.midp.mobilephoto.core.ui.
javax.microedition.rms.RecordStore. javax.microedition.rms.R datamodel.ImageAccessor.loadI
openRecordStore(String,boolean)
ecordStoreException
mageDataFromRMS(String)
ubc.midp.mobilephoto.core.ui.
datamodel.ImageAccessor.upda
teImageInfo(ubc.midp.mobilep
hoto.core.ui.datamodel.ImageD
javax.microedition.rms.RecordStore. javax.microedition.rms.R ata,ubc.midp.mobilephoto.core
openRecordStore(String,boolean)
ecordStoreException
.ui.datamodel.ImageData)
ubc.midp.mobilephoto.core.ui.
datamodel.ImageAccessor.loadI
javax.microedition.rms.RecordStore. javax.microedition.rms.R mageBytesFromRMS(String,
openRecordStore(String,boolean)
ecordStoreException
String, int)
ubc.midp.mobilephoto.core.ui.
datamodel.ImageAccessor.delet
javax.microedition.rms.RecordStore. javax.microedition.rms.R eSingleImageFromRMS(String,
openRecordStore(String,boolean)
ecordStoreException
String)
ubc.midp.mobilephoto.core.ui.
datamodel.ImageAccessor.upda
teImageInfo(ubc.midp.mobilep
javax.microedition.rms.R hoto.core.ui.datamodel.ImageD
javax.microedition.rms.RecordStore. ecordStoreNotOpenExce ata,ubc.midp.mobilephoto.core
closeRecordStore()
ption
.ui.datamodel.ImageData)
133