Teste de programas orientados a aspectos:uma abordagem estrutural para AspectJ
Otávio Augusto Lazzarini Lemos
SERVIÇO DE PÓS-GRADUAÇÃO DO ICMC-USP
Data de Depósito: 20 de Janeiro de 2005
Assinatura:
Teste de programas orientados a aspectos:uma abordagem estrutural para AspectJ
Otávio Augusto Lazzarini Lemos
Orientador: Prof. Dr. Paulo Cesar Masiero
Dissertação apresentada ao Instituto de Ciências Matemá-ticas e de Computação – ICMC-USP, como parte dos re-quisitos para obtenção do título de Mestre em Ciências –Ciências de Computação e Matemática Computacional.
USP - São CarlosFevereiro/2005
Agradecimentos
Agradeço aos meus pais e família pelo carinho, amor e formação que me dedicaram. Aosmeus amigos: Ricardo Menotti, Mario Pazoti, Wilson Batista, Fernando Molento, Gilberto Sté-fan, Régis Frias, Valter Camargo, Fabiano Ferrari, Sandro Bianchini, Miguel Sugai, Bruno Freitas,Luis Stucchi, Adenilso Simão, Luis Sanhermelando, José Cabral, Rogério Colman, Rodrigo Pe-dra, Paula Herculano, Débora Medeiros, Luiz Ywata, Jéssica Zorzatto, André Domingues, MarcoGraciotto, André Rocha, Antonielly Rodrigues, Carolina Ferraz, Juliane Perondi, Débora Paiva,Tatiana Sugeta, Erika Höhn, Maria Istela Cagnin, Auri Vincenzi, Camila Kozlowski, Claudio Ya-mamoto, Luciana e Richard, Simone Domingues, Ubirajara Maltez Jr., Ellen Barbosa, AndreaPadovan, Daniel Cruz, Reginaldo Ré, Vanderlei Bonato, toda a galera do LABES que cheguei aconhecer; galera da PGCOMPUSP04/futebol – amigos recentes mas nem por isso menos parceiros– Zappa, Jão, Damiance, Augusto, Manzato, Jean, Eler, Marleta, Delane, Rafael, Alberto & CIA; ea todos os outros que esqueci no momento mas que são não menos importantes. Agradeço tambémao meu orientador Paulo Cesar Masiero pela dedicada atenção, e também ao professor José CarlosMaldonado pelo grande auxílio neste trabalho.
Agradeço também a todos os funcionários e professores do ICMC, e a todas as pessoas que dealguma maneira contribuíram para a realização deste trabalho.
Agradeço ao CNPq pelo auxílio financeiro.
The aim of life is appreciation;there is no sense in not appreciating things;
and there is no sense in having more of them if you have less appreciation of them.(G. K. Chesterton)
Resumo
EM meados dos anos 90, alguns pesquisadores constataram a existên-cia de certos interesses que, independente da técnica de programa-ção utilizada ou da maneira como o sistema venha a ser decomposto,
não se encaixam em módulos individuais, mas ficam espalhados por váriasunidades do software (também chamados de interessestransversais). A pro-gramação orientada a aspectos (POA) foi concebida como uma proposta deresolução desse problema, a partir do uso de mecanismos que permitem oisolamento dos interesses transversais. Entretanto, por ser uma técnica nova,nesses primeiros anos os pesquisadores preocuparam-se em estabelecer osconceitos e técnicas básicos das linguagens orientadas a aspectos, deixandopara uma segunda fase a investigação de outras características do desenvol-vimento de programas orientados a aspectos, como métodos de projeto eabordagens de teste. Nesta dissertação é apresentada uma abordagem deteste estrutural para programas orientados a aspectos baseados na linguagemAspectJ, que pode contribuir para o aumento da confiança no software de-senvolvido utilizando essa técnica e auxiliar o entendimento das novas cons-truções e comportamentos envolvidos nesses programas. Modelos de fluxode controle e de dados baseados no código-objeto resultante da compila-ção/combinação de programas escritos na linguagem AspectJ são propostos,bem como nove critérios de teste baseados nesses modelos. Uma ferramentadesenvolvida para apoiar o teste estrutural de unidade de programas Java foiestendida para dar apoio aos modelos e critérios propostos nesta disserta-ção. Além disso, algumas propriedades do teste de integração de programasorientados a aspectos são discutidas teoricamente.
i
Abstract
I N the mid 90’s, researchers from the Software Engineering field discove-red the existence of certain concerns that can not be well modularizedusing the decompositions supported by the available programming te-
chniques (also calledcrosscuttingconcerns). The aspect-oriented program-ming (AOP) was conceived as a proposal to solve this problem, offeringmechanisms to support the modularization of crosscutting concerns. Howe-ver, being a novel technology, in the early years the researchers of AOP havefocused in establishing the underlying concepts and techniques, leaving fora future phase the investigation of other properties, such as design metho-dologies and testing approaches. This dissertation presents a structural tes-ting approach for aspect-oriented programs based on the AspectJ language,aiming at enhancing the confidence on software developed using the AOPtechnique and helping in understanding the behavior involved in such pro-grams. Control and data flow models for aspect-oriented programs based onthe bytecode resulted from the compilation of AspectJ programs are propo-sed, together with nine unit testing criteria. A prototype tool to support theapproach proposed was extended from an existing tool, and properties of theintegration testing of aspect-oriented programs are discussed theoretically.
ii
Sumário
Resumo i
Abstract ii
1 Introdução 11.1 Contextualização. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Motivação. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.4 Organização. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Revisão Bibliográfica 52.1 Considerações Iniciais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2 Programação Orientada a Aspectos. . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2.1 Linguagens de Suporte à Programação Orientada a Aspectos. . . . . . . . 82.3 Teste de Software. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
2.3.1 Técnica Funcional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .162.3.2 Técnica Estrutural. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .182.3.3 Teste Estrutural de Programas Orientados a Objetos. . . . . . . . . . . . . 242.3.4 Ferramentas de Teste. . . . . . . . . . . . . . . . . . . . . . . . . . . . .302.3.5 Teste de Programas Orientados a Aspectos. . . . . . . . . . . . . . . . . 33
2.4 Considerações Finais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .40
3 Teste Estrutural de Programas Orientados a Aspectos 413.1 Considerações Iniciais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413.2 Fases do Teste Estrutural de Programas OA. . . . . . . . . . . . . . . . . . . . . 413.3 Teste Estrutural de Unidade. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42
3.3.1 Grafo de Fluxo para Programas Orientados a Aspectos. . . . . . . . . . . 433.3.2 Critérios de Fluxo de Controle. . . . . . . . . . . . . . . . . . . . . . . . 503.3.3 Critérios de Fluxo de Dados. . . . . . . . . . . . . . . . . . . . . . . . . 553.3.4 Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .583.3.5 Comparação entre os Critérios de Teste de Unidade. . . . . . . . . . . . . 59
3.4 Teste Estrutural de Integração. . . . . . . . . . . . . . . . . . . . . . . . . . . . .603.4.1 Teste Método-adendo. . . . . . . . . . . . . . . . . . . . . . . . . . . . .60
3.5 Comparação e Avaliação da Abordagem Proposta. . . . . . . . . . . . . . . . . . 643.6 Considerações Finais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69
iii
4 Teste de Unidade: Automatização e Exemplos 704.1 Considerações Iniciais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .704.2 Extensão da Ferramenta JaBUTi para Apoio ao Teste de Unidade de Programas OA714.3 Funcionamento da Ferramenta JaBUTi/AJ e Exemplos de Aplicação. . . . . . . . 72
4.3.1 Aplicação de Figuras 2D. . . . . . . . . . . . . . . . . . . . . . . . . . .724.3.2 Aplicação de Simulação de Telefonia. . . . . . . . . . . . . . . . . . . . 79
4.4 Exemplo de Fluxo de Dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . .874.5 Considerações Finais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .94
5 Conclusão 955.1 Considerações Finais. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .955.2 Contribuições. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .965.3 Pesquisas Futuras. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .96
Referências 102
iv
Lista de Figuras
2.1 Interesse transversal de atualização da tela (adaptado do trabalho deElrad et al.(2001a)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Interesse deloggingno Tomcat (adaptado do trabalho deKersten (2002)) . . . . . 72.3 Processo de combinação. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.4 Exemplo de código em AspectJ. . . . . . . . . . . . . . . . . . . . . . . . . . . .102.5 Exemplo de definição de conjunto de junção nomeado (adaptado do livro deLad-
dad (2003)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .112.6 Exemplos de declarações inter-tipos (adaptado do livro deLaddad (2003)) . . . . . 132.7 Sintaxe da declaraçãodeclare parents (adaptado do livro deLaddad (2003)) . 132.8 Exemplo do declare error e do declare warning (adaptado do livro deLaddad (2003)) 132.9 Exemplo dobytecodegerado para o adendo posterior que afeta o conjunto de jun-
çãomudancaEstado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .142.10 Método iterativo para o cálculo de fatorial e 3 casos de teste construídos utilizando
o JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .172.11 Casos de teste para o teste dos limites das classes de equivalência construídos
utilizando o JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .182.12 Grafo de fluxo de controle do método para validação de identificadores (adaptado
do trabalho deMaldonado et al. (2003)) . . . . . . . . . . . . . . . . . . . . . . . 192.13 Grafo Def-Uso do método para validação de identificadores (adaptado do trabalho
deMaldonado et al. (2003)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .212.14 Hierarquia dos critérios deRapps e Weyuker (1982) e Potenciais-Usos (Maldo-
nado, 1991). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .232.15 Implementação parcial da classeSymbolTable . . . . . . . . . . . . . . . . . . . 272.16 Grafo de chamadas de classe para a classe de Tabela de Símbolos (adaptado do
trabalho deHarrold e Rothermel (1994)) . . . . . . . . . . . . . . . . . . . . . . . 272.17 Grafo de chamadas de classe para a classe de Tabela de Símbolos englobado por
frame(adaptado do trabalho deHarrold e Rothermel (1994)) . . . . . . . . . . . . 282.18 Grafo de fluxo de controle de classe para a classe de Tabela de Símbolos (adaptado
do trabalho deHarrold e Rothermel (1994)) . . . . . . . . . . . . . . . . . . . . . 292.19 Principais classes doframeworkJUnit (adaptado do livro deFontoura (2002)) . . . 302.20 Exemplo de caso de teste implementado utilizando o JUnit. . . . . . . . . . . . . 312.21 Tela da ferramenta JaBUTi mostrando um grafo Def-Uso de um dado método, a
partir do critério Todos-Nós-Primários.. . . . . . . . . . . . . . . . . . . . . . . . 332.22 Exemplo de motivação para o teste de programas orientados a aspectos.. . . . . . 36
v
2.23 Grafo de chamadas para o c-aspectoPointShadowProtocol e para a c-classePonto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .36
2.24 FCFG para o c-aspectoPointShadowProtocol e para a c-classePoint . . . . 372.25 Modelo FREE para classe de contas bancárias (Xu et al., 2004a). . . . . . . . . . . 372.26 Modelo ASM para classe de contas bancárias (Xu et al., 2004a). . . . . . . . . . . 382.27 Árvore de transição baseada no modelo ASM para classe de contas bancárias (Xu
et al., 2004a). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .382.28 AFG parcial baseado no modelo ASM para classe de contas bancárias (Xu et al.,
2004a). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39
3.1 Algoritmo para gerar um grafoAODU a partir de um grafoIG. . . . . . . . . . . 493.2 Exemplo de um programa orientado a aspectos escrito em AspectJ.. . . . . . . . . 503.3 Parte do bytecode do métodoaffectedMethod sem a presença do aspecto
AnAspect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513.4 Parte do bytecode do métodoaffectedMethod com a presença do aspecto
AnAspect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513.5 AODUs do métodoaffectedMethod sem e com a presença do aspectoAn-
Aspect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .523.6 Um programa simples escrito em AspectJ e osAODUs dos métodosmethodA e
methodB da classeClassA . . . . . . . . . . . . . . . . . . . . . . . . . . . . .563.7 Hierarquia dos critérios definidos no trabalho deVincenzi (2004) e neste trabalho.. 593.8 GrafosAODU utilizados como contra-exemplos.. . . . . . . . . . . . . . . . . . 603.9 GrafoMADU do métodoaffectedMethod . . . . . . . . . . . . . . . . . . . 623.10 Caminho percorrido no grafoMADU do métodoaffectedMethod , a partir
da entrada ((0, 1), 7, 10).. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .653.11 GrafoAOCFG para um dado método.. . . . . . . . . . . . . . . . . . . . . . . . 673.12 Grafo do c-método baseado no grafo do método apresentado na Figura3.11. . . . . 68
4.1 Diagrama UML da aplicação de figuras 2D orientada a aspectos.. . . . . . . . . . 724.2 Código das classesPoint , LineSegment eMain do pacotecomponent . . . . 734.3 Código dos aspectosFigureLogging ePointBoundsChecking . . . . . . . 744.4 Janela do gerenciador de projetos da JaBUTi/AJ.. . . . . . . . . . . . . . . . . . 754.5 Telas da JaBUTi/AJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .764.6 Requisitos de teste do métodoLineSegment.distance gerados pelos crité-
rios Todos-NósC , Todas-ArestasC , e Todos-UsosC . . . . . . . . . . . . . . . . . . 774.7 GrafosAODU gerados pela ferramenta JaBUTi/AJ baseados nos critérios Todos-
NósC e Todas-ArestasC , após a execução do caso de teste.. . . . . . . . . . . . . 784.8 Caso de teste para cobrir os nós transversais do métodoLineSegment.distan-
ce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .784.9 Caso de teste para cobrir a aresta transversal(60, 200) do métodoLineSegment.
distance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .784.10 AODU do adendo anterior do aspectoPointBoundsChecking . . . . . . . . . 794.11 Conjunto de casos de teste Todos-Nós-adequado para o adendo anterior do aspecto
PointBoundsChecking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .804.12 Diagrama UML das classes-base da aplicação de telefonia (adaptado da página
mantida porAspectJ Team (2004)). . . . . . . . . . . . . . . . . . . . . . . . . . . 814.13 Código das classes-base da aplicação de telefonia.. . . . . . . . . . . . . . . . . . 824.14 Código dos aspectos da aplicação de telefonia.. . . . . . . . . . . . . . . . . . . . 83
vi
4.15 Elementos requeridos para o critério todos-nós-transversais para a aplicação detelefonia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .84
4.16 Código-fonte da classeCall com os elementos requeridos do critério todos-nós-transversais em destaque.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .85
4.17 GrafosAODU de construtor e método da classeCall . . . . . . . . . . . . . . . . 864.18 Classe contendo um conjunto de casos de teste para o teste da aplicação de telefonia.864.19 Código fonte da aplicação para ilustração do critério de fluxo de dados.. . . . . . 874.20 Bytecode do métodoaMethod da classeAClass . . . . . . . . . . . . . . . . . 884.21 Requisitos de teste dos critérios Todos-NósC , Todos-ArestasC e Todos-UsosC , para
o métodoaMethod da classeAClass . . . . . . . . . . . . . . . . . . . . . . . . 894.22 Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, com base
no critério Todos-NósC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .904.23 AODU do métodoaMethod da classeAClass . . . . . . . . . . . . . . . . . . . 914.24 Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, com base
no critério Todos-UsosC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .924.25 Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, após a
seleção da definição da variávels no início do método. . . . . . . . . . . . . . . . 934.26 Casos de teste para a aplicação exemplo.. . . . . . . . . . . . . . . . . . . . . . . 93
vii
Lista de Tabelas
2.1 Tipos de conjuntos de junção do AspectJ e suas respectivas sintaxes (adaptado dolivro deLaddad (2003)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .11
2.2 Relação entre as fases de teste e o teste de programas OO (adaptado do trabalho dedos Santos Domingues (2001)) . . . . . . . . . . . . . . . . . . . . . . . . . . . .25
3.1 Diferentes classes de instruções de bytecode (Vincenzi, 2004). . . . . . . . . . . . 463.2 Requisitos de teste para cada critério OA definido, para o métodoaffectedMethod . 583.3 Pares Def-Uso aspectuais coletados a partir do grafoMADU do métodoaffected-
Method e conjuntos de casos de teste adequados para cada critério.. . . . . . . . 643.4 Total de casos de teste e de requisitos não-executáveis para cada critério definido,
para o métodoaffectedMethod . . . . . . . . . . . . . . . . . . . . . . . . . . 653.5 Relação entre as fases de teste no teste de programas OA na abordagem deste
trabalho e na abordagem deZhao (2003). . . . . . . . . . . . . . . . . . . . . . . 66
4.1 Arquivos criados na extensão da JaBUTi para JaBUTi/AJ.. . . . . . . . . . . . . . 71
viii
CAPÍTULO
1Introdução
1.1 Contextualização
No fim da década de 60 e início da década de 70, como resultado da crise ocorrida em razão
do aumento da complexidade dos programas, emergiram alguns princípios de projeto e de progra-
mação, com o objetivo de organizar e apoiar o processo de desenvolvimento de software. Nessa
época o paradigma procedimental tornou-se dominante e, devido à utilização desses princípios de
projeto e programação, possibilitou-se o desenvolvimento de código mais legível, fácil de manter
e mais flexível. Adicionalmente, boas práticas de projeto foram estabelecidas ao longo dos anos
para o paradigma procedimental.
Com a evolução desses princípios e o avanço das técnicas de programação, foi concebida a
programação orientada a objetos (POO), que tornou possível o desenvolvimento de sistemas par-
ticionados em módulos (classes) capazes de encapsular abstrações de dados juntamente com ope-
rações sobre esses dados. Esse paradigma de programação possibilitou uma melhor separação dos
diversos interesses de um sistema, com a estruturação de projetos e códigos mais próxima da ma-
neira como os desenvolvedores naturalmente idealizam os sistemas (Elrad et al., 2001b). Além
disso, entre outros benefícios, essa técnica de programação facilitou a reutilização de código de-
senvolvido em diferentes projetos, auxiliando também a modelagem de aplicações do mundo real
(Capretz, 2003).
Apesar da POO ter revolucionado as técnicas de programação, alguns problemas não foram
completamente resolvidos por suas construções e, especificamente, essa técnica não permite a
clara separação de alguns interesses, geralmente não funcionais (também chamados de interesses
transversais). Como uma proposta de resolução desse problema, em 1997 (Kiczales et al., 1997)
1
CAPÍTULO 1. INTRODUÇÃO 2
surgiu a programação orientada a aspectos (POA), que oferece mecanismos para a construção de
programas em que os interesses transversais ficam isolados e separados dos interesses funcionais,
em vez de espalhados pelo sistema. A POA não tem o intuito de ser um novo paradigma de progra-
mação, mas uma técnica para ser utilizada em conjunto com as atuais linguagens de programação
(Elrad et al., 2001b), possibilitando a construção de sistemas com maior qualidade.
O teste de software sempre foi uma atividade de grande importância no processo de desen-
volvimento de software, por fornecer evidências da conformidade dos sistemas com os requisitos
especificados, aumentar o entendimento dos programas, diminuir a possibilidade da existência de
erros e, dessa forma, aumentar a confiança no software. Desde o paradigma procedimental, muitas
técnicas têm sido propostas para auxiliar a descoberta de erros nos diversos artefatos produzidos
em um projeto de software. Estudos têm confirmado cada vez mais a importância da utilização des-
sas técnicas para assegurar a qualidade do produto final e alguns pesquisadores chegam a calcular
o custo do teste – para alguns tipos de sistema – como sendo metade de todo o custo de desenvol-
vimento de software (Harrold, 2000). Além disso, a atividade de teste se torna ainda mais crítica
na medida em que programas são utilizados para realizar atividades complexas como controle de
satélites e artefatos bélicos.
As três técnicas de teste mais conhecidas são: a funcional, a estrutural e a baseada em erros.
Elas diferem pela origem da informação utilizada para gerar e avaliar os casos de teste (Adrion
et al., 1982; Zhu et al., 1997). A técnica estrutural tem sido uma das mais utilizadas e concentra-
se na cobertura de partes do código fonte a partir dos casos de teste, sendo portanto baseada em
uma implementação específica. Para a programação procedimental e orientada a objetos, foram
propostas várias abordagens de teste estrutural, utilizando-se como base tanto o fluxo de controle
quanto o fluxo de dados da aplicação (Harrold e Soffa, 1989; Harrold e Rothermel, 1994; Frankl e
Weyuker, 1988).
A POA tem sido reconhecida como uma técnica que permite a construção de sistemas com
melhor arquitetura, facilitando a manutenção dos diferentes interesses e a legibilidade do código.
Entretanto, a sua simples utilização não evita que erros sejam introduzidos ao longo do desenvol-
vimento do software, sendo necessária a utilização de técnicas de teste para aumentar a qualidade
dos programas.
1.2 Motivação
Pelo fato de ser um tema de pesquisa muito recente, os pesquisadores da área de POA preocupa-
ram-se nestes primeiros anos em estabelecer os conceitos e técnicas básicos das linguagens orien-
tadas a aspectos (Kiczales et al., 1997; Elrad et al., 2001a,b). Numa segunda fase, que ocorre
atualmente, as pesquisas se voltam para o uso da POA em situações reais, o que sugere, por exem-
plo, a necessidade de métodos de projeto, análise de requisitos e abordagens de teste específicas
para esse contexto.
CAPÍTULO 1. INTRODUÇÃO 3
A POA acrescenta novos construtores e conceitos aos já conhecidos das linguagens de progra-
mação e que, portanto, devem ser explorados na proposta de uma abordagem de teste estrutural
adequada, já que, de modo geral, as abordagens atuais não podem ser utilizadas diretamente .Ale-
xander (2003) enfatiza que o entendimento e teste adequado de programas orientados a aspectos é
um dos principais meios para tornar essa técnica menos custosa na prática. Outros pesquisadores
têm chamado a atenção para a dificuldade de entendimento e subseqüente teste de programas orien-
tados a aspectos, e que tal problema deve ser explorado antes que a técnica possa ser efetivamente
adotada na prática (Bodkin, 2003; Huang, 2000). Além disso, percebe-se o crescente interesse
nesse assunto, com trabalhos publicados recentemente (Xu et al., 2004b,a; Xie et al., 2004; Ale-
xander et al., 2004; Zhou et al., 2004), e também com a realização do primeiro workshop sobre
o tema – o WTAOP 2005 (Workshop on Testing Aspect Oriented Programs) – que será realizado
em conjunto com a conferência mais importante da área de programação orientada a aspectos – a
AOSD (Aspect Oriented Software Development Conference).
Com isso, e levando em conta também o fato de poucos trabalhos terem sido publicados sobre
esse tema, abre-se um nicho de pesquisa interessante e importante a ser explorado, para a adoção
efetiva da POA. Além de possibilitar o aumento da confiança nos programas orientados a aspectos,
uma proposta de teste estrutural para esses programas também auxilia o entendimento das novas
construções e do comportamento envolvidos quando se utiliza essa nova técnica de programação.
1.3 Objetivo
O objetivo deste trabalho é propor uma abordagem de teste estrutural para programas orienta-
dos a aspectos, que visa a adaptar os modelos e critérios estruturais propostos para os paradigmas
procedimental e orientado a objetos para esse novo contexto, tanto para o teste baseado no fluxo de
controle quanto para o teste baseado no fluxo de dados dos programas. Dessa forma, permitir-se-á
a utilização dos critérios estruturais no teste de programas OA, explorando-se também as peculia-
ridades dessa técnica.
A abordagem abrange o teste de unidade e também algumas características do teste de integra-
ção. Adicionalmente, é tratada a implementação da abordagem de teste de unidade de programas
OA baseados na linguagem AspectJ em uma ferramenta chamada JaBUTi/AJ, estendida a partir da
ferramenta JaBUTi, resultado do trabalho deVincenzi (2004).
1.4 Organização
No Capítulo2 é apresentada uma revisão bibliográfica sobre os temas abordados no trabalho:
a programação orientada a aspectos e linguagens orientadas a aspectos, com ênfase na linguagem
AspectJ; o teste de software, com ênfase no teste estrutural; e, por fim, o teste de programas orien-
tados a aspectos. No Capítulo3 é apresentada a abordagem de teste estrutural para programas OA
CAPÍTULO 1. INTRODUÇÃO 4
baseados na linguagem AspectJ proposta, tanto para o teste de unidade quanto para o teste de inte-
gração. No Capítulo4 é discutida a automação da abordagem de teste estrutural de unidade, com
o auxílio de alguns exemplos e a apresentação da ferramenta JaBUTi/AJ – extensão da ferramenta
JaBUTi (Java Bytecode Understanding and TestIng) (Vincenzi, 2004). Finalmente, no Capítulo5
são discutidas as conclusões finais, as contribuições do trabalho e os trabalhos futuros.
CAPÍTULO
2Revisão Bibliográfica
2.1 Considerações Iniciais
Neste capítulo é apresentada uma revisão bibliográfica dos temas relevantes para a proposta
deste trabalho. Os principais conceitos da programação orientada a aspectos e das linguagens que
a apóiam são definidos, com ênfase na linguagem AspectJ. O teste de software também é abordado
genericamente, porém o teste estrutural é estudado com mais profundidade. Por fim, é feita uma
revisão bibliográfica dos trabalhos relacionados com o teste de programas OA.
2.2 Programação Orientada a Aspectos
Em Ciência da Computação, as técnicas de programação têm experimentado inúmeras trans-
formações, evoluindo desde construções de baixo nível – como as linguagens de montagem – até
abordagens de alto nível – como a programação orientada a objetos (POO) (Elrad et al., 2001a,b).
Ao elevarem-se os níveis de abstração, as diferentes técnicas têm permitido a idealização de pro-
jetos e programas mais próximos da maneira como os desenvolvedores naturalmente raciocinam.
Nesse contexto, é importante citar a influência significativa das pesquisas na área de Engenharia
de Software para o projeto de linguagens de programação. Essas pesquisas têm sido uma fonte
valiosa para a implementação de propriedades importantes que hoje são comuns nas técnicas de
programação1. Um exemplo é a maneira como as linguagens de vanguarda têm implementado o
1O projeto IMPACT do SIGSOFT da ACM explora de forma detalhada os relacionamentos entre esses dois campos.
5
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 6
tratamento de exceções que, segundoRyder e Soffa (2003), é altamente influenciada pela ênfase
dada pela Engenharia de Software à confiabilidade dos sistemas.
O conceito deinteresse(concern) foi introduzido porDijkstra (1976) há quase trinta anos, e
tem sido utilizado em Engenharia de Software para se referir tanto a atributos de alto nível de um
determinado software quanto a funcionalidades de baixo nível (Elrad et al., 2001a). De maneira
geral, um interesse pode indicar tanto um requisito funcional, quanto não funcional de um sistema.
Em um projeto de software – para fins de simplicidade, legibilidade, e conseqüente facilidade
na manutenção e maior potencial para reutilização – é importante que os vários interesses relevan-
tes para um sistema estejam localizados em módulos separados. De forma geral, todas as técnicas
de programação oferecem suporte a esse tipo deseparação de interesses2 (Dijkstra, 1976), porém
cada uma à sua maneira (utilizando sub-rotinas, procedimentos, funções, classes, APIs) e em graus
diferentes.
Em meados dos anos 90, alguns pesquisadores constataram a existência de certos interesses
que, independentemente da técnica de programação utilizada ou da maneira como o sistema ve-
nha a ser decomposto, não se encaixam em módulos individuais, mas ficam espalhados por várias
unidades do software. A partir daí, dois fenômenos interessantes foram percebidos: oespalha-
mento, que acontece quando um interesse encontra-se em diversos módulos do sistema; e oema-
ranhamento, que acontece quando há a implementação de diversos interesses dentro de um único
módulo3.
Esses tipos de interesse são geralmente não funcionais, e também são chamados de interesses
transversais, pois podem ser visualizados como integrantes de uma segunda dimensão, transversal
àquela dos módulos que normalmente implementam requisitos funcionais. Exemplos de possí-
veis interesses transversais são: políticas de sincronização, mecanismos de tolerância a falhas,
funcionalidades de qualidade de serviços (QoS), controle de acesso, rastreamento da execução de
programas e persistência (Elrad et al., 2001a).
Um exemplo de interesse transversal é apresentado na Figura2.1. O diagrama de classes re-
presenta um sistema para a edição de figuras, no qual existem duas classes concretas de elementos
de figura: Ponto e Linha. Quando as coordenadas de um ponto ou os pontos de uma linha são
alterados, é necessário que a tela seja atualizada. Dessa forma, o interesse de atualização da tela
não pertence a nenhuma das duas classes, masentrecortaambas e, por isso, pode ser entendido
como transversal a essas classes.
Para se ter uma idéia do espalhamento de um interesse transversal, na Figura2.2são mostradas
as classes do Tomcat, um servidorwebbaseado em Java. As colunas representam cada classe do
sistema, enquanto que os detalhes mostram as partes relacionadas à funcionalidade delogging.
2Na verdade, como idealizado por Dijkstra, o princípio daseparação de interessestem como objetivo dividirum domínio de conhecimento em partes menores, para poder entender objetivamente cada uma delas. Portanto, aimplementação de um sistema particionado em módulos que contêm cada um dos interesses é uma conseqüência dautilização desse princípio.
3Nesse caso,módulopode se referir a um método, classe, procedimento, ou qualquer unidade de uma determinadatécnica de programação.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 7
*
Tela
Figura ElementoDeFigura
setY()setX()getY()getX()
AtualizaçãoDaTela
getP1()getP2()setP1()setP2()
Ponto Linha2
Figura 2.1: Interesse transversal de atualização da tela (adaptado do trabalho deElrad et al.(2001a))
Figura 2.2: Interesse deloggingno Tomcat (adaptado do trabalho deKersten (2002))
Com o intuito de resolver esse problema, foi proposta a programação orientada a aspectos (POA),
uma tecnologia de suporte para a implementação dos interesses transversais de maneira localizada.
A POA é resultado de vários estudos em metaprogramação, programação orientada a assuntos,
filtros de composição, programação adaptativa e outros. O termoaspectorefere-se aos interesses
transversais que podem ser implementados em módulos separados (Kiczales et al., 1997, 2001).
A POA oferece mecanismos para que os aspectos possam ser construídos em módulos sepa-
rados e provê meios para a definição de pontos do programa onde esses aspectos possam definir
comportamento. A partir daí, um programa executável pode ser gerado, combinando os módulos
básicos com os aspectos. Dessa forma, a POA pretende dar suporte aos interesses transversais
assim como a POO tem dado suporte aos objetos (Kiczales et al., 2001).
Desde a primeira proposta de POA (Kiczales et al., 1997), muitas pesquisas têm sido feitas
nesse tema, principalmente no que diz respeito a linguagens de suporte e a aplicações. Como essa
técnica é relativamente nova, ainda existem muitos campos pouco explorados, e alguns deles estão
começando a ser abordados. Exemplos desses assuntos são: uma teoria para a POA, abordando
essa técnica a partir de perspectivas formais (Walker et al., 2003); estudos empíricos para a ava-
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 8
liação da efetividade da técnica (Murphy et al., 2001; Walker et al., 1999); projeto de programas
orientados a aspectos (Stein et al., 2002; Baniassad e Clarke, 2004; Clarke e Walker, 2002); e ve-
rificação, validação e teste de programas orientados a aspectos (Zhao, 2003; Störzer et al., 2003;
Alexander et al., 2004; Alexander, 2003; Zhou et al., 2004; Xu et al., 2004b,a; Xie et al., 2004).
2.2.1 Linguagens de Suporte à Programação Orientada a Aspectos
As linguagens orientadas a aspectos podem ser classificadas em: linguagens de domínio espe-
cífico e linguagens de propósito geral. As de domínio específico tratam somente de determinados
tipos de aspectos, ligados a apenas um domínio. Alguns exemplos são:
• COOL (Lopes, 1997): interesse delocking/exclusão mútua;
• RIDL (Lopes, 1997): interesse de invocação remota;
• D2AL (Becker, 1998): computação distribuída;
• QuO (Karr et al., 2001): política para o transporte em rede.
As linguagens de propósito geral oferecem mecanismos linguísticos que permitem codificar
qualquer tipo de aspecto em unidades separadas, e capturar locais onde esses aspectos afetam os
módulos básicos do programa (Kiczales et al., 2001). A partir daí, qualquer desenvolvedor pode
conceber um programa orientado a aspectos, utilizando as facilidades da linguagem. Para que os
aspectos possam interagir com os módulos básicos do programa (também chamados de compo-
nentes), em uma linguagem de propósito geral, é necessário que o programador possa determinar
em quais pontos da execução dos módulos básicos serão definidos comportamentos. Esses pontos
são chamados depontos de junção, e são definidos de acordo com determinadas regras (que de-
pendem da linguagem). Após a codificação dos componentes e dos aspectos com pontos de junção
determinados, é necessário um processo que os una em um programa executável, e esse processo
é chamado decombinação(weaving) (Kiczales et al., 2001). Esse processo pode ser realizado
tanto em tempo de compilação – também chamada de combinação estática – quanto em tempo de
execução – também chamada de combinação dinâmica – de acordo com a abordagem adotada pela
linguagem. Na Figura2.3são mostradas representações para os dois tipos de combinação.
De acordo com Karl Lieberherr (Elrad et al., 2001b), uma linguagem orientada a aspectos deve
determinar:
• Um modelo de pontos de junção que descrevam osganchosem que comportamentos possam
ser definidos;
• Um mecanismo de identificação dos pontos de junção;
• Unidades que encapsulem tanto especificações de pontos de junção quanto definições de
comportamento desejadas;
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 9
C − Componente
A
C
A
C
Compilação eCombinação Executável
Programa
A − Aspecto
(a) Combinação estática
Execução
C
C
ExecutávelPrograma
AA
Compilação
Combinação
(b) Combinação dinâmica
Figura 2.3: Processo de combinação
• Um processo para combinar as unidades em um programa (com o combinador).
Existem inúmeras maneiras de implementar uma linguagem orientada a aspectos. Algumas
linguagens utilizam os mecanismos de empacotamento de métodos (wrapping), outras criam novas
construções para alguma linguagem existente e outras, ainda, fornecemframeworksque permitem
a criação dos aspectos.
AspectJ
A linguagem AspectJ é uma extensão de Java criada para permitir a programação orientada a
aspectos de maneira genérica, no contexto dessa linguagem. Foi desenvolvida noXerox Palo Alto
Research Centere posteriormente foi agregada ao projetoEclipseda IBM. Basicamente, as novas
construções do AspectJ consistem em: conjuntos de junção (pointcut ) que identificam conjun-
tos de pontos de junção; adendos (advice) que definem o comportamento em um dado conjunto de
junção; construções para afetar estaticamente a estrutura dos módulos básicos do programa (decla-
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 10
rações inter-tipos e que alteram a hierarquia de classes); e os aspectos (aspect ) que encapsulam
as construções novas e as tradicionais de uma classe Java4.
Na Figura2.4 é mostrada uma implementação em AspectJ para o exemplo de interesse trans-
versal apresentado anteriormente. Essa implementação será utilizada ao longo da seção. O aspecto
AtualizacaoDaTelase encarrega de atualizar a visualização todas as vezes que alguma figura é
alterada. Como as figuras são alteradas a partir dos métodos que alteram suas coordenadas, os
adendos são definidos nas chamadas a esses métodos.
public class Ponto implements ElementoDeFigura {private int x = 0, y = 0;int getX() { return x; }int getY() { return y; }void setX( int x) { this .x = x; }void setY( int y) { this .y = y; }public void move( int dx, int dy) { x += dx; y += dy; }
}
public class Linha implements ElementoDeFigura {private Ponto p1, p2;Ponto getP1() { return p1; }Ponto getP2() { return p2; }void setP1(Ponto p1) { this .p1 = p1; }void setP2(Ponto p2) { this .p2 = p2; }public void move( int dx, int dy) { p1.move(dx,dy); p2.move(dx,dy); }
}
public aspect AtualizacaoDaTela {public pointcut mudancaEstado() :
call ( void ElementoDeFigura.move( int , int )) ||call (* Ponto. set *(*)) || call (* Linha. set *(*));
before (): mudancaEstado() {System.out.println("O estado vai mudar...");
}
after () returning : mudancaEstado() {Tela.atualiza();
}}
Figura 2.4: Exemplo de código em AspectJ
Conjuntos de Junção são utilizados para identificar pontos de junção no fluxo do programa.
A partir da especificação desses pontos, regras de combinação podem ser definidas – tal como re-
alizar uma certa ação antes ou depois da execução dos pontos de junção. Além disso, os conjuntos
de junção podem expor as informações de contexto dos pontos de junção alcançados, podendo
ser utilizadas pelas ações que afetam esses pontos. No AspectJ, os conjuntos de junção podem
conter nomes ou serem anônimos. Na Figura2.5 é mostrado um exemplo de conjunto de jun-
ção nomeado. Um conjunto de junção pode ser definido como uma combinação de conjuntos de
junção, utilizando operadores lógicos binários ‘e’ (&&) e ‘ou’ (|| – como no exemplo). Além
disso, o operador unário de negação (!) também pode ser usado quando não se quer capturar pon-
tos de junção definidos por um conjunto de junção específico. O primeiro conjunto de junção que
4A maior parte desta seção foi baseada no livro deLaddad (2003).
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 11
compõemudancaEstado , por exemplo, captura todas as chamadas ao métodomove da classe
ElementoDeFigura , que recebe dois inteiros como parâmetros e não retorna nenhum valor.
public pointcut mudancaEstado : call( void ElementoDeFigura.move(call( *call( * *
*)
Palavra−chaveTipo do
de acessoEspecificador Nome do
conjunto de juncao
conjunto de juncao
Ponto.set (..)) || Linha.set (..));
||)int,int
Figura 2.5: Exemplo de definição de conjunto de junção nomeado (adaptado do livro deLaddad(2003))
As notações coringa ‘*’, ‘..’ e ‘+’, denotam respectivamente: qualquer número de caracteres
com exceção do ‘.’, qualquer número de caracteres incluindo o ‘.’, e qualquer subclasse ou sub-
interface de um dado tipo5.
Em Java, as classes, interfaces, métodos e atributos contêm assinaturas. No AspectJ, quando se
quer especificar padrões que designam várias assinaturas contidas em um programa, são utilizados
ospadrões de assinatura. No exemplo da Figura2.5, no segundo conjunto de junção que compõe
mudancaEstado , é utilizado um padrão de assinatura que captura as chamadas aos métodos que
iniciam com a cadeiaset , pertencem à classePonto , recebem quaisquer tipos de parâmetros e
retornam quaisquer valores.
Na tabela2.1são relacionados os tipos de conjuntos de junção implementados no AspectJ, com
as respectivas sintaxes.
Tabela 2.1:Tipos de conjuntos de junção do AspectJ e suas respectivas sintaxes (adaptado dolivro deLaddad (2003))
Tipo Sintaxe
Execução de método execution(AssinaturaDeMétodo)Chamada a método call(AssinaturaDeMétodo)
Execução de construtor execution(AssinaturaDeConstrutor)Chamada a construtor call(AssinaturaDeConstrutor)
Iniciação de classe staticinicialization(AssinaturaDeTipo)Acesso de leitura de atributo get(AssinaturaDeAtributo)
Acesso de modificação de atributo set(AssinaturaDeAtributo)Execução de tratador de exceção handler(AssinaturaDeTipo)
Iniciação de objeto initialization(AssinaturaDeConstrutor)Pré-iniciação de objeto preinitialization(AssinaturaDeConstrutor)Execução de advices adviceexecution()
5tipo no AspectJ refere-se a uma classe, interface, tipo primitivo ou aspecto.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 12
Adendos são construções similares a métodos e definem ações que acontecem em um dado
ponto de junção. Existem três tipos de adendos no AspectJ:
• Anteriores (before() ), que executam antes do ponto de junção;
• Posteriores (after() ), que executam depois do ponto de junção;
• De contorno (around() ), que têm a habilidade de executar em substituição ao ponto de
junção, podendo também continuar com a execução normal ou causar a execução com o
contexto alterado, por meio do método especialproceed() .
Um adendo pode ser visto como uma sobrecarga de um ponto de junção. No exemplo da
Figura2.4são mostrados dois adendos, um anterior e outro posterior, que executam antes e depois
dos pontos de junção capturados pelo conjunto de junçãomudancaEstado , respectivamente.
O primeiro apenas imprime uma mensagem avisando que o estado está para mudar e o segundo
atualiza a tela de fato, depois dos elementos de figura terem sido alterados.
As diferenças entre métodos e adendos no AspectJ são:
• adendos não têm nome;
• adendos não podem ser chamados diretamente (é trabalho do sistema executá-los nos mo-
mentos apropriados);
• adendos não têm especificadores de acesso;
• adendos têm acesso a outras variáveis especiais além dothis :
thisJoinPoint , thisJoinPointStaticPart ethisEnclosingJoinPoint-
StaticPart . Essas variáveis possuem informações reflexivas6 referentes ao ponto de jun-
ção alcançado, como valores de parâmetros, assinatura do método em execução e dados do
objeto que chamou o método. A diferença entre elas é que athisJoinPoint encapsula
informações estáticas e dinâmicas enquanto quethisJoinPointStaticPart guarda
somente informações de contexto estático. A variávelthisEnclosingJoinPoint-
StaticPart guarda informações estáticas do ponto de junção mais próximo acima do
interceptado.
Modificações estáticas permitem afetar o programa estaticamente com declarações inter-
tipos, modificações da hierarquia de classes, e introdução de avisos e erros de compilação. As
declarações inter-tipos permitem a introdução de novos atributos e métodos nas classes básicas do
programa.
Na Figura2.6é mostrado um exemplo de aspecto que utiliza os dois tipos de declarações inter-
tipos. O aspecto implementa uma regra de negócios de saldo mínimo para contas em um sistema
6Informações reflexivas referem-se ao próprio programa sendo executado, como, por exemplo, assinaturas de mé-todos e tipos de parâmetros.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 13
de contas. A primeira declaração inter-tipos introduz um atributo na classe de contasConta , que
representa o valor mínimo do saldo. A segunda declaração introduz um método na mesma classe
Conta para obter o saldo disponível, subtraindo o mínimo do atual. O adendo posterior executa
quando alguma instância deContaPoupanca é criada, atribuindo o saldo mínimo de 25.
public aspect RegraSaldoMinimo {private float Conta.saldominimo; \\Introduz atributo
public float Conta.obtemSaldoDisponivel() { \\Introduz metodoreturn getSaldo() - saldominimo;
}
after (Conta conta) : execution (ContaPoupanca. new(..)) &&this (conta) {
conta.saldominimo = 25;}
}
Figura 2.6: Exemplos de declarações inter-tipos (adaptado do livro deLaddad (2003))
A modificação na hierarquia das classes é feita por meio da declaração inter-tiposdeclare
parents . Essa declaração permite alterar a hierarquia e indicar que alguma classe herda de
outra classe ou implementa certas interfaces. Na Figura2.7 é mostrada a sintaxe da declaração
declare parents , para os dois casos (herança e implementação de interface).
declare parents : [PadraoDeTipo] implements [ListaDeInterfaces];declare parents : [PadraoDeTipo] extends [Classe/ListaDeInterfaces];
Figura 2.7: Sintaxe da declaraçãodeclare parents (adaptado do livro deLaddad (2003))
Com a introdução dos avisos e erros de compilação, o usuário pode implementar comporta-
mentos similares às diretivas#error e #warning de alguns pré-processadores C/C++. Além
disso podem ser construídas diretivas mais complexas e poderosas.
A declaraçãodeclare error permite ao desenvolvedor declarar um erro que interrompe
a compilação e mostra uma mensagem toda a vez que o compilador encontra um dado ponto de
junção (capturado por um conjunto de junção). A construçãodeclare warning funciona de
maneira similar, porém a compilação não pára, somente a mensagem é mostrada. Na Figura2.8são
exemplificados os dois comandos. O primeiro é utilizado para interromper a compilação toda vez
que um código de terceiro considerado perigoso é chamado, e o outro para emitir uma mensagem
de aviso quando operações bloqueantes são chamadas, aconselhando o usuário a se assegurar de
que não estão sendo feitas a partir dethreadsAWT.
Implementação dos Adendos: para ser compatível com qualquer máquina virtual Java, o
compilador do AspectJ gera, a partir do código-fonte oubytecodedos componentes e código-fonte
dos aspectos,bytecodeJava comum, obtido por meio da combinação de componentes e aspectos.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 14
declare error : chamadaCodigoPerigoso(): "Este codigo de terceiro tem apresentado muitos problemas.";
declare warning : chamadaOperacoesBloqueantes(): "Por favor certifique-se que estas operacoes n\~{a}o sejamchamadas a partir de uma thread AWT.";
Figura 2.8: Exemplo do declare error e do declare warning (adaptado do livro deLaddad (2003))
Para que isso seja possível, os aspectos são transformados em classes e os adendos são transforma-
dos em métodos comuns. Os parâmetros passados para esses métodos são os mesmos parâmetros
dos adendos, possivelmente acrescentados das variáveis que contêm informações reflexivas (por
exemplo, othisJoinPoint ). O corpo do método é o mesmo do adendo, com exceção do trata-
mento especial que é dado para os adendos de contorno, quando o métodoproceed() é invocado
(Hilsdale e Hugunin, 2004). Na Figura2.9é mostrado o exemplo de adendo posterior que atualiza
a tela, apresentado no início da seção, e sua respectiva transformação embytecode.
after () returning : mudancaEstado() {Tela.atualiza();
}
public void ajc$afterReturning$AtualizacaoDaTela$1af()0: invokestatic #38 <Method void Tela.atualiza()>3: return
Figura 2.9: Exemplo dobytecodegerado para o adendo posterior que afeta o conjunto de junçãomudancaEstado
A implementação da execução dos adendos é feita da seguinte maneira: primeiramente o com-
pilador identifica os possíveis pontos de junção no programa e, de maneira geral, coloca uma
chamada ao método correspondente ao adendo antes, depois ou ao invés do ponto de junção, de
acordo com o tipo do adendo, se é anterior, posterior ou de contorno. Além disso, como alguns
pontos de junção só podem ser resolvidos em tempo de execução, para estes casos são adiciona-
dos alguns resíduos nobytecodepara realizar as checagens dinâmicas necessárias. Um exemplo
de resíduo pode ser uma instrução condicionalif que checa o tipo de um determinado parâmetro,
colocada antes da chamada ao método correspondente a um adendo, quando a execução do adendo
depende do tipo do parâmetro (Hilsdale e Hugunin, 2004).
2.3 Teste de Software
Muitas propostas de técnicas e ferramentas para a verificação, validação e teste de software
têm sido apresentadas no contexto da grande área agregada sob o nome de Garantia de Qualidade
de Software (GQS). Dentre elas, o teste é uma das mais utilizadas e consiste na execução de um
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 15
produto com a intenção de revelar erros7. Sua importância se deve ao fato das outras atividades de
GQS serem insuficientes para a descoberta dos erros introduzidos ao longo do desenvolvimento do
software (Pressman, 2002; Myers, 1979).
Em um projeto de software, os objetos submetidos ao teste podem incluir programas, especifi-
cação de requisitos e de projeto, estruturas de dados e quaisquer outros artefatos conceitualmente
executáveis utilizados na implementação da aplicação. Esses objetos representam uma função que,
por sua vez, descreve a relação entre um elemento de entrada (chamado deelemento do domínio)
e um elemento de saída. A atividade de teste, por conseguinte, verifica se uma dada função está
corretamente representada por um programa (ou qualquer outro objeto conceitualmente executável
que esteja em teste) (Adrion et al., 1982).
Para se testar o software, são necessários cinco elementos: um programa executável, a descri-
ção do comportamento esperado (obtida por meio de umoráculo), a observação da execução do
programa, a descrição do domínio funcional e um método para determinar se o comportamento
observado corresponde ao esperado (Adrion et al., 1982). Dessa forma, podem ser definidas qua-
tro etapas nessa atividade: planejamento da atividade de teste, projeto dos casos de teste, execução
dos casos de teste e avaliação dos resultados (Pressman, 2002).
Geralmente, a atividade de teste é realizada em três fases:
• Teste de unidade: Nessa fase procura-se identificar erros na lógica e na implementação de
cada módulo do software, isoladamente. No paradigma procedimental, o teste de unidade
consiste em testar um procedimento separadamente do sistema e também pode ser chamado
de teste intraprocedimental. Equivalentemente, no desenvolvimento orientado a objetos, o
teste de unidade é chamado intra-método8 e consiste no teste individual de cada método.
Para se testar as unidades de um sistema, tem-se a necessidade da implementação de uni-
dades pseudo-controladoras e pseudo-controladas (drivers e stubs). As unidades pseudo-
controladoras são responsáveis por coordenar e ativar a unidade que está sendo testada,
passando os dados de teste fornecidos pelo testador, e as pseudo-controladas, por sua vez,
consistem em implementações que substituem unidades que são chamadas pela unidade em
teste.
• Teste de integração: Nessa fase procura-se descobrir erros nas interfaces dos módulos, du-
rante a integração da estrutura do programa. No caso de um programa orientado a objetos,
por exemplo, um tipo de teste de integração consiste em testar cada método juntamente com
os métodos chamados direta ou indiretamente por ele dentro da mesma classe, e também é
chamado de teste inter-método.7Neste texto, é utilizado o termoerro como sinônimo dos termosdefeito, enganoe erro do padrão IEEE 610.12-
1990, e o termofalhapara indicar um comportamento incorreto do programa (Standards Coordinating Comittee of theComputer Society of the IEEE, 1990). De maneira geralerro se refere à causa efalhaà conseqüência.
8Neste texto, ométodoé considerado como a menor unidade de um programa orientado a objetos.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 16
• Teste de sistema: Nessa fase procura-se identificar erros nas funções e características de
desempenho do sistema como um todo e, para isso, geralmente é utilizada a especificação.
Um caso de teste é um par ordenado(d, S(d)) tal qued é um elemento de um determinado
domínioD (d ∈ D) e S(d) é a saída esperada para uma dada função da especificação, quando
d é utilizado como entrada. Uma verificação completa de um determinado programaP poderia
ser obtida testandoP com um conjunto de casos de testeT que inclui todos os elementos do
domínio. Entretanto, como geralmente o conjunto de elementos do domínio é infinito ou muito
grande, torna-se necessária a obtenção de subconjuntos desses casos de teste.
Para isso, podem ser utilizados critérios de teste que auxiliam o testador fornecendo: 1) um
método para a avaliação de conjuntos de casos de teste e 2) uma base para a seleção de casos de
teste. No primeiro caso os critérios de adequação servem para evidenciar a suficiência da atividade
de teste e, no segundo caso, para ajudar na construção de casos de teste (Frankl e Weyuker, 2000).
Os critérios de teste são geralmente derivados a partir de três técnicas conhecidas: funcional,
estrutural e baseada em erros, que diferem pela abordagem de teste subjacente utilizada para gerar
e avaliar os casos de teste (Zhu et al., 1997).
A técnica funcional, também chamada de teste “caixa-preta”, tem o objetivo de determinar se
o programa satisfaz aos requisitos funcionais e não-funcionais encontrados na especificação. A
técnica estrutural, por sua vez, concentra-se na cobertura de partes do código fonte a partir dos
casos de teste, sendo portanto baseada em uma implementação específica (e por esse fato é conhe-
cida também por teste “caixa-branca”). Por fim, a técnica baseada em erros utiliza informações
sobre os erros mais freqüentemente encontrados no desenvolvimento de software para derivar os
requisitos de teste. Essas técnicas são consideradas complementares, sendo que uma estratégia de
utilização se torna interessante para explorar as vantagens de cada critério. Nesse contexto, têm
sido realizados vários estudos teóricos e empíricos envolvendo a aplicação das diferentes técnicas
de teste (Zhu et al., 1997).
Apesar de ser impossível provar que um programa está absolutamente correto por meio de tes-
tes – para praticamente qualquer programa (Myers, 1979) –, a sua utilização fornece evidências da
conformidade com as funcionalidades especificadas e, dessa forma, uma atividade de teste bem re-
alizada aumenta a confiança no produto, além de auxiliar no entendimento dos artefatos testados.
Evidências da importância do teste de software são os investimentos em pesquisas não somente
acadêmicas mas também por empresas como aTelcordia Technologiese Siemens Corporate Re-
search(EUA) (Agrawal et al., 1998; Hutchins et al., 1994), e organizações como a NASA (EUA)
(Waligora e Coon, 1995).
2.3.1 Técnica Funcional
O principal objetivo da utilização da técnica funcional é encontrar discrepâncias entre o pro-
grama e sua especificação externa (Myers, 1979) e, para isso, procura-se derivar requisitos de teste
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 17
que exercitem completamente as funções especificadas (Pressman, 2002). É chamada também de
teste caixa-preta, pelo fato de tratar o software como uma caixa com conteúdo desconhecido e do
qual só se visualiza o lado externo (dados de entrada e saída). Alguns exemplos de critérios da
técnica funcional são:
• Teste exaustivo: O critério mais óbvio para a técnica funcional é o teste exaustivo que con-
siste no uso de cada condição de entrada como um caso de teste (Adrion et al., 1982; Myers,
1979) e, conforme observado, geralmente é impraticável (considere por exemplo, o teste de
um compilador para o qual o conjunto de entradas é infinito).
• Particionamento em classes de equivalência: O critério particionamento em classes de equi-
valência consiste em particionar o conjunto de elementos do domínio em um número finito
de classes de equivalência de maneira que o teste de um elemento representativo de uma
classe seria equivalente ao teste de qualquer outro elemento da mesma classe. A idéia é que
se um caso de teste para um dado elemento de uma classe revela um erro, todos os outros
casos de teste para os outros elementos da mesma classe revelariam o mesmo erro. Equi-
valentemente, se um caso de teste não revela nenhum erro, espera-se também que nenhum
outro caso de teste na mesma classe o revelaria (Myers, 1979).
...public long factorial( int num){
if (num < 0)return -1;
long temp_fact0 = 1;long temp_fact1 = 1;boolean out_of_bounds = false ;while (num > 0 && !out_of_bounds) {
temp_fact1 = temp_fact0;temp_fact0 = temp_fact0 * num;if (temp_fact1 > temp_fact0)
out_of_bounds = true ;num--;
}if (out_of_bounds)
return -2;return temp_fact0;
}...
...public void testNegativeNumber() {
long f = fi.factorial(-34);assertEquals(-1, f);
}
public void testValidNumber() {long f = fi.factorial(10);assertEquals(3628800, f);
}
public void testOutOfBoundsNumber() {long f = fi.factorial(212);assertEquals(-2, f);
}...
Figura 2.10: Método iterativo para o cálculo de fatorial e 3 casos de teste construídos utilizandoo JUnit
Na Figura2.10é mostrado um pequeno método iterativo escrito em Java para o cálculo do
fatorial de um número (factorial ). Para esse método, poderia ser realizado um teste
funcional utilizando o critério particionamento em classes de equivalência, particionando-se
o domínio em 3 classes de equivalência: uma classe inválida correspondente aos núme-
ros negativos (x < 0), uma classe válida correspondente aos números dentro dos limites
(0 ≤ x ≤ 20) e uma classe inválida correspondente aos números cujos fatoriais excedam os
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 18
limites de uma variável do tipolong (x > 20). Para essas 3 classes poderiam ser criados
3 casos de teste (como os criados utilizando o framework JUnit (Beck e Gamma, 2002),
e exemplificados na Figura2.10), testando um elemento representativo de cada classe de
equivalência (no exemplo foram escolhidos os elementos−34, 10 e212).
...public void testOutOfBoundsLimitNumber() {
long f = fi.factorial(21);assertEquals(-2, f);
}
public void testValidLimitNumber() {long f = fi.factorial(20);String s = "2432902008176640000";long l = Long.valueOf(s).longValue();assertEquals(l, f);
}
public void testValidLimitNumber2() {long f = fi.factorial(0);assertEquals(1, f);
}
public void testNegativeLimitNumber() {long f = fi.factorial(-1);assertEquals(-1, f);
}...
Figura 2.11: Casos de teste para o teste dos limites das classes de equivalência construídosutilizando o JUnit
• Análise de valor limite: Análise de valor limite é um critério funcional que complementa o
particionamento em classes de equivalência, exigindo que existam casos de teste para testar
os limites de cada classe de equivalência. Esse critério é interessante pois geralmente os erros
ocorrem nos limites dos domínios de entrada (Pressman, 2002). Para o exemplo apresentado
anteriormente, poder-se-ia acrescentar casos de teste para testar os elementos−1, 0, 20 e
21, testando assim os limites das classes de equivalência (como pode ser visualizado na
Figura2.11).
• Grafo causa-efeito: O critério grafo de causa-efeito explora a fraqueza dos outros critérios
funcionais que não combinam as circunstâncias de entrada, mas apenas exigem o teste de um
subconjunto delas (Myers, 1979). Para isso verifica o efeito combinado de dados de entrada,
identificando as causas (condições de entrada) e os efeitos (ações), e juntando-os em um
grafo. A partir desse grafo é montada uma tabela de decisão que é utilizada na geração dos
casos de teste (Maldonado e Fabbri, 2001).
2.3.2 Técnica Estrutural
A técnica estrutural é vista como complementar à técnica funcional e baseia-se na estrutura de
um programa para derivar seus casos de teste. Em geral, os critérios dessa técnica utilizam ografo
de fluxo de controleou grafo de programa. Esse grafo ilustra o fluxo de controle lógico de um
programa utilizando uma notação padrão (Pressman, 2002).
Considerando um programaP , constituído de vários comandos, um bloco consiste em um con-
junto de comandos no qual a execução do primeiro comando do bloco acarreta a execução de todos
os outros comandos do mesmo bloco, na seqüência determinada. Sendo assim, cada comando de
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 19
um bloco, possivelmente com exceção do primeiro, tem um único predecessor e exatamente um
sucessor, exceto possivelmente o último comando. Um grafo de fluxo de controle consiste em uma
correspondência entre nós e blocos de um programa, que indica possíveis fluxos de controle entre
esses blocos através de arcos (Zhu et al., 1997).
No que diz respeito ao grafo de fluxo de controle, alguns conceitos são importantes para o seu
uso na técnica estrutural. Dado um grafo de programaG = (N, E, s) em queN é o conjunto de
nós,E o conjunto de arestas es o nó de entrada, umcaminhocorresponde a uma seqüência finita
de nós(n1, n2, ..., nk), parak ≥ 2, tal que existam arcos deni parani+1 parai = 1, 2, ..., k − 1.
Um caminho é ditosimplesse todos os nós que compõem esse caminho, exceto possivelmente o
primeiro e o último, forem distintos. Se todos forem distintos, incluindo o primeiro e o último, o
caminho é ditolivre de laço. Osnós de entradaenós de saídacorrespondem, respectivamente, aos
nós que não possuem nenhum antecessor e aos nós que não possuem nenhum sucessor, em outras
palavras, os nós de entrada não possuem nenhum arco de entrada e os nós de saída não possuem
nenhum arco de saída (Zhu et al., 1997). Um caminhocompletoé um caminho que vai do nó de
entrada a um nó de saída do grafo de programa.
Outro termo importante no teste estrutural é o de caminhosnão executáveis. Um caminho não
executável é um caminho do grafo de programa impossível de ser coberto para qualquer elemento
do domínio de entrada. Isso acontece quando as condições lógicas que deveriam ser satisfeitas
para que a seqüência de nós do caminho fosse executada são contraditórias (Howden, 1987).
A partir do grafo de programa, podem ser escolhidos os caminhos a serem executados, com o
apoio dos diferentes critérios da técnica estrutural. Considerando um método para a validação de
identificadores, na Figura2.12é mostrado o código e o grafo de fluxo de controle referente a ele.
Os critérios de teste estruturais são baseados na idéia de que não se pode confiar em uma
atividade de testes se existem certos caminhos que ainda não foram executados no programaP
sendo testado. Esses critérios geralmente associam um conjuntoT de casos de teste (que contém
um subconjunto das entradas do programa) com um conjuntoΠ de caminhos no grafo de fluxo
de controle deP , que são percorridos quando esses casos de teste são executados. O conjunto de
casos de testeT satisfaz o critérioC paraP (“T é C-adequado para o teste deP ”) se, e somente
se, todo caminho requerido porC é um sub-caminho de um dos caminhos deΠ (Frankl e Weyuker,
1988).
Os primeiros critérios estruturais propostos são baseados apenas no fluxo de controle da apli-
cação. Por outro lado, existem também critérios baseados na análise de fluxo de dados dos progra-
mas.
Critérios Baseados em Fluxo de Controle
Entre os critérios baseados em fluxo de controle, os mais conhecidos são:
• Todos-Nós: Este critério exige que cada comando do programa seja executado ao menos uma
vez, ou seja, que cada nó do grafo de programa seja coberto. Logicamente, a cobertura de
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 20
public static boolean verify(String s) {/* 01 */ if (s == null || s.length() == 0)/* 02 */ return false ;/* 03 */ char achar;/* 03 */ boolean valid_id;/* 03 */ valid_id = true ;/* 03 */ achar = s.charAt(0);/* 03 */ valid_id = valid_s(achar);/* 03 */ if (s.length() == 1 && valid_id)/* 04 */ return true ;/* 05 */ int i = 1;/* 06 */ while (i < s.length()) {/* 07 */ achar = s.charAt(i);/* 07 */ if (!valid_f(achar))/* 08 */ valid_id = false ;/* 09 */ i++;/* 09 */ }/* 10 */ if (valid_id && (s.length() <= 6))/* 11 */ return true ;/* 12 */ return false ;/* 12 */ }
1
2 3
4 5
6
7 10
8
9
11 12
13
Figura 2.12: Grafo de fluxo de controle do método para validação de identificadores (adaptadodo trabalho deMaldonado et al. (2003))
cada sentença é uma necessidade no teste de qualquer tipo de programa, mas geralmente este
é um critério fraco pois na prática não revela mesmo os erros mais simples (Myers, 1979).
• Todos-Arcos: Todos-Arcos requer que cada desvio do fluxo de controle do programa seja
exercitado pelo menos uma vez, ou seja, que cada arco do grafo seja coberto. No grafo de
programa para o método de validação de identificadores apresentado na Figura2.12, é fácil
ver que, para este caso, a cobertura de todos os nós do grafo não implica na cobertura de
todos os arcos. Por exemplo, pode-se ter casos de teste que passem pelos caminhos(1, 2),
(1, 3, 4), (1, 3, 5, 6, 7, 8, 9, 6, 10, 11) e (1, 3, 5, 6, 10, 12) e que cobrem todos os nós, porém
não exercitam o arco7− 9.
• Todos-Caminhos: O critério Todos-Caminhos requer que todos os caminhos possíveis do
grafo de fluxo de controle sejam executados. Esse critério é o mais exigente do teste caixa-
branca e o número de requisitos de teste gerados para ele, mesmo em um programa simples,
pode ser astronomicamente grande (possivelmente infinito) (Myers, 1979). Na Figura2.12,
por exemplo, em decorrência do laço(6, 7, 8, 9, 6), tem-se inúmeros caminhos no grafo.
Critérios Baseados em Fluxo de Dados
Para os critérios baseados no fluxo de dados de uma aplicação é utilizado o grafo Def-Uso,
que é construído a partir do grafo de programa com informações adicionais sobre as definições e
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 21
public static boolean verify(String s) {/* 01 */ if (s == null || s.length() == 0)/* 02 */ return false ;/* 03 */ char achar;/* 03 */ boolean valid_id;/* 03 */ valid_id = true ;/* 03 */ achar = s.charAt(0);/* 03 */ valid_id = valid_s(achar);/* 03 */ if (s.length() == 1 && valid_id)/* 04 */ return true ;/* 05 */ int i = 1;/* 06 */ while (i < s.length()) {/* 07 */ achar = s.charAt(i);/* 07 */ if (!valid_f(achar))/* 08 */ valid_id = false ;/* 09 */ i++;/* 09 */ }/* 10 */ if (valid_id && (s.length() <= 6))/* 11 */ return true ;/* 12 */ return false ;/* 12 */ }
up={s}
1
2 3
4 5
6
7 10
8
9
11 12
13
up={s}up={s}
d={achar, valid_id}
up={s,valid_id}up={s,valid_id}
d={i}
up={i,s}
d={achar}uc={achar, s}
d={i}uc={i}
d={valid_id}
up={i,s}
d ={s}
d=definiçãouc=c−usoup=p−uso
uc={achar, s}
up={s}
Figura 2.13: Grafo Def-Uso do método para validação de identificadores (adaptado do trabalhodeMaldonado et al. (2003))
subseqüentes usos das variáveis contidas em um programa. Uma definição de variável ocorre toda
vez que um valor é atribuído a uma variável. O uso de variáveis, por sua vez, pode ser de dois tipos:
uso computacional(c-uso) euso predicativo(p-uso). O primeiro refere-se a sentenças do tipo
y = f(x1, ..., xn), em que variáveis são utilizadas em uma computação. O uso predicativo acontece
em sentenças do tipoif p(x1, ..., xn), em que o valor das variáveis é utilizado como condição de
desvios (não somente em sentençasIF-THEN-ELSE mas também em laços). A partir daí, se
existe alguma sentença em um bloco contendo um c-uso ou definição de uma variável, adiciona-se
essa informação ao nó referente no grafo. Já os p-usos são associados aos arcos do grafo, posto que
o valor das variáveis afeta a seqüência de execução do programa. Umcaminho livre de definição
para uma variávelx é um caminho(n1, ..., nm), m ≥ 0, no qual não há definições dex nos nós
n2, ..., nm (Rapps e Weyuker, 1982). Outro conceito importante é o depar Def-Uso, que se refere
a um par de definição e subseqüente c-uso ou p-uso de uma variável. Na Figura2.13é mostrado o
grafo Def-Uso do método que valida identificadores.
Para que as informações de definição e uso das variáveis sejam adicionadas ao grafo Def-uso,
cada nó do grafo é associado aos conjuntosc-usoe def, e cada aresta ao conjuntop-uso. def(i) é
o conjunto de variáveis definidas no nói; c-uso(i) é o conjunto de variáveis para as quais existem
c-usos emi; e p-uso(i, j) é o conjunto de variáveis para as quais existem p-usos na aresta(i, j)
(Rapps e Weyuker, 1982). Definem-se ainda os seguintes conjuntos:
• dcu(x, i) é o conjunto de todos os nósj tais quex ∈ c-uso(j) e para os quais existe um
caminho livre de definição parax dei a j (Rapps e Weyuker, 1982).
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 22
• dpu(x, i) é o conjunto de arestas(j, k) tais quex ∈ p-uso(j, k) e para as quais existe um
caminho livre de definição parax dei a (j, k) (Rapps e Weyuker, 1982).
SejaΠ o conjunto de caminhos completos para um grafo Def-Uso de um dado programa. Diz-
se que um nói está incluído emΠ seΠ contém um caminho(n1, . . . , nm) tal quei = nj para
algumj, 1 ≤ j ≤ m. Similarmente, uma aresta(i1, i2) é incluída emΠ seΠ contém um caminho
(n1, . . . , nm) tal quei1 = nj e i2 = nj+1 para algumj, 1 ≤ j ≤ m− 1. Um caminho(i1, . . . , ik) é
incluído emΠ seΠ contém um caminho(n1, . . . , nm) tal quei1 = nj, i2 = nj+1, . . . , ik = nj+k−1
para algumj, 1 ≤ j ≤ m− k + 1.
Para o propósito deste trabalho, dentre os critérios baseados em fluxo de dados são definidos
os seguintes:
• Todas-Definições: Um conjuntoΠ de caminhos completos executados por um conjunto de
casos de teste satisfaz o critério Todas-Definições se para cada nói do grafo Def-Uso e para
cadax ∈ def(i), Π inclui um caminho livre de definição parax de i a algum elemento de
dcu(x, i) oudpu(x, i) (Rapps e Weyuker, 1982).
• Todos-C-Usos: Um conjuntoΠ de caminhos completos executados por um conjunto de casos
de teste satisfaz o critério Todos-C-Usos se para cada nói do grafo Def-Uso e para cada
x ∈ def(i), Π inclui um caminho livre de definição parax dei a cada elemento dedcu(x, i)
(Rapps e Weyuker, 1982).
• Todos-P-Usos: Um conjuntoΠ de caminhos completos executados por um conjunto de casos
de teste satisfaz o critério Todos-P-Usos se para cada nói do grafo Def-Uso e para cada
x ∈ def(i), Π inclui um caminho livre de definição parax dei a cada elemento dedpu(x, i)
(Rapps e Weyuker, 1982).
• Todos-Usos: Um conjuntoΠ de caminhos completos executados por um conjunto de casos
de teste satisfaz o critério Todos-Usos se para cada nói do grafo Def-Uso e para cadax ∈def(i), Π inclui um caminho livre de definição parax de i a cada elemento dedcu(x, i) e a
cada elemento dedpu(x, i) (Rapps e Weyuker, 1982).
• Todos-Potenciais-Usos: Um conjuntoΠ de caminhos executados por um conjunto de casos
de teste satisfaz o critério Todos-Potenciais-Usos se para cada nói do grafo Def-Uso e para
cadax ∈ def(i), Π inclui um caminho livre de definição parax de i a cada nój do grafo
Def-Uso (pois emj pode haver um uso dex).
Comparação entre Critérios de Teste Estruturais
Apesar da dificuldade existente na comparação entre os diferentes critérios de teste, esse tipo de
estudo é muito desejável, pois permite o estabelecimento de estratégias na aplicação das diversas
técnicas de teste. Nesse tipo de teorização é sempre importante ter em mente em que sentido um
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 23
critério pode ser consideradomelhorque outro. Os três tipos de medida encontrados na literatura
são: habilidade na detecção de erros, confiabilidade do software e custo de aplicação (Zhu et al.,
1997).
No que diz respeito à habilidade na detecção de erros, os métodos para comparação de cri-
térios de adequação podem ser classificados em três tipos: experimentação estatística, simulação
e análise formal. Em análises formais geralmente se define alguma relação entre os critérios de
adequação, provando para cada par de critérios se a relação é verdadeira ou não.
A relação deinclusãoevidencia o poder relativo entre os diferentes critérios de teste (Ntafos,
1988). Um critério C1 inclui um critérioC2 se para qualquer programaP , qualquer conjunto
de casos de testeC1-adequado for tambémC2-adequado. Um critérioC1 inclui estritamenteum
critério C2, denotado porC1 ⇒ C2, seC1 inclui C2 e existe um programaP e um conjunto de
casos de testeC2-adequado que não éC1-adequado. Além disso, seC1 não incluiC2 e C2 não
inclui C1, C1 eC2 são ditosincomparáveis.
Rapps e Weyuker (1982) mostraram que seus critérios de fluxo de dados formam uma hierar-
quia de acordo com a relação de inclusão, por essa relação estabelecer uma ordem parcial9. O
mesmo foi feito porMaldonado (1991), incluindo nessa hierarquia a família de critérios Potenci-
ais Usos. Na Figura2.14(a)é mostrada a hierarquia formada pelos critérios deRapps e Weyuker
(1982) e deMaldonado (1991).
Geralmente, algumas restrições precisam ser impostas aos programas para que se possa de-
monstrar a hierarquia de inclusão dos critérios. No caso da família de critérios deRapps e Weyuker
(1982), a análise de inclusão se aplica para programasP que satisfaçam as seguintes propriedades:
• NSUP –No-Syntactic-Undefined-P-Use Property: Para cada p-uso de uma variável x em
uma aresta(i, j), emP , existe algum caminho do nó de entrada para a aresta(i, j), o qual
contém uma definição global10 de x.
• NSL – No-Straight-Line Property: P contém ao menos um comando condicional ou de
repetição:
– Pelo menos um nó do grafo de fluxo de controle deP tem mais de um sucessor;
– Pelo menos uma variável tem um p-uso emP .
Frankl e Weyuker (1988) observaram que, na realidade, na presença de caminhos não-executá-
veis, nenhum dos critérios de fluxo de dados deRapps e Weyuker (1982) inclui o critério todas-
arestas. Já os critérios Potenciais Usos exigem menos restrições aos programas para que a análise
9Uma relação de ordem parcial é uma relação que possui as propriedades da reflexividade, antissimetria e transiti-vidade.
10Um nói possui uma definição global de uma variávelx se ocorre uma definição dex no nói e existe um caminholivre de definição dei para algum nój 6= i ou alguma aresta que contém um c-uso ou um p-uso, respectivamente, davariável x.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 24
Todos-Ca m in h os
Todos-Pot-D U -Ca m in h os
Todos-Pot-U sos/ D U
Todos-D U -Ca m in h os
Todos-Pot-U sos
Todos-U sos
Todos-C-U sos-A lg u m -P-U so
Todos-P-U sos-A lg u m -C-U so
Toda s-D e fs
Todos-P-U sos
Toda s-A re sta s
Todos-N os
(a) Caminhos executáveis
Todos-Ca m in h os
Todos-Pot-D U -Ca m in h os
Todos-Pot-U sos/ D U
Todos-D U -Ca m in h os
Todos-Pot-U sos
Todos-U sos
Toda s-A re sta s
Todos-C-U sos-A lg u m -P-U so
Todos-P-U sos-A lg u m -C-U so
Todos-N os
Toda s-D e fs
Todos-P-U sos
(b) Caminhos não executáveis
Figura 2.14: Hierarquia dos critérios deRapps e Weyuker (1982) e Potenciais-Usos (Maldonado,1991).
de inclusão se aplique. Na verdade, tais critérios exigem apenas que a propriedadeLDEN – at-
Least-oneDefinition-in-the-Entry-Node(ao menos uma definição de variável no nó de entrada)
– seja satisfeita. Na Figura2.14(b)é mostrada a hierarquia de inclusão dos critérios deRapps e
Weyuker (1982) e deMaldonado (1991) na presença de caminhos não-executáveis.
2.3.3 Teste Estrutural de Programas Orientados a Objetos
Basicamente, as técnicas apresentadas neste texto foram primeiramente propostas para o para-
digma procedimental mas, apesar disso, também podem ser aplicadas ao teste de programas OO.
A técnica estrutural tem sido utilizada principalmente no teste de unidade, já que os requisitos de
teste exigidos geralmente limitam-se ao escopo da unidade. Entretanto, podem ser identificados
esforços de pesquisa para a utilização dos critérios estruturais no teste de integração (Harrold e
Soffa, 1989; Harrold e Rothermel, 1994).
No teste de unidadede programas OO, o método pode ser considerado como a menor uni-
dade (também chamado de módulo) e a classe como sendo o pseudo-controlador do método (dos
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 25
Santos Domingues, 2001). Como comentado anteriormente, o teste de unidade de programas OO
também pode ser chamado de intra-método (Harrold e Rothermel, 1994).
Em programas OO, um método pode interagir com outros métodos, chamando-os direta ou
indiretamente, dentro de uma mesma classe. O teste que envolve esse tipo de interação pode ser
considerado como um tipo deteste de integração, e é chamado inter-método (Harrold e Rothermel,
1994).
Outro tipo de interação que acontece entre métodos em classes são as chamadas a métodos
públicos feitas em seqüências arbitrárias, por usuários de uma classe. Para aumentar a confiança
de que as seqüências de chamadas interagem apropriadamente, é utilizado o teste intra-classe, que
também pode ser considerado como um tipo deteste de integração. Ainda na fase de integração,
também é definido o teste inter-classe que utiliza o mesmo conceito do teste intra-classe, porém
considera também as interações entre métodos de classes distintas (Harrold e Rothermel, 1994).
Após os testes de unidade e integração serem feitos, o sistema todo pode ser integrado, e
testes de sistemapodem ser realizados. Para esse tipo de teste geralmente utilizam-se critérios
funcionais que não apresentam diferenças fundamentais em relação ao paradigma procedimental,
quando aplicados no paradigma OO (dos Santos Domingues, 2001).
Tabela 2.2:Relação entre as fases de teste e o teste de programas OO (adaptado do trabalho dedos Santos Domingues (2001))
Menor Unidade: Método
Fase Teste de Software Orientado a ObjetosUnidade Intra-método
Integração Inter-método, Intra-classe e Inter-classeSistema Toda a aplicação
Menor Unidade: Classe
Fase Teste de Software Orientado a ObjetosUnidade Intra-método, Inter-método e Intra-classe
Integração Inter-classeSistema Toda a aplicação
Alguns autores consideram a classe como menor unidade de um programa OO, o que implica
em uma abordagem de teste diversa, em que os testes de unidade e de integração devem ser vistos
de uma perspectiva diferente. Na Tabela2.2 são sintetizados os tipos de teste OO aplicados em
cada fase, de acordo com a abordagem adotada.
Teste de Fluxo de Dados em Classes
As abordagens existentes para o teste baseado em fluxo de dados de programas procedimentais,
podem ser aplicadas para o teste de programas OO. Assim como são testados os procedimentos
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 26
de forma isolada (teste intraprocedimental) e a interação entre eles (teste interprocedimental), a
mesma idéia pode ser aplicada aos métodos isolados de uma classe (teste intra-método), e aos
métodos de uma classe que interagem entre si (teste inter-método). Porém, no paradigma OO
devem ser consideradas também as interações de fluxo de dados que acontecem quando usuários
de uma classe invocam seqüências de métodos de maneira arbitrária (Harrold e Rothermel, 1994).
Por exemplo, em uma classeC, com atributoa e métodosm1, m2, m3, deve ser analisado se as
diferentes seqüências de chamadas (por exemplo:m2, m1, m3, m2) não provocam nenhum estado
inconsistente na classeC com relação ao atributoa.
A partir desse problema,Harrold e Rothermel (1994) propuseram o teste de fluxo de dados
de classe, que considera não somente os testes intra e inter-método, mas também o teste intra-
classe que leva em conta a interação entre métodos públicos de uma classe quando chamados em
diferentes seqüências. Esses autores consideram a classe como a menor unidade de um programa
OO e, sendo assim, consideram três níveis para o teste de unidade:
• Intra-Método, que testa os métodos individualmente (equivalente ao teste de unidade de
programas procedimentais);
• Inter-Método, que testa os métodos em conjunto com outros métodos da mesma classe (equi-
valente ao teste de integração de programas procedimentias);
• Intra-Classe, que testa a interação entre métodos públicos quando chamados em diferentes
seqüências.
Com isso, devem ser considerados três tipos de pares Def-Uso, correspondentes a cada nível
de teste. Considerando uma classeC em teste,d uma sentença definindo uma variável eu uma
sentença contendo um uso de uma variável, definem-se:
• Pares Def-Uso Intra-Método: SejaM um método deC. Sed e u estão emM e existe um
programaP que chamaM , tal que(d, u) é um par Def-Uso exercitado durante uma simples
invocação deM , então(d, u) é um par Def-Uso intra-método.
• Pares Def-Uso Inter-Método: SejaM0 um método público deC e seja{M1, M2, ...,Mn} o
conjunto de métodos chamados direta ou indiretamente quandoM0 é invocado. Suponha que
d está emMi e queu está emMj, sendo que tantoMi quantoMj estão em{M1, M2, ...,Mn}.Se existe um programaP que chamaM0 tal que, em P,(d, u) é um par Def-Uso exercitado
durante uma simples invocação deM0 por P , e Mi 6= Mj ou Mi e Mj são invocações
separadas do mesmo método, então(d, u) é um par Def-Uso inter-método.
• Pares Def-Uso Intra-Classe: SejaM0 um método público deC e seja{M1, M2, ...,Mn} o
conjunto de métodos chamados direta ou indiretamente quandoM0 é invocado. SejaN0
um método público de C e seja{N1, N2, ..., Nn} o conjunto de métodos chamados direta
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 27
ou indiretamente quandoN0 é invocado. Suponha qued está em algum dos métodos em
{M1, M2, ...,Mn} e u em algum dos métodos em{N1, N2, ..., Nn}. Se existe um programa
P que chamaM0 eN0, tal que em P(d, u) é um par Def-Uso e que a chamada aM0 é feita
apósd ter sido executado eM0 encerra sua execução antes queu seja executado, então(d, u)
é uma par Def-Uso intra-classe.
01 // symboltable.h: definition02 #include "symbol.h"0304 class SymbolTable {05 private :06 TableEntry *table;07 int numentries, tablemax;08 int *Lookup( char *);09 public :10 SymbolTable( int n) {11 tablemax = n;12 numentries = 0;13 table = new TableEntry[tablemax]; };14 SymbolTable() { delete table; };15 int AddtoTable( char *symbol, char *syminfo);16 int GetfromTable( char *symbol, char *syminfo);17 };1819 // symboltable.c: implementation20 #include "symboltable.h"2122 int SymbolTable::Lookup( char *key, int index) {23 int saveindex;24 int Hash( char *);25 saveindex = index = Hash(key);26 while (strcmp(GetSymbol(index),key) != 0) {27 index++;28 if (index == tablemax) /* wrap around */29 index = 0;30 if (GetSymbol(index)==0 || index==saveindex)31 return NOTFOUND;32 }33 return FOUND;34 }35
36 int SymbolTable::AddtoTable( char *symbol,char *syminfo) {
37 int index;38 if (numentries < tablemax) {39 if (Lookup(symbol,index) == FOUND)40 return NOTOK;41 AddSymbol(symbol,index);42 AddInfo(syminfo,index);43 numentries++;44 return OX;45 }46 return NOTOK;47 }4849 int SymbolTable::GetfromTable( char *symbol,
char **syminfo) {50 int index;51 if (Lookup(symbol,index) == NOTFOUND)52 return NOTOK;53 *syminfo = GetInfo(index);54 return OK;55 }5669 void SymbolTable::AddInfo(syminfo,index)70 ...73 strcpy(table[index].syminfo,syminfo);75 }7677 char *SymbolTable::GetInfo(index)78 ...82 return table[index].syminfo;83 }
Figura 2.15: Implementação parcial da classeSymbolTable .
Os autores argumentam que para os testes inter e intra-método pode ser utilizado o algoritmo
de Pande, Landi e Ryder (PLR) (Pande et al., 1994), concebido para computar os pares Def-Uso
intra e interprocedimentais para a linguagem C. Porém, para a computação dos pares Def-Uso
intra-classe, o algoritmo não pode ser utilizado diretamente e, para isso, é gerado ografo de fluxo
de controle de classe(CCFG -Class Control Flow Graph), queconectatodos os métodos de uma
classe (Harrold e Rothermel, 1994). Na Figura2.15é mostrada uma implementação parcial em
C++ de uma classe de tabela de símbolos (SymbolTable) que será utilizada para exemplificar essa
abordagem de teste (Harrold e Rothermel, 1994).
Para construir o CCFG, primeiramente é necessário gerar ografo de chamadas de classe, que
representa as chamadas entre métodos e também as chamadas que podem ser feitas de fora da
classe – os nós representam cada método e as arestas representam as chamadas. Na Figura2.16
é mostrado o grafo de chamadas para a classe de tabela de símbolos, as arestas pontilhadas re-
presentam as chamadas aos métodos públicos da classe. A partir daí, para poder ser utilizado o
algoritmo PLR, é necessário englobar o grafo de chamadas com umframepara permitir que sejam
feitas as chamadas aos métodos em quaisquer seqüências para a computação dos pares Def-Uso
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 28
SymbolTable AddtoTable GetfromTable ~SymbolTable
AddtoSymbol
Hash
GetSymbolAddInfo
Class SymbolTable
Lookup GetInfo
Figura 2.16: Grafo de chamadas de classe para a classe de Tabela de Símbolos (adaptado dotrabalho deHarrold e Rothermel (1994))
SymbolTable
frame entry
frame loop
frame exit
frame return
frame call
AddtoTable GetfromTable ~SymbolTable
AddtoSymbol
Hash
GetSymbolAddInfo
Lookup GetInfo
Figura 2.17: Grafo de chamadas de classe para a classe de Tabela de Símbolos englobado porframe(adaptado do trabalho deHarrold e Rothermel (1994))
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 29
AddInfo
AddSymbol
~SymbolTable
GetInfo
CR
T
F
T F
frame exit
frame return
frame callframe loop
frame entry
index = hash(key);
Hash
|| index = saveindex)
T
F
TT F
F
SymbolTable
index),key)!=0)
T F
C R
GetSymbol
36. enter AddtoTable
38. if (numentries <= tablemax)
46. return NOTOK
40. return NOTOK41. AddSymbol(symbol,index)
42. AddInfo(syminfo),index
43. numentries++
44. return OK
47. exit AddtoTable
49. enter GetfromTable
52. return NOTOK 53. *syminfo=GetInfo(index)
54. return OK
55. exit GetFromTable
22: enter Lookup
25. saveindex =
26. while (strcmp(GetSymbol(
27. index++
28. if (index==TableMax)
33. return FOUND
31. return NOTFOUND
30. if (GetSymbol(index)==0
34. exit Lookup
29. index=0
Figura 2.18: Grafo de fluxo de controle de classe para a classe de Tabela de Símbolos (adaptadodo trabalho deHarrold e Rothermel (1994))
intra-classe. Oframe funciona como uma espécie de unidade pseudo-controladora da classe11 e
possui cinco nós:frame entry, frame exit, que representam a entrada e saída doframe; frame loop,
que facilita o seqüenciamento dos métodos;frame calle frame return, que representam a chamada
e o retorno a qualquer método público que possa ser invocado. Oframeainda possui quatro ares-
tas: (frame entry, frame loop), (frame loop, frame call), (frame loop, frame exit) e (frame return,
frame loop). A partir daí, cada nó que representa um método no grafo de chamadas é substituído
pelo seu grafo de fluxo de controle.
11Harrold e Rothermel (1994) definem oframecomo uma abstração de um método principal (main) P, no qualchamadas aos métodos públicos são selecionadas arbitrariamente por um comando de escolha (switch) S, ondeS éenglobado por um laçoL.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 30
Na Figura2.17 é mostrado o grafo de chamadas da classe de tabela de símbolos englobado
pelo frame, e na Figura2.18o CCFG parcial da classe. Na última figura apenas são expandidos
os nós correspondentes aos métodosAddtoTable , GetFromTable e Lookup com os grafos
de fluxo de controle de cada um deles, adicionando também arcos referentes às chamadas de um
método a outros da mesma classe. OsCs eRs contidos na figura correspondem às chamadas e
retornos de métodos, respectivamente.
Os três tipos de teste são úteis para o teste de classes. Por exemplo, se utilizarmos o critério
baseado em fluxo de dados Todos-Usos, então o par Def-Uso Intra-Método nas linhas 27–28 testa
se o métodoLookup volta ao começo da tabela quando chega ao final do vetor. O par Def-Uso
Inter-Método nas linhas 29–41 testa se o usuário pode inserir um símbolo na posição 0 da tabela,
enquanto que o par Def-Uso Intra-Classe nas linhas 43–38 testa se o métodoAddtoTable age
corretamente quando a tabela está cheia. Finalmente o par Def-Uso Intra-Classe nas linhas 73–82
testa se a informação adicionada à tabela anteriormente pode ser encontrada (Harrold e Rothermel,
1994). Para exercitar esse último par Def-Uso, por exemplo, poderia-se utilizar um caso de teste
que executasse o seguinte caminho do grafo da Figura2.18: (frame entry, frame loop, frame call,
36, 38, C, 22, 25, 26, 27, 28, 30, 31, 34, R, 41, 42, 43, 44, 47,frame return, frame loop, frame
call, 49, C, 22, 25, 26, 33, 34, R, 52, 55,frame return, frame loop, frame exit). A seqüência de
chamadas poderia ser: <AddtoTable ("B" , "bbbbb" ), GetFromTable ("B" , &info )>, em
queinfo é um ponteiro de caracter.
Uma outra vantagem apontada porHarrold e Rothermel (1994) em seu artigo é que os pa-
res Def-Uso Intra-Classe também auxiliam na seleção de seqüências de métodos que devem ser
testadas. Por exemplo, para exercitar o par Def-Uso Intra-Classe das linhas 73–82, é necessário
chamar a seqüência de métodos <AddtoTable , GetFromTable >, como comentado anterior-
mente. Entretanto, como não existe par Def-Uso Intra-Classe cuja definição se encontra no método
GetFromTable e uso no métodoAddtoTable , tem-se a evidência de que a execução do pri-
meiro método não afeta o segundo e, sendo assim, a seqüência não precisa ser exercitada.
Harrold e Rothermel (1994) consideram ainda o teste de fluxo de dados na integração das
classes, que corresponde a um quarto nível de teste, o inter-classe. Esse tipo de teste envolve os
pares Def-Uso em que a definiçãod de uma variável se encontra em uma classe e o usou em outra.
Para isso deve ser feita a interação entre os diversos CCFG’s de cada classe (Harrold e Rothermel,
1994).
2.3.4 Ferramentas de Teste
A atividade de teste, se realizada manualmente, geralmente é propensa a erros e limitada a
aplicações de pequeno porte (dos Santos Domingues, 2001). Nesse contexto, ferramentas de teste
podem auxiliar na automatização dessa tarefa, permitindo a aplicação prática de critérios de teste,
o teste de programas maiores, o apoio a estudos empíricos e a transferência das tecnologias de teste
para a indústria. Além disso as ferramentas de teste possibilitam a realização detestes de regres-
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 31
são, quando manutenções são feitas no programa e os casos de teste armazenados são executados
novamente para a validação da aplicação modificada (dos Santos Domingues, 2001).
Para o teste de programas OO em C++ e Java, existem várias ferramentas e, entre elas, podem
ser citadas:C++ Test, JProbe Suite, JTest, Panorama C/C++, Panorama for Javae xSuds Tool-
suite. A maioria delas apóia o teste estrutural e algumas delas apóiam o teste funcional, além de
apresentarem algumas outras funcionalidades de análise do programa em teste (dos Santos Domin-
gues, 2001).
*
TestSuite
<<C−Component>><<interface>>
Test
+run() <<C−operation>>
+run() <<C−operation>>
<<C−Composite>>TestCase
#tearDown()#setup()+addTest() <<C−add>>+run() <<C−operation>>
...
... ...
<<fixed>>©
<<C−Composite>>
Figura 2.19: Principais classes doframeworkJUnit (adaptado do livro deFontoura (2002))
O JUnit é um pequenoframeworkde teste de regressão para programas escritos em Java que
fornece suporte à criação, execução e avaliação de casos de teste (Beck e Gamma, 2002). Apesar
de testar se, para uma dada função e elemento do domínio, a saída é correta, não avalia a cobertura
do programa segundo critérios de teste. Na Figura2.19 são mostradas as classes principais do
JUnit – TestCase e TestSuite – que são utilizadas para desenvolver casos de teste e conjuntos de
casos de teste, respectivamente (utilizando a notação UML-F (Fontoura, 2002)). Para gerar um
caso de teste, cria-se uma classe de teste derivada de TestCase e projetam-se os métodos para fazer
os testes. Na Figura2.20é mostrado um exemplo de caso de teste utilizando o JUnit, para o teste
do método de verificação de identificadores. O métodoassertEquals faz parte do JUnit e
serve para realizar asserções.
A Ferramenta JaBUTi – Teste de Fluxo de Dados OO
O Grupo de Pesquisa em Engenharia de Software do ICMC, em colaboração com outros gru-
pos de pesquisa, vem propondo algumas ferramentas para apoiar a atividade de teste. Uma delas é
a JaBUTi (Java Bytecode Understanding and Testing), que tem o intuito de ser um ambiente com-
pleto para o entendimento e teste de programas e componentes Java. A ferramenta JaBUTi fornece
diferentes critérios de teste estruturais para a análise de cobertura, um conjunto de métricas estáti-
cas para avaliar a complexidade das classes que compõem o programa/componente, e implementa
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 32
import junit.framework.*;public class FunctionalTC extends TestCase {
boolean b;...public void testBeginsWithLetter() {
b = Identifier.verify("abcde");assertEquals( true , b);
}public void testDoesntBeginWithLetter() {
b = Identifier.verify("1abc");assertEquals( false , b);
}public void testContainsOnlyLettersDigits() {
b = Identifier.verify("a12n3");assertEquals( true , b);
}public void testDoesntContainOnlyLettersDigits() {
b = Identifier.verify("a1n3_");assertEquals( false , b);
}...
}
Figura 2.20: Exemplo de caso de teste implementado utilizando o JUnit
ainda algumas heurísticas de particionamento de programas que visam a auxiliar a localização de
erros12.
Para permitir a análise de cobertura de um programa Java, a ferramenta JaBUTi implementa
quatro critérios de teste baseados no fluxo de controle –Todos-Nós-Primários, Todos-Nós-Secun-
dários, Todos-Arcos-Primários, Todos-Arcos-Secundários– e dois critérios de teste baseados no
fluxo de dados da aplicação –Todos-Usos-Primáriose Todos-Usos-Secundários. Os pares Todos-
Nós-Primários – Todos-Nós-Secundários e Todos-Arcos-Primários – Todos-Arcos-Secundários,
compõem os critérios tradicionais Todos-Nós e Todos-Arcos. A composição do par Todos-Usos-
Primários – Todos-Usos-Secundários, resulta no critério Todos-Usos. A decisão por dividir esses
critérios foi tomada em virtude da distinção entre partes de código relacionadas com a execução
’normal´ do programa daquelas relacionadas com o tratamento de exceções. Essa distinção permite
ao testador se concentrar em perspectivas diferentes do programas, uma de cada vez, realizando
a atividade de teste de modo incremental e respeitando as restrições de tempo e custo que lhe
forem impostas (Vincenzi et al., 2003). Mais recentemente foram implementados na ferramenta
os critériosTodos-Potenciais-Usos-Primáriose Todos-Potenciais-Usos-Secundários, a partir do
critério Todos-Potenciais-Usos deMaldonado (1991).
Para realizar a análise de cobertura, as principais atividades executadas pela ferramenta são:
instrumentar arquivos .class, coletar informação de cobertura durante a execução do programa
(execution trace information), e determinar a suficiência do teste de cada um dos métodos de
acordo com os critérios de teste disponíveis.
Para facilitar a geração de casos de teste de modo a aumentar a cobertura em relação aos cri-
térios, utilizando o conceito de dominadores e super-bloco (Agrawal, 1994), a ferramenta atribui
diferentes pesos aos requisitos de teste indicando qual o requisito de teste que, se coberto, au-
12A maior parte desta seção foi retirada da tese deVincenzi (2004).
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 33
mentaria ao máximo a cobertura em relação ao critério considerado. Para avaliar o andamento
da atividade de teste, relatórios de teste com diferentes níveis de granularidade (por projeto, por
classe, por método, por caso de teste) podem ser gerados para auxiliar o testador a decidir quando
parar os testes ou quais partes ainda não foram suficientemente testadas.
As classes que implementam a ferramenta estão agrupadas em 11 pacotes. Seis deles (criteria,
graph, lookup, metrics, project and verifier) são responsáveis pela realização da análise estática,
implementação dos critérios de teste e avaliação da cobertura. Os demais estão relacionados com a
implementação da interface gráfica e com a coleta e armazenamento das informações de execução.
O primeiro passo para se conduzir uma sessão de teste na JaBUTi é a criação de um projeto de
teste (um arquivo .jbt), o qual identifica o conjunto de classes em teste (CUTs). O módulo Lookup
é utilizado para identificar a hierarquia completa das classes que compõem a aplicação. Esse mó-
dulo recebe como entrada um determinado arquivo .class (CF), o qual corresponde à classe base
da aplicação, e produz como saída toda a hierarquia de classes (CH) necessária para se executar
a classe base, incluindo tanto classes do sistema quanto classes definidas pelo usuário. A partir
das classes definidas pelo usuário qualquer subconjunto de classes pode ser selecionado, caracte-
rizando as classes a serem testadas CUTs. CH e CUT são armazenadas em uma base de dados do
projeto. Selecionadas as CUTs, a ferramenta utiliza o módulo Graph para a construção dos grafos
Def-Uso (DUG) de cada método. A partir do DUG, os requisitos de teste (TRs) referentes aos oito
critérios estruturais implementados pela JaBUTi são derivados e armazenados na base de dados do
projeto para serem utilizados posteriormente para a análise de cobertura. O módulo Loader é o car-
regador de classes (class loader) da JaBUTi. Esse módulo instrumenta as CUTs, carrega as classes
instrumentadas para serem executadas, e armazena as informações de execução em um arquivo de
trace com extensão .trc (TF). Cada execução do carregador de classes da JaBUTi corresponde a
um novo caso de teste sendo adicionado. Observa-se que o carregador de classes sempre realiza a
instrumentação das classes no ato, acarretando um atraso na execução das classes que estão sendo
testadas. Opcionalmente, o testador pode criar um arquivo .jar contendo as classes já instrumenta-
das evitando o atraso no processo de instrumentação. O módulo Coverage utiliza as informações
produzidas pelos outros módulos, incluindo o conjunto de requisitos de teste (TR) e o arquivo de
trace (TF), para identificar o conjunto de requisitos que foram cobertos considerando a execução
dos casos de teste armazenados em TF. Ele também é responsável pela geração dos diferentes re-
latórios de teste, os quais são utilizados pelo testador para avaliar a qualidade do conjunto de teste
e decidir quando parar os testes.
Na Figura2.21é mostrada uma tela da ferramenta JaBUTi, com o grafo Def-Uso de um dado
método.
2.3.5 Teste de Programas Orientados a Aspectos
As pesquisas na área da programação orientada a aspectos têm focado basicamente as fases
de análise, projeto e codificação dos modelos de processos da Engenharia de Software. Poucos
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 34
Figura 2.21: Tela da ferramenta JaBUTi mostrando um grafo Def-Uso de um dado método, apartir do critério Todos-Nós-Primários.
trabalhos até o momento abordam os tópicos de verificação, validação e teste de software, no
contexto da POA. Apesar disso, recentemente foram publicados alguns trabalhos sobre o tema, e
que serão discutidos nesta seção.
Teste Estrutural de Programas OA
O primeiro pesquisador a propor uma abordagem de teste estrutural para programas orientados
a aspectos foiZhao (2002, 2003). Em seus artigos, esse pesquisador apresenta uma abordagem
baseada em fluxo de dados para o teste de unidade de programas orientados a aspectos e propõe a
construção de uma ferramenta que implementa a abordagem. Zhao argumenta, a partir da imple-
mentação da linguagem AspectJ, ser impraticável o teste separado de classes e aspectos, pelo fato
de não haver referências explícitas aos aspectos nas classes afetadas, após a compilação e combina-
ção (o que não se aplica às versões mais recentes do AspectJ, no que diz respeito aobytecode). Por
conseguinte, propõe que para testar corretamente aspectos e classes, deve-se (1) testar os aspectos
juntamente com os métodos cujos comportamentos podem ser afetados pelos adendos (perspectiva
dos aspectos), e (2) testar as classes juntamente com os adendos que podem afetar o seu compor-
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 35
tamento (perspectiva das classes). A partir daí são definidos os conceitos de aspecto combinado
e classe combinada (c-aspecto e c-classe), para indicar um aspecto juntamente com as partes das
classes que ele afeta e, inversamente, a classe com os adendos que a afetam. Conseqüentemente,
para os métodos que são afetados por adendos, são definidos os c-métodos (que compreendem os
métodos juntamente com os adendos que os afetam), e para os adendos que afetam um conjunto de
métodos, os c-adendos (que compreendem os adendos juntamente com os métodos que afetam).
As c-classes e os c-aspectos são considerados as menores unidades do programa, na proposta de
Zhao. A partir daí, baseado no trabalho deHarrold e Rothermel (1994), são definidos três níveis de
teste de unidade: intra-módulo, inter-módulo e intra-aspecto ou intra-classe. Um módulo, para esse
pesquisador, pode ser um c-adendo, c-método, c-construtor, método simples, construtor simples ou
método introduzido13.
De acordo com a abordagem baseada em fluxo de dados de Zhao, o teste intra-módulo de um
programa orientado a aspectos deve selecionar casos de teste para executar caminhos no grafo
Def-Uso que exercitem pares Def-Uso internos de um módulo (equivalente ao teste inter-método
proposto porHarrold e Rothermel (1994)). Da perspectiva inter-módulo, casos de teste são selecio-
nados para exercitar pares Def-Uso que extrapolam o módulo. O teste intra-aspecto ou intra-classe
deve exercitar os pares Def-Uso obtidos a partir de chamadas em seqüências arbitrárias aos méto-
dos públicos de um dado c-aspecto ou c-classe.
Para possibilitar a utilização dessa abordagem, é proposto um modelo estrutural baseado nas
abordagens tradicionais. Para cada um dos módulos (c-método, c-adendo, etc) é construído um
grafo de programa com as informações de definições e usos de variáveis (o que foi chamado de
grafo Def-Uso na Seção2.3.2deste capítulo). Para cada um dos c-aspectos e c-classes também é
construído umgrafo interprocedimental, que é formado por um grafo de chamadas que representa
as possíveis chamadas de cada módulo mais o grafo de programa de cada um dos módulos. Além
disso é proposta a utilização dografo de fluxo de controle com frame(FCFG –Framed Control
Flow Graph) baseado no CCFG proposto porHarrold e Rothermel (1994), porém considerando
as unidades como sendo os c-aspectos ou c-classes. A partir desse grafo pode ser feito o teste
intra-classe ou intra-aspecto proposto por Zhao.
Na abordagem de Zhao um problema que pode ser encontrado é que não se pode testar aspec-
tos e classes separadamente. No primeiro nível de teste – o intra-módulo – os adendos já estão
combinados com os componentes que afetam, sem que tenham sido testados isoladamente. Essa
falha é decorrente do fato da abordagem ser baseada em uma estratégia de implementação anterior
do AspectJ, na qual os aspectos não eram compilados em unidades separadas. Na mais recente
implementação do compilador/combinador do AspectJ, os aspectos e classes são compilados em
unidades separadas, de maneira que as classes ficam mais limpas e tem-se a possibilidade de iden-
13Note-se que Zhao utiliza o termomódulodiferentemente deste texto, pois na sua concepção unidade e módulonão se referem à mesma idéia. O que esse pesquisador chama de módulo é apenas uma parte da unidade em teste, enão a unidade em si.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 36
tificar os pontos onde os aspectos afetam, sem que eles fiquem inteiramente misturados com as
classes.
Na Figura2.22 é mostrado o exemplo que o pesquisador utilizou para ilustrar a abordagem
proposta. A classePoint representa pontos e possui atributosx ey referentes às coordenadas do
ponto. A classeShadow, por sua vez, representa sombras de pontos, e também possui atributosx
e y para as suas coordenadas. O aspectoPointShadowProtocol implementa a relação entre
pontos e sombras e introduz um atributoshadow na classePoint para representar a sombra
de um dado ponto. Para relacionar os objetos, o aspecto associa uma sombra a cada ponto que
é criado, por meio do adendo posterior que executa quando o construtor dePoint é chamado.
O adendo faz uso do métodoassociate para associar a sombra criada ao ponto, atribuindo-
a àshadow (o atributo introduzido). Para manter a relação consistente, sempre que um objeto
ponto se move como conseqüência da execução de um dos métodos que alteram suas coordenadas
(setX e setY ), a sua sombra é atualizada pelos adendos posteriores que executam sempre que
esses métodos são chamados. O métodogetShadow é utilizado pelos adendos posteriores para
obter a sombra do objeto ponto interceptado.
ce0 public class Point {s1 protected int x, y;
me2 public Point( int _x, int _y) {s3 x = _x;s4 y = _y;
}me5 public int getX() {
s6 return x;}
me7 public int getY() {s8 return y;
}me9 public void setX( int _x) {s10 x = _x;
}me13 public void printPosition() {
s14 System.out.println("Point at("+x+", "+y+")");}
me15 public static void main(String[] args) {s16 Point p = new Point(1,1);s17 p.setX(2);s18 p.setY(2);
}}
ce19 class Shadow {s20 public static final int offset = 10;s21 public int x, y;
me22 Shadow( int _x, int _y) {s23 x = _x;s24 y = _y;
}me25 public void printPosition() {
s26 System.out.println("Shadow at ("+x+","+y+")");}
}
ase27 aspect PointShadowProtocol {s28 private int shadowCount = 0;
me29 public static int getShadowCount() {s30 return PointShadowProtocol. aspectOf ().shadowCount;
}s31 private Shadow Point.shadow;
me32 public static void associate(Point p, Shadow s) {s33 p.shadow = s;
me34 public static Shadow getShadow(Point p) {s35 return p.shadow;
}pe36 pointcut setting( int x, int y, Point p):
args (x, y) && call (Point. new( int , int );pe37 pointcut settingX(Point p):
target (p) && call (Point.setX( int ));pe38 pointcut settingY(Point p):
target (p) && call (Point.setY( int ));
ae39 after ( int x, int y, Point p) returning :setting(x, y, p) {
s40 Shadow s = new Shadow(x + Shadow.offset,y + Shadow.offset);
s41 associate(p, s);s42 shadowCount++;
}ae43 after (Point p): settingX(p) {
s44 Shadow s = getShadow(p);s45 s.x = p.getX() + Shadow.offset;s46 p.printPosition();s47 s.printPosition();
}ae48 after (Point p): settingY(p) {
s49 Shadow s = getShadow(p);s50 s.y = p.getY() + Shadow.offset;s51 p.printPosition();s52 s.printPosition();
}}
Figura 2.22: Exemplo de motivação para o teste de programas orientados a aspectos.
A partir do código são gerados então os grafos necessários. Na Figura2.23 são mostrados
os grafos de chamadas para a c-classePoint e para o c-aspectoPointShadowProtocol .
Na Figura2.24é mostrado o FCFG para as duas unidades. Esses dois últimos são utilizados na
computação dos pares Def-Uso no teste intra-classe ou intra-aspecto.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 37
me32 ae43ae39me34me29 ae48
me2 me9 me11
ae43ae39
ae48
me5 me9 me11me2
me7 me13 me15
Figura 2.23: Grafo de chamadas para o c-aspectoPointShadowProtocol e para a c-classePonto .
s45
me32
framecall
ae43
s33
s44
s46
ae39
s41
me34
s35
me29
s30
s47
s50
ae48
s49
s51
s52
s40
s42
framereturn
frameexit
frameloop
frameentry
me2me9 me11
s3
s4
s10s12
s45
me5
framecall
ae43
s6
s44
s46
ae39
s41
me7
s8
s47
s50
ae48
s49
s51
s52
s40
s42
framereturn
frameexit
frameloop
frameentry
me2me9 me11
s3
s4
s10 s12
me13
s14
me15
s16
s17
s18
Figura 2.24: FCFG para o c-aspectoPointShadowProtocol e para a c-classePoint .
Teste Baseado em Estados de Programas OA
Xu et al. (2004b) e Xu et al. (2004a) propuseram uma abordagem mista de teste baseada em
estados e estrutural para programas orientados a aspectos (porém com ênfase na técnica baseada
em estados). A idéia principal é estender o modelo baseado em estados FREE (Flattened Regular
Expression) proposto porBinder (1999), para um modelo de estados aspectual (Aspectual State
Model – ASM). O modelo FREE representa os estados e comportamentos dinâmicos de objetos
e oferece diretrizes para implementação. Na Figura2.25é mostrado um modelo FREE para uma
classeAccount de contas bancárias e na Figura2.26é mostrado o modelo ASM para representar
a classe juntamente com os aspectos que interagem com ela. As transições e estados tracejados
correspondem respectivamente à execução de adendos e aos estados novos adicionados pelos as-
pectos. A execução dos adendos anteriores e posteriores iniciam em um losango preenchido perto
do início ou do final da transição, respectivamente. A execução de adendos de contorno inicia com
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 38
um círculo preenchido no ponto de junção referente. A partir desse modelo é derivada uma árvore
de transições da qual são criados casos de teste que definem seqüências de chamadas a métodos.
Na Figura2.27é mostrada a árvore de transições baseada no modelo ASM da Figura2.26.
balance
close
settle
currentY-lastY>5
balance
freeze
unfreeze
debit
open
credit
balance
Frozen
closed
Open
inactive
Figura 2.25: Modelo FREE para classe de contas bancárias (Xu et al., 2004a).
balance
close
[currentY-lastY>5&&
b<MAX_INA_BALANCE]
proceed
[currentY-lastY>5]
[b<0]
balance
freeze
unfreeze
debit
open
credit
balance
Frozen
Closed
Open
[b<0]
Overdrawn
settle
Inactive
credit[b<0]
credit
[b>=0]
Figura 2.26: Modelo ASM para classe de contas bancárias (Xu et al., 2004a).
A segunda parte da abordagem mistura a técnica estrutural à técnica baseada em estados. A
idéia é substituir as transições e interações de aspectos por grafos de fluxo de controle referentes
aos métodos e adendos. Por exemplo, as transições realizadas a partir dos métodoscredit e debit,
do estadoOpen paraOverdrawn, são substituídas pelos grafos de fluxo de controle dos métodos
credit e debite do adendo posterior que interage com esses métodos. O modelo é chamado grafo
de fluxo de aspectos (Aspect Flow Graph– AFG). Uma parte do AFG construído a partir do ASM
representado na Figura2.26é mostrado na Figura2.28. Com esse modelo podem ser realizadas
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 39
balance
freeze
debit
credit
[(currentY-lastY)>5]&&
B<MAX_INA_BALANCE balance
settle
balance
close
[(currentY-lastY)>5]
Open
Closed
Inactive
Inactive
Closed
Settle Inactive
Inactive
Closed
[b<0]
[b>=0]
[amt>MAX_AMT] freeze
[b<=0]
[b>0]
Open
Frozen
unfreeze
balance
Frozen
Overdrawn
Frozen unfreeze
Frozen
Open
credit
[b<=0]
[b>0]
Open
Overdrawn
Overdrawn
credit
[b<=0]
[b>0]
Open
Overdrawn Open
Open
Open
Figura 2.27: Árvore de transição baseada no modelo ASM para classe de contas bancárias (Xu etal., 2004a).
análises de cobertura a partir da execução dos casos de teste gerados com auxílio da árvore de
transição, para auxiliar na geração de novos casos de teste e para se ter idéia da suficiência da
atividade de teste.
Outros Esforços
Recentemente foram propostos também outros trabalhos relacionados com o teste de progra-
mas OA, porém explorando outras propriedades sem relações com uma técnica de teste específica.
Em alguns artigos,Alexander (2003) e Bieman ((Alexander e Bieman, 2002a; Alexander et
al., 2004)) discutem a problemática do teste de programas orientados a aspectos. É apontado, por
exemplo, que os testes feitos para os componentes podem não se aplicar aos artefatos combinados,
por causa de mudanças nas dependências de dados e controle que podem surgir a partir da combi-
nação. Pode ocorrer que um defeito em um programa OA não se localize nem nos componentes,
nem nos aspectos, mas sim em um efeito da combinação de uns com os outros.
Mais recentementeAlexander et al. (2004) propuseram um modelo de defeitos para programas
OA. O modelo explora as defeitos que podem ocorrer devido às peculiaridades de um programa
OA. O modelo de defeitos é baseado nas seguintes classes: 1) restrição incorreta em padrões
de conjuntos de junção; 2) incorreta precedência de aspectos; 3) defeito na preservação de pós-
condições impostas; 4) defeito na preservação de invariantes de estado; 5) foco incorreto no fluxo
de controle; e 6) mudanças incorretas em dependências de controle.
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 40
c2c1
c3
c0
Open
a1
a0
Overdrawn
d1
d0
d2
Inactive
r2r1
r3
r0
Frozen
b2b1
b3
b0
f2f1
f3
f0
c2c1
c3
c0
Overdrawn Open
a2
Figura 2.28: AFG parcial baseado no modelo ASM para classe de contas bancárias (Xu et al.,2004a).
A primeira classe de defeitos explora erros que podem acontecer na definição de conjuntos de
junção, pois o desenvolvedor pode criar conjuntos de junção que são muito restritivos, ou seja,
capturam menos pontos de junção do que deviam, ou pouco restritivos. Já a segunda explora erros
que o desenvolvedor pode cometer na definição da precedência entre aspectos que afetam pontos
de junção coincidentes. A terceira e quarta classe de defeitos exploram os erros cometidos nos
aspectos que alteram dados dos componentes que afetam, ferindo pós-condições e invariantes de
estado previamente definidas. A quinta classe de defeitos explora erros cometidos em conjuntos
de junção que utilizam o designador de pontos de junção do tipocflow . Por fim, a sexta classe
de defeitos explora mudanças errôneas nas dependências de fluxo de controle que podem ser feitas
a partir do mau uso de aspectos. No próprio trabalho deAlexander et al. (2004) é citado que a
abordagem proposta não é um método completo, mas apenas um passo inicial para auxiliar no
teste de programas OA.
Xie et al. (2004) propuseram uma abordagem para seleção de casos de teste para programas
escritos em AspectJ que detecta casos de teste redundantes para programas desse tipo. Outra
abordagem para seleção de casos de teste é deZhou et al. (2004), entretanto, esse trabalho procura
propor um método prático completo para testar programas OA a partir da seleção de casos de teste
que são relevantes para aspectos. Além disso é proposto um novo critério de cobertura, baseado
no algoritmo de seleção de casos de teste relevantes para aspectos.
O algoritmo proposto porXie et al. (2004) identifica os casos de teste relevantes da seguinte
forma: analisa-se cada método para saber se algum aspecto o afeta em algum ponto; depois para
CAPÍTULO 2. REVISÃO BIBLIOGRÁFICA 41
cada caso de teste, analisa-se se os métodos chamados são afetados por algum aspecto, sendo que
o caso de teste é considerado relevante para o teste de sum dado aspecto se ele chama um método
que é afetado pelo aspecto.
2.4 Considerações Finais
Neste capítulo foram abordados os principais conceitos relacionados com a programação ori-
entada a aspectos e teste de software. Um fato importante a ser notado é a pequena quantidade de
trabalhos publicados relacionados com o teste de programas OA. Apesar disso, nota-se um cres-
cente interesse por esse tema, já que a maioria das publicações referenciadas são do ano de 2004.
Especificamente sobre teste estrutural de programas OA, até o momento, podem ser destacados
apenas os trabalhos deZhao (2003) e deXu et al. (2004a). Nota-se também que nenhuma das
abordagens explora a fundo as características da POA no que diz respeito aos critérios de fluxo de
controle e de dados, além da falta de implementações ou mesmo subsídios para implementações
de critérios de teste para programas OA. Além do mais, ainda não foram propostos critérios de
teste especificamente orientados a aspectos.
Com isso, abre-se um nicho interessante para ser explorado: o teste estrutural de programas
OA, focando as peculiaridades de programas desse tipo e critérios que explorem esses pontos. No
próximo capítulo é proposta uma abordagem de teste estrutural para programas OA.
CAPÍTULO
3Teste Estrutural de Programas
Orientados a Aspectos
3.1 Considerações Iniciais
Considerando a pequena quantidade de trabalhos publicados sobre o tema e o crescente inte-
resse pelo assunto, este capítulo apresenta uma abordagem de teste estrutural para programas OA,
adaptando o modelo de fluxo de controle e de dados e os critérios estruturais. É apresentada uma
análise de inclusão dos critérios estruturais de unidade e também uma comparação da abordagem
com outras propostas.
Na Seção3.2as fases do teste estrutural de programas OA são definidas. Na Seção3.3é apre-
sentada a abordagem de teste de unidade e na Seção3.4 é apresentada a abordagem de teste de
integração, focando na fase de teste Método-adendo. Na Seção3.5é feita uma avaliação da abor-
dagem, comparando com os outros trabalhos relacionados e, por fim, na Seção3.6são apresentadas
algumas considerações finais do capítulo.
3.2 Fases do Teste Estrutural de Programas OA
Considerando o teste de programas OA, baseando-se no livro deSommerville (2000) e na
abordagem deHarrold e Rothermel (1994), essa atividade poderia ser particionada nas seguintes
fases (Lemos et al., 2004b,a):
42
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 43
1. Teste de Unidade: O teste de cada método e adendo isoladamente, também chamado de
teste intra-método ou intra-adendo.
2. Teste de Módulo: O teste de uma coleção de unidades dependentes – unidades que inte-
ragem por meio de chamadas ou interações com adendos. Essa fase pode ser dividida nos
seguintes tipos de teste (considerando classes e aspectos como entidades diferentes):
• Inter-método: Consiste em testar cada método público juntamente com outros métodos
da mesma classe chamados direta ou indiretamente (chamadas indiretas são aquelas
que ocorrem fora do escopo do próprio método, dentro de um método chamado em
qualquer profundidade) (Harrold e Rothermel, 1994).
• Adendo-método: Consiste em testar cada adendo juntamente com outros métodos cha-
mados por ele direta ou indiretamente.
• Método-adendo: Consiste em testar cada método público juntamente com os adendos
que o afetam direta ou indiretamente (considerando que um adendo pode afetar outro
adendo). Nesse tipo de teste não é considerada a integração dos métodos afetados com
os outros métodos chamados por eles, nem com método chamados pelos adendos.
• Adendo-adendo: Consiste em testar cada adendo juntamente com outros adendos que
o afetam direta ou indiretamente.
• Inter-método-adendo: Consiste em testar cada método público juntamente com os
adendos que o afetam direta e indiretamente, e com métodos chamados direta ou indi-
retamente. Esse tipo de teste inclui os quatro primeiros tipos de teste descritos acima.
• Intra-classe: Consiste em testar as interações entre os métodos públicos de uma classe
quando chamados em diferentes seqüências (Harrold e Rothermel, 1994), considerando
ou não a interação com os aspectos.
• Inter-classe: Consiste em testar as interações entre classes diferentes, considerando ou
não a interação dos aspectos.
3. Teste de Sistema: A integração de todos os módulos forma um subsistem ou um sistema
completo. Para essa fase geralmente é utilizado o teste funcional.
A próxima seção trata o teste de unidade, que consiste em exercitar cada unidade de um pro-
grama como uma entidade independente, procurando identificar erros em sua lógica e implemen-
tação. O teste de integração é tratado na Seção3.4.
3.3 Teste Estrutural de Unidade
Para o teste de unidade é necessária a definição da menor unidade de um programa que será
testada nessa fase, de acordo com a técnica de programação utilizada. No caso de programas OO,
por exemplo, pode-se considerar cada método como a menor unidade a ser testada (Seção2.3.3).
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 44
Para os programas orientados a aspectos, seguindo a mesma linha do trabalho deVincenzi
(2004), que considera o método como a menor unidade de um programa OO, neste trabalho
considera-se cada método, adendo e método inter-tipo declarado como as menores unidades do
programa. A partir daí, o teste de unidade para uma dada classe deve ser feito para cada método
e método inter-tipo declarado (caso exista); e para um dado aspecto deve-se testar cada adendo e
cada método. O teste de unidade de um método é chamado intra-método e, similarmente, o teste
de um adendo isoladamente pode ser chamado de testeintra-adendo.
No teste estrutural, para que os critérios possam ser aplicados, é necessário que sejam definidos
os grafos de fluxo de controle e de dados adequados (Seção2.3). Na próxima seção são defini-
dos genericamente os grafos de fluxo para programas orientados a aspectos e sua versão para a
linguagem AspectJ.
3.3.1 Grafo de Fluxo para Programas Orientados a Aspectos
No fluxo de controle de programas orientados a aspectos, quando os aspectos definem compor-
tamento em algum ponto do sistema por meio dos adendos, pode ser detectado um novo tipo de
interação. Quando o ponto de junção é alcançado, o fluxo de controle é passado para o adendo do
aspecto que afeta o ponto, retornando ao final da execução. Esse tipo de interação pode ser compa-
rada com uma chamada a método, na qual o fluxo de controle é passado para o método chamado,
retornando ao final de sua execução. Porém, é necessário que se faça uma correta distinção entre os
pontos de chamada e os pontos onde os aspectos definem comportamento por meio dos adendos,
pois no primeiro caso o desenvolvedor do método inseriu explicitamente a chamada ao método
para atender os propósitos do método em programação, enquanto que no segundo caso são os
aspectos os responsáveis por definir o comportamento desejado em pontos de junção específicos.
Dessa forma, no fluxo de controle de um programa orientado a aspectos tem-se um novo tipo
de interação que torna necessária a adaptação do grafo para uma representação adequada do fluxo
de controle das unidades que são afetadas por adendos de aspectos. Para esse fim é definido um
novo tipo de nó – o nó transversal (crosscutting node) – para representar os pontos das unidades
onde os aspectos definem comportamentos transversais, tomando o fluxo de controle para si.
O Grafo de Fluxo de Controle Orientado a Aspectos (AOCFG) é um grafo definido com o
objetivo de apoiar o teste de unidade de um programa orientado a aspectos. Como é possível que
em uma linguagem orientada a aspectos os adendos sejam afetados por outros adendos – como, por
exemplo, no AspectJ – oAOCFG pode ser usado para representar tanto métodos quanto adendos.
Um grafoAOCFG de uma dada unidadeu é definido como um grafo dirigidoAOCFG(u) =
(N, E, s, C, T ), tal que:
• N representa o conjunto de nós de um grafoAOCFG : N = {n|n corresponde a uma
seqüência linear de computações deu};
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 45
• E ⊆ N ×N é o conjunto completo de arestas doAOCFG. Cada arestae ∈ E representa a
transferência de controle que pode ocorrer entre dois nós;
• s ∈ N é o nó de entrada deu. s é o único nó do grafo que não possui nenhuma aresta de
entrada;
• C ⊆ N é o conjunto (possivelmente vazio) de nós transversais que representam um nó no
qual ocorre uma interação com um adendo de um dado aspecto;
• T ⊆ N é o conjunto (possivelmente vazio) de nós de saída. Ou seja, cadat ∈ T não possui
nenhuma aresta de saída.
O Grafo Def-Uso Orientado a Aspectos (AODU ) é oAOCFG com informações de definições
e usos de variáveis, para a aplicação de critérios baseados em fluxo de dados. Como o grafoAODUé umAOCFG estendido, é necessária apenas a construção doAODU para se derivar requisitos
de teste tanto para o fluxo de controle quanto para o fluxo de dados do programa.
Para uma dada linguagem orientada a aspectos, o grafoAODU deve ser instanciado de maneira
a representar as unidades de programas escritos nessa linguagem e para que possa ser definido um
modelo de dados a partir do qual identificam-se as definições/usos de variáveis e quais tipos de
variáveis são tratadas. No caso do AspectJ – linguagem na qual este trabalho é baseado – decidiu-
se por estender o trabalho deVincenzi (2004), que definiu modelos e critérios de teste para o
bytecode Java. Essa decisão é baseada no fato de que o compilador/combinador do AspectJ produz
bytecode Java comum como resultado da compilação/combinação (Seção2.2).
Antes de construir o grafoAODU para as unidades de um programa escrito em AspectJ, é
construído o grafo de instruções de fluxo de dados (IG) (Vincenzi, 2004). Informalmente oIG é
um grafo no qual cada nó contém uma única instrução de bytecode e as arestas conectam instruções
que podem ser executadas seqüencialmente. A partir daí o grafoIG é acrescido de informações
de acesso a dados (definição ou uso) para cada instrução de bytecode (Lemos et al., 2004c,d).
A idéia do grafoIG é abstrair o fluxo de controle e de dados envolvidos em cada instrução
de bytecode individual. Porém, algumas características do bytecode precisam ser tratadas com
cuidado durante a análise do fluxo de controle. Em particular, é necessário tratar cuidadosamente
as instruções de chamadas a sub-rotinas implementadas pela Máquina Virtual Java (JVM) (jsr e
jsr_w ) e o mecanismo de tratamento de exceção de Java. No último caso o problema é que os
tratadores de exceção de Java não são executados a partir de um fluxo de controle normal de um
dado programa, mas apenas quando exceções são lançadas. A solução adotada porVincenzi (2004)
foi utilizar dois tipos de arestas: arestas regulares, que representam o fluxo de controle normal e
arestas de exceção, que representam o fluxo de controle quando exceções ocorrem (uma aborda-
gem similar foi proposta porSinha e Harrold (1998), entretanto somente considerando exceções
explicitamente geradas). A extensão feita para o grafoIG neste trabalho diz respeito à adição do
conjunto de nós transversais como uma componente do grafo.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 46
Formalmente o grafoIG de uma dada unidadeu é definido como um grafo dirigidoIG(u) =
(NI, EI, si, T I, CI), tal que:
• NI representa o conjunto não-vazio de nós de um grafoIG: NI = {ni|ni corresponde a uma
instrução de bytecodei, para todo bytecodei alcançável deu1}. Como cada nó corresponde
a uma única instrução bytecode, usa-se daqui por dianteni para referenciar indistintamente
o nó do grafoIG e a instrução a ele correspondente;
• EI = EIr ∪ EIe é o conjunto completo de arestas doIG tal que:
– EIr eEIe correspondem a dois subconjuntos disjuntos de arestas regulares e de exce-
ção, respectivamente:
∗ EIr é o conjunto de arestas regulares definido comoEIr = {(ni, nj)| a instrução
emnj pode ser executada imediatamente após a instrução emni e(ni, nj) /∈ EIe}.
∗ EIe é o conjunto de arestas de exceção definido comoEIe = {(ni, nj)| a instrução
deni está no escopo de um tratador de exceção2 que inicia na instrução denj};
• si ∈ NI é o nó de entrada que corresponde ao nó que contém a primeira instrução deu.
Considerex um nó de umIG, IN(x) corresponde ao número de arestas de entrada de x.
Tem-se queIN(si) = 0.
• TI ⊆ NI é o conjunto de nós de saída deu. Considerex um nó de umIG, OUT (x)
corresponde ao número de arestas de saída dex. Tem-se queTI = {ni ∈ NI|OUT (ni) =
0}.
• CI ⊆ NI é o conjunto (possivelmente vazio) de nós transversais, isto é, nós que representam
a execução de adendos de determinados aspectos. De fato, esses nós correspondem aos
nós cujas instruções de bytecode são invocações aos métodos correspondentes aos adendos
(Seção2.2.1).
Para adicionar informações relacionadas com o fluxo de dados das unidades noIG, a solução
adotada porVincenzi (2004) foi classificar as instruções de bytecode, relacionando cada uma com
o fluxo de dados envolvido. Essa classificação é apresentada na Tabela3.1.
Além da classificação das instruções de bytecode, é necessário fazer algumas suposições sobre
a identificação da definição e uso de variáveis (adaptado do trabalho deVincenzi (2004)):
1. Variáveis agregadas são consideradas como sendo uma única posição de memória e a de-
finição/uso de qualquer elemento da variável agregadaa[] é considerada como sendo uma
definição/uso dea[] . Assim sendo, em um comando do tipo “a[i] = a[j] + 1 ” existe uma
definição e um uso da variável agregadaa[] .
1Se o compilador gerar alguma instrução não-alcançável, essa instrução não será representada noIG.2Um tratador de exceçãoj é responsável por tratar exceções geradas pelas instruções de bytecode localizadas
dentre um determinado intervalo deoffsets[ojm ..ojn ]. Diz-se que uma instrução de bytecodei localizada no offsetoi
está no escopo do tratador de exceçãoj se e somente seoi ∈ [ojm ..ojn ].
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 47
Tabela 3.1:Diferentes classes de instruções de bytecode (Vincenzi, 2004).
Classe Instrução de Bytecode Implicações no Fluxo de Dados
0 athrow, goto, goto_w, if_acmpeq, if_acmpne,if_icmpeq, if_icmpge, if_icmpgt, if_icmple, if_icmplt ,if_icmpne, ifeq, ifge, ifgt , ifle, iflt , ifne, ifnonnull , ifnull ,lookupswitch, tableswitch, areturn , dreturn , freturn ,ireturn , lreturn , return , ret, monitorenter, monitore-xit , pop, pop2, breakpoint, impdep1, impdep2, nop,checkcast, wide, swap
Essas instruções não têm implicações no fluxo de dados daunidade.
1 invokeinterface, invokespecial, invokestatic, invokevir-tual, jsr , jsr_w, dadd, ddiv, dmul, dneg, drem, dsub,fadd, fdiv , fmul , fneg, frem, fsub, iadd, iand, idiv , imul ,ineg, ior , irem, ishl, ishr, isub, iushr, ixor , ladd, land,ldiv , lmul , lneg, lor , lrem, lshl, lshr, lsub, lushr, lxor ,arraylength, instanceof, aconst_null, bipush, dconst,fconst, iconst, lconst, sipush, ldc, ldc_w, ldc2_w, d2f,d2i, d2l, f2d, f2i, f2l, i2b, i2c, i2d, i2f, i2l, i2s, l2d, l2f, l2i,new, multianewarray , anewarray, newarray, dcmpg,dcmpl, fcmpg, fcmpl, lcmp
Essas instruções não têm implicações no fluxo de dados daunidade. Além disso, elas deixam no topo da pilha de exe-cução um elemento desconhecido. Acessos a tal elementonão irão caracterizar definição ou uso de nenhuma variável.Por exemplo, a instruçãonew empilha a referência a umnovo objeto no topo da pilha de execução e qualquer usofuturo de tal referência, tal como o acesso de um de seusatributos, não é considerado um uso ou definição desse ob-jeto.
2 aaload, baload, caload, daload, faload, iaload, laload,saload
Carregam no topo da pilha de execução um elemento de umvetor caracterizando um uso do vetor.
3 aastore, bastore, castore, dastore, fastore, iastore, las-tore, sastore
Armazenam o valor do topo da pilha de execução em umelemento do vetor caracterizando uma definição do vetor.
4 putfield Armazena o valor do topo da pilha de execução em um atri-buto de instância caracterizando a definição de tal atributo.
5 putstatic Armazena o valor do topo da pilha de execução em umatributo de classe caracterizando a definição de tal atributo.
6 dup, dup2, dup_x1, dup_x2, dup2_x1, dup2_x2 Essas instruções duplicam o valor do topo da pilha de exe-cução e não têm implicações no fluxo de dados da unidade.
7 aload, dload, fload, iload, lload Carregam no topo da pilha de execução o valor armaze-nado em uma variável local caracterizando um uso de talvariável.
8 astore, dstore, fstore, istore, lstore Armazenam o valor do topo da pilha de execução em umavariável local caracterizando uma definição de tal variável.
9 getfield Carrega no topo da pilha de execução o valor armazenadoem um atributo de instância caracterizando um uso de talatributo.
10 getstatic Carrega no topo da pilha de execução o valor armazenadoem um atributo de classe caracterizando um uso de tal atri-buto.
11 iinc Incrementa o valor armazenado em uma dada variável localcaracterizando um uso e uma definição de tal variável.
2. Se uma variável agregadaa[][] é declarada, acessos aos seus elementos caracterizam uma
definição ou uso dea[][] , dependendo do tipo de acesso. Assim sendo, comandos de atri-
buição do tipo “a[0][0] = 10 ” ou “a[0] = new int[10]” caracterizam uma definição dea[][]
e a[] , respectivamente, ao passo que no comando “a[0][0] = a[2][3] ” existe um uso e uma
definição da variávela[][] .
3. Toda vez que um atributo de instância é usado (definido) existe um uso da variável de refe-
rência que permite o acesso ao atributo e um uso (definição) do atributo em si. Por exemplo,
considereref_1e ref_2duas variáveis de referência para objetos da classeC a qual contém
dois atributos de instânciax e y do tipo inteiro (int), no comando “ref_1.x = ref_2.y” existe
usos das variáveis de referênciaref_1 e ref_2, um uso do atributo de instânciaref_2.y, e
uma definição do atributo de instânciaref_1.x. Uma vez que os atributos de instância são
válidos no escopo de toda a classe (aspecto), cada atributo de instância usado em uma dada
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 48
unidade, e que não tenha sido definido na unidade, é considerado como tendo sido definido
no primeiro nó do grafoIG da unidade em questão.
4. Atributos estáticos podem ser considerados como variáveis globais e o acesso a eles é feito
sem a necessidade de uma variável de instância. Considerando uma classeC com os atributos
estáticosw e z, ambos do tipoint, no comando “C.z = C.w + 10” existe um uso do atributo
C.we uma definição do atributoC.z. Mesmo que o acesso ao atributo estático seja feito por
meio de uma variável de instânciaref_1do tipo da classeC, tal como “ref_1.w = 10”, em
bytecode, tal variável de referência é automaticamente convertida no nome da classe, não
sendo caracterizado um uso da variável de referência nesse caso. Uma vez que os atributos
estáticos são válidos no escopo de toda a classe (aspecto), cada atributo estático usado em
uma dada unidade, e que não tenha sido definido na unidade, é considerado como tendo sido
definido no primeiro nó do grafoIG da unidade em questão.
5. Na invocação de um método, tal comoref_1.foo(e_1, e_2,. . ., e_n), considera-se que ocorre
um uso da variável de instânciaref_1. As regras para a identificação de definição e uso nas
expressõese_1, e_2,. . ., e_nsão as mesmas descritas nos itens de 1 a 4.
Ainda no caso das unidades de instância, uma definição para a variável que representa a ins-
tância correntethis é associada ao primeiro nó doIG . O mesmo ocorre para as variáveis
locais que correspondem aos parâmetros formais da unidade em execução. Para métodos de
classe, somente as variáveis locais correspondentes aos parâmetros formais são considera-
das definidas no nó de entrada deIG, já que nenhuma variável de instância é requerida para
invocar um método de classe.
O IG oferece uma maneira prática de percorrer o conjunto de instruções de uma dada unidade,
identificando usos e definições de variáveis. Entretanto, o número de nós e arestas envolvidos
nesse tipo de grafo pode ser muito grande. Dessa maneira, constrói-se oAODU baseando-se no
conceito de bloco de instruções, isto é, instruções que são executadas seqüencialmente em um fluxo
de controle normal, sem nenhuma possível interrupção (como na versão generalizada doAODUdefinida acima). OAODU é então o modelo base para derivar requisitos de teste baseados no
fluxo de controle e fluxo de dados para o teste de unidade de programas escritos em AspectJ. O
AODU de uma dada unidadeu é definido como um grafo dirigidoAODU(u) = (N, E, s, T, C),
tal que cada nón ∈ N representa um bloco de instruções de bytecode:
• N representa o conjunto de nós de um grafoAODU : N = {n|n corresponde a um bloco
de instruções de bytecode deu}. Isto é,N é o conjunto não-vazio de nós, representando as
instruções de bytecode deu. In é a n-tupla ordenada de instruções agrupadas no nóu;
• E = Er ∪ Ee é o conjunto completo de arestas do grafoAODU . Considere oIG(u) =
(NI, EI, si, T I, CI):
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 49
– Er é o conjunto de arestas regulares definido comoEr = {(ni, nj)| existe uma aresta
regular que vai do último elemento deIniao primeiro elemento deInj
noIG(u)};
– Ee é o conjunto de arestas de exceção definido comoEe = {(ni, nj)| existe uma aresta
de exceção que vai do último elemento deIniao primeiro elemento deInj
noIG(u)};
– Ec ⊆ Er é o conjunto de arestas transversais (crosscutting edges) definido comoEc =
{(x, y) ∈ E|(x ∈ C) ∨ (y ∈ C)} (componenteC definida abaixo);
• s ∈ N |IN(s) = 0 é o nó de entrada deu;
• T ⊆ N é o conjunto (possivelmente vazio) de nós de saída, isto é,T = {n ∈ N |OUT (n) =
0};
• C ⊆ N é o conjunto (possivelmente vazio) de nós transversais. Nesse caso um nó transversal
corresponde a um bloco de instruções no qual uma das instruções representa uma invocação
de um método correspondente a um adendo de um dado aspecto.
O algoritmo utilizado para reduzir um grafo de instrução de fluxo de dadosIG para umAODUé apresentado na Figura3.1. O algoritmo também é estendido do trabalho deVincenzi (2004) para
considerar os nós e arestas transversais. Nas linhas15-20 os nós transversais são identificados
e adicionados ao conjuntoC. Também é garantido que haja apenas uma execução de adendo
para cada nó transversal. Além disso, nas linhas5-7, as arestas transversais (conjuntoEc) são
identificadas.
Na Figura3.2é mostrada uma aplicação OA simples escrita em AspectJ. Partes dos bytecodes
do métodoaffectedMethod sem e com a presença do aspecto são mostradas nas Figuras3.3e
3.4e os grafosAODU referentes ao método, sem e com a presença do aspecto, são mostrados na
Figura3.5. Os conjuntoscu, pu e d correspondem aos c-usos, p-usos e definições de variáveis. A
representação gráfica doAODU é definida da seguinte forma:
• Nós regulares são representados por círculos com o rótulo contendo a primeira instrução de
bytecode do bloco;
• Nós de chamada são representados por círculos duplos;
• Nós transversais são representados por elipses tracejadas, com informação de qual tipo de
adendo afeta aquele ponto (posterior, anterior ou de contorno –before, after ou around),
e a qual aspecto pertence. Por exemplo, se há uma interação com um adendo posterior de
um aspectoAspectem um certo ponto, o nó transversal correspondente é adicionado com a
informação� after − Aspect �;
• Nós de saída são representados por nós negritados;
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 50
# Entrada: IG , o grafo de fluxo de dados de instrução# IG =< NI, EI, si, T I, CI > a ser reduzido;# Saída: AODU , o grafo de fluxo de dadosAODU =< N,E, s, T, C >01 s:= NovoBloco(si)02 para cadax ∈ N03 sex não tem sucessores04 T := T ∪ {x}05 para cada(x, y) ∈ E06 sex ∈ C ouy ∈ C07 Ec := Ec ∪ {(x, y)}#Função Auxiliar: NovoBloco#Entrada: Um nóy do grafoIG#Saída: Um bloco do grafoAODU08 ins := a instrução de bytecode emy09 sey já foi visitado10 retorne o nów ∈ N que contémy11 BlocoAtual := novobloco12 N := N ∪ {BlocoAtual}13 x := y14 faça15 sex ∈ CI //x é um nó transversal deIG16 seBlocoAtual ∈ C17 E := E ∪ {(BlocoAtual, NovoBloco(x))}18 x := null19 senão20 C := C ∪ {BlocoAtual}21 sex 6= null22 incluax como parte doBlocoAtual23 marquex como visitado24 sex termina o bloco atual25 para cadav tal que(x, v) ∈ EIr
26 Er := Er ∪ {(BlocoAtual, NovoBloco(v))}27 para cadav tal que(x, v) ∈ EIe
28 Ee := Ee ∪ {(BlocoAtual, NovoBloco(v))}29 x := null30 senão31 se existe umv tal que(x, v) ∈ EIr
32 x := v33 senãox := null34 enquantox 6= null35 retorneBlocoAtual
Figura 3.1: Algoritmo para gerar um grafoAODU a partir de um grafoIG.
• Arestas de exceção (Ee) são representadas por arestas tracejadas, representando o fluxo de
controle do ponto onde uma exceção é gerada até o primeiro nó correspondente ao tratador
daquela exceção.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 51
public class Point {public int x;public int y;public AClass a;
public Point( int _x, int _y) {x = _x; y = _y;
}
public void affectedMethod(Point p, int_x, int _y) {try {
if (p.x <= 10 && p.y <= 10) {p.x = _x; p.y = _y;p.a = new AClass(10, 20);
}p.printPoint(p);System.out.println(p.x);
} catch (AnException ae) {System.out.println("Exception " +
"catched!");}
}
public void printPoint(Point p)throws AnException {if (p.x == 0) {
AnException ae = new AnException();throw ae;
}...
}...public static class AnException
extends Exception {...
}}
public aspect AnAspect {pointcut exec(Point p, int i, int j):
execution ( voidPoint.affectedMethod(Point, int , int ))&& args (p, i, j);
pointcut settingA(AClass a):set (AClass Point.a) && args (a) &&! within (aspects.*);
pointcut handlerPC(Point p) :handler (Point.AnException) && this (p);
before (Point p, int i, int j):exec(p, i, j) {if (p.x >= 0) p.x = i + 3;if (p.y >= 0) p.y = j + 4;
}
after (Point p, int i, int j) returning ():exec(p, i, j) {System.out.println("after " +"returning exec");if (i > 10)
System.out.println("i > 10");else
System.out.println("i <= 10");if (p.x > 10)
System.out.println("p.x > 10");else
System.out.println("p.x <= 10");}
void around (AClass a) : settingA(a) {System.out.println("around settingA");a.a = 20; a.b = 30;proceed (a);
}
before (Point p) : handlerPC(p) {p.x = 40;
}}
Figura 3.2: Exemplo de um programa orientado a aspectos escrito em AspectJ.
Adendos de Contorno – AspectJ Na implementação atual do AspectJ (versão 1.2), quando
um adendo de contorno que utiliza o métodoproceed – que prossegue com a execução do ponto
de junção – é aplicado em uma determinada unidade, o ponto de junção original (bloco de by-
tecode) é extraído e colocado em um método separado que aceita quaisquer variáveis do método
onde o adendo é aplicado. O adendo de contorno recebe então um novo parâmetro, um objeto
fecho (closure object), e as chamadas aoproceed são substituídas por chamadas a um método
run do objeto fecho recebido como parâmetro, que por sua vez faz a chamada ao método que
encapsula o ponto de junção original. Isso é feito porque quando a chamada aoproceed é feita
em um tipo aninhado, o combinador deve assumir que a chamada é referente ao ponto de junção
mais próximo no aninhamento.
Sendo assim, em uma implementação da abordagem descrita neste capítulo em uma ferramenta
de teste para programas escritos em AspectJ, podem ser geradosAODUs também para os métodos
que encapsulam os pontos de junção originais. Dessa forma, permite-se a análise de cobertura para
os pontos de junção afetados por adendos de contorno que utilizam oproceed .
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 52
0 aload_11 getfield #17 <Field int x>4 bipush 106 if_icmpgt 439 aload_110 getfield #19 <Field int y>13 bipush 1015 if_icmpgt 4318 aload_119 iload_220 putfield #17 <Field int x>23 aload_124 iload_325 putfield #19 <Field int y>28 aload_129 new #29 <Class components.AClass>32 dup33 bipush 1035 bipush 2037 invokespecial #31 <Method AClass(int,int)>
40 putfield #33 <Field components.AClassa>
43 aload_144 aload_145 invokevirtual #37 <Method void printPoint(
components.Point)>48 getstatic #43 <Field java.io.PrintStream
java.lang.System.out>51 aload_152 getfield #17 <Field int x>55 invokevirtual #49 <Method void
println(int)>58 goto 7161 astore 463 getstatic #43 <Field java.io.PrintStream
java.lang.System.out>66 ldc #51 <String "Exception catched!">68 invokevirtual #54 <Method void println(
java.lang.String)>71 return
Figura 3.3: Parte do bytecode do métodoaffectedMethod sem a presença do aspectoAnAspect .
0 aload_1...18 invokevirtual #116 <Method void
ajc$before$aspects_AnAspect$1$dcd6c0af(components.Point, int, int)>
21 aload_122 getfield #17 <Field int x>25 bipush 1027 if_icmpgt 10930 aload_131 getfield #19 <Field int y>34 bipush 1036 if_icmpgt 10939 aload_1...106 invokevirtual #110 <Method void
ajc$around$aspects_AnAspect$2$f8ad947f(components.AClass, org.aspectj.runtime.internal.AroundClosure)>
109 aload_1110 aload_1111 invokevirtual #37 <Method void
printPoint(components.Point)>
114 getstatic #43 <Field java.io.PrintStreamjava.lang.System.out>
117 aload_1118 getfield #17 <Field int x>128 invokestatic #106 <Method
aspects.AnAspect aspects.AnAspect.aspectOf()>
131 aload_0132 invokevirtual #113 <Method void
ajc$before$aspects_AnAspect$4$e6001804(components.Point)>
135 astore 4137 getstatic #43 <Field java.io.PrintStream
java.lang.System.out>140 ldc #51 <String "Exception catched!">142 invokevirtual #54 <Method void println(
java.lang.String)>145 goto 148...157 invokevirtual #119 <Method void
ajc$afterReturning$aspects_AnAspect$3$dcd6c0af(components.Point, int, int)>
160 return
Figura 3.4: Parte do bytecode do métodoaffectedMethod com a presença do aspectoAnAspect .
3.3.2 Critérios de Fluxo de Controle
Antes da definição dos critérios, alguns conceitos precisam ser definidos. Nos seguintes cri-
térios de teste estruturais – seguindo o trabalho deVincenzi (2004) – os requisitos de teste foram
divididos em dois conjuntos disjuntos: um contendo todos os requisitos que podem ser cobertos
durante a execução normal de um programa – denominado independente de exceção – e outro con-
tendo apenas os requisitos que necessitam do lançamento de uma exceção para serem cobertos –
denominado dependente de exceção. A partir daí, definem-se os seguintes conceitos:
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 53
pu={p.x, p}
pu={p.y,p}
pu={p.y,p}
pu={p.x, p}
d={p,p.x,p.y,_x,_y}
cu={p,_x,_y}
cu={p.x}
d={p.a,p.x, p.y}
cu={p.x,p}
<<around−AnAspect>>
<<before−AnAspect>>
<<before−AnAspect>>
pu={p.x, p}
pu={p.y,p}
cu={p.x}
cu={p.x,p}
pu={p.y,p}
pu={p.x, p}
cu={p,_x,_y}d={p.x, p.y}
d={p,p.x,p.y,_x,_y}
cu={p, _x, _y}<<afterReturning−AnAspect>>
18
61
71
43
0
9
39
21
30
0
109
135
127
148
160
Figura 3.5: AODUs do métodoaffectedMethod sem e com a presença do aspectoAnAspect .
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 54
• Conjunto de nós predicativos: ConsidereOUTr(i) como o número de arestas regulares de
saída de um nói, formalmente:OUTr(i) = |{(i, j)|(i, j) ∈ Er}|. O conjunto de nós
predicativos é o conjuntoNpred = {n ∈ N |OUTr(n) > 1}, isto é, o conjunto de todos os
nós do grafoAODU que contêm mais de uma aresta regular de saída.
• Caminhos livres de exceção: O conjunto de caminhos livres de exceção é o conjuntoπ|∀(ni
, nj) ∈ π ⇒ (ni, nj) é alcançável por um caminho que não contém nenhuma aresta de
exceção.
• Nós dependentes e independentes de exceção: O conjunto de nós dependentes de exceção é
definido comoNed = {n ∈ N |@ um caminho livre de exceçãoπ tal quen ∈ π}. O conjunto
de nós independentes de exceção é o conjunto definido comoNei = N −Ned.
• Arestas dependentes e independentes de exceção: As arestas dependentes de exceção for-
mam o conjuntoEed = {e ∈ E|@ um caminho livre de exceçãoπ tal quee ∈ π}. As arestas
independentes de exceção formam o conjuntoEei = E − Eed.
• Arestas transversais: O conjunto de arestas transversais é definido comoEc = {(x, y) ∈Er|(x ∈ C) ∨ (y ∈ C)}.
• C-usos globais e locais: Um c-uso de uma variávelx em um nój é um c-uso global se não há
nenhuma definição dex no mesmo nój, em uma instrução anterior ao c-uso. Caso contrário
é um c-uso local.
Dois critérios de fluxo de controle – todos-nós e todas-arestas – definidos porMyers (1979)
são aplicados no contexto de programas orientados a aspectos. ConsidereT um conjunto de casos
de teste para um programaP (sendo queAODU é o grafo de fluxo de controle/dados deP ), eΠ o
conjunto de caminhos exercitados na execução deT . Um conjuntoΠ inclui um nói seΠ contém
um caminho(ni, ..., nm) tal quei = nj para algumj, 1 ≤ j ≤ m. Similarmente, um conjuntoΠ
inclui uma aresta(i1, i2) seΠ contém um caminho(n1, ..., nm) tal quei1 = nj e i2 = nj+1 para
algumj, 1 ≤ j ≤ m− 1.
O Critério Todos-Nós
• Π satisfaz o critério todos-nós seΠ inclui cada nón ∈ N do grafoAODU . Em outras
palavras, esse critério garante que cada instrução (ou comando) de uma dada unidade é
executada pelo menos uma vez por um caso de teste deT .
Para utilizar as considerações de tratamento de exceção que foram feitas, seguindo o trabalho de
Vincenzi (2004), o critério Todos-Nós foi particionado em dois conjuntos disjuntos de elementos
de teste requeridos, resultando nos seguintes critérios:
• todos-nós-independentes-de-exceção (Todos-Nósei)
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 55
– Π satisfaz o critério todos-nós-independentes-de-exceção se cada nónei ∈ Nei está in-
cluído emΠ. Em outras palavras, este critério requer que cada nó de um grafoAODUque é alcançável por meio de pelo menos um caminho livre de exceção seja executado
ao menos uma vez por algum caso de teste deT .
• todos-nós-dependentes-de-exceção (Todos-Nósed)
– Π satisfaz o critério todos-nós-dependentes-de-exceção se cada nóned ∈ Ned está in-
cluído emΠ. Em outras palavras, este critério requer que cada nó de um grafoAODUque não é alcançável por meio de um caminho livre de exceção seja executado ao me-
nos uma vez por por algum caso de teste deT .
Na aplicação do critério Todos-Nós em programas orientados a aspectos, uma propriedade
interessante seria a habilidade de saber quais dos nós cobertos são específicos desse contexto, para
que haja a possibilidade do testador focar nesses pontos na atividade de teste. Como observado na
seção anterior, um novo tipo de interação ocorre em unidades de programas orientados a aspectos,
representada pelos nós transversais.
Dessa maneira, seria interessante a existência de um critério de teste particular que requere-
ria a cobertura dos nós transversais, para auxiliar a revelar defeitos particulares a esses pontos.
Conseqüentemente, a partir da análise de cobertura baseada nesse critério, o testador teria conhe-
cimento sobre quando os casos de teste estariam – ou não – sensibilizando os aspectos.
A partir dessa motivação, define-se o seguinte critério:
• todos-nós-transversais (Todos-NósC)
– Π satisfaz o critério todos-nós-transversais se cada nóni ∈ C está incluído emΠ. Em
outras palavras, esse critério requer que cada nó transversal, e portanto cada execução
de adendo que ocorre na unidade afetada, seja exercitado pelo menos uma vez por
algum caso de teste deT .
O Critério Todas-Arestas
• Π satisfaz o critério Todas-Arestas se cada arestae ∈ E de um grafoAODU está incluída
em Π. Em outras palavras, esse critério garante que cada aresta do grafoAODU de uma
dada unidade é executada ao menos uma vez por algum caso de teste deT .
Assim como foi feito para o critério Todos-Nós, para utilizar as considerações de tratamento
de exceção que foram feitas, seguindo o trabalho deVincenzi (2004), o critério Todas-Arestas
foi particionado em dois conjuntos disjuntos de elementos de teste requeridos, resultando nos
seguintes critérios:
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 56
• todas-arestas-independentes-de-exceção (Todas-Arestasei)
– Π satisfaz o critério todas-arestas-independentes-de-exceção se cada arestaeei ∈ Eei
está incluída emΠ. Em outras palavras, esse critério exige que cada aresta do grafo
AODU que é alcançável por pelo menos um caminho livre de exceção seja exercitada
pelo menos uma vez por algum caso de teste deT .
• todas-arestas-dependentes-de-exceção (Todas-Arestased)
– Π satisfaz o critério todas-arestas-dependentes-de-exceção se cada arestaeed ∈ Eed
está incluída emΠ. Em outras palavras, esse critério exige que cada aresta do grafo
AODU que é inalcançável por qualquer dos caminhos livres de exceção doAODU é
exercitada pelo menos uma vez por algum caso de teste deT .
Da mesma maneira que tem-se nós especiais noAODU – os nós transversais – pode-se consi-
derar também a existência de arestas especiais, que conectam os nós transversais. Do ponto de vista
do teste, seria interessante também a informação de quando tais arestas são exercitadas, seguindo a
mesma idéia do critério todos-nós-transversais. Essa informação é interessante porque um defeito
poderia ser revelado somente quando uma aresta transversal em particular fosse escolhida.
Na Figura3.6são mostrados dois exemplos de métodos afetados por um aspecto, um relacio-
nado com arestas transversais de entrada (methodA ) e outro relacionado com arestas transversais
de saída (methodB ). A cobertura dos nós transversais não revelaria necessariamente a divisão
por zero. Se a aresta transversal(0, 9) do métodomethodA e (0, 14) do métodomethodB são
exercitadas, uma exceção de divisão por zero é lançada.
A partir dessa motivação, define-se o critério todas-arestas-transversais:
• todas-arestas-transversais (Todas-ArestasC)
– Π satisfaz o critério todas-arestas-transversais se cada arestaec ∈ Ec está incluída em
Π. Em outras palavras, esse critério requer que cada aresta do grafoAODU que tem
um nó transversal como nó início ou destino seja exercitada por algum caso de teste de
T .
3.3.3 Critérios de Fluxo de Dados
Antes da definição dos critérios de fluxo de dados, alguns conceitos precisam ser definidos:
• Os conjuntos def, c-uso, p-uso, dcu e dpu: Na implementação dos critérios de fluxo de da-
dos, todos os usos de uma variável em um nó predicativoi ∈ Npred são considerados p-usos.
Essa decisão foi feita no trabalho deVincenzi (2004) porque é computacionalmente caro
distinguir c-usos de p-usos a partir das instruções de bytecode Java, considerando a estrutura
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 57
public class ClassA {int n;public ClassA() {
n = 10;}public void methodA( boolean b) {
this .n = 1;if (b == true )
this .n = 0;}public void methodB( boolean b) {
int num;num = 10;if (b == true )
num = num / this .n;System.out.println(num);
}}
public aspect AspectA {after (ClassA c) returning () :
execution ( voidClassA.methodA( boolean ))&& this (c) {
int div = 10 / c.n;}before (ClassA c) :
execution ( voidClassA.methodB( boolean ))&& this (c) {
c.n = 0;}
}
<<afterRetrurning−AspectA>>
0
9
17
<<before−AspectA>>0
14
21
Figura 3.6: Um programa simples escrito em AspectJ e osAODUs dos métodosmethodA emethodB da classeClassA .
orientada a pilhas da máquina virtual Java. Assim, para um nói e uma variávelx do grafo
AODU , são definidos:
def(i) = {variáveis definidas no nói}
c-uso(i)=
variáveis com uso global emi sei é computacional
∅ caso contrário
p-uso(i)=
variáveis com uso local ou global emi sei é predicativo
∅ caso contráriodcu(x, i) = { nós j de um grafoAODU tal quex ∈ c-uso(j) e existe um caminho livre de
definição parax dei a j}
dpu(x, i) = { arestas(j, k) de um grafoAODU tal quex ∈ p-uso(j) e existe um caminho
livre de definição parax dei à aresta(j, k)}
• Pares def-c-uso e def-p-uso: Um par def-c-uso é uma tripla(i, j, x) ondei é um nó que
contém uma definição dex e j ∈ dcu(x, i). Um par def-p-uso é uma tripla(i, (j, k), x) onde
i é um nó que contém uma definição dex e (j, k) ∈ dpu(x,i). Um par Def-Uso é um par
def-c-uso ou def-p-uso.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 58
• Os conjuntos dcued, dcuei, dpued, dpuei: Os conjuntos de pares foram particionados, con-
siderando a existência ou não existência de caminhos livres de definição que também são
livres de exceção. Assim dcued(x, i) ={nós j de um grafoAODU tal quex ∈ c-uso(j) e
não existe nenhum caminho livre de definição parax que também é um caminho livre de
exceção dei a j}. O dcuei(x, i) = dcu(x, i)− dcued(x, i). O mesmo é feito com relação aos
p-usos: o dpued(x, i) ={arestas(j, k) de um grafoAODU tal quex ∈ p-uso(j) e não existe
nenhum caminho livre de definição parax que também é um caminho livre de exceção dei
a (j, k)}. O dpuei(x, i) = dpu(x, i)− dpued(x, i).
O critério Todos-Usos
• Π satisfaz o critério Todos-Usos se para cadai ∈ def(i), Π inclui um caminho livre de
definição parax de i a cada elemento de dcu(x, i) e a cada elemento dpu(x, i). Em outras
palavras, esse critério requer que cada par def-c-uso(i, j, x) tal quej ∈ dcu(x, i) e cada
par def-p-uso(i, (j, k), x) tal que(j, k) ∈ dpu(x, i) seja exercitado ao menos uma vez para
algum caso de teste deT .
Seguindo a mesma idéia que foi aplicada aos critérios Todos-Nós e Todas-Arestas, os requisitos
de teste do critério Todos-Usos são particionados em dois conjuntos disjuntos, como definido nos
seguintes critérios:
• todos-usos-independentes-de-exceção (Todos-Usosei)
– Π satisfaz o critério todos-usos-independentes-de-exceção se para cada nói ∈ N e
para cadax ∈ def(i), Π inclui um caminho livre de definição parax do nói a todos
elementos de dcuei(x, i) e todos elementos de dpuei(x, i). Em outras palavras, esse
critério requer que cada par def-c-uso livre de exceção(i, j, x)|j ∈ dcuei(x, i) e cada
par def-p-uso livre de exceção(i, (j, k), x)|(j, k) ∈ dpuei(x, i) seja exercitado pelo
menos uma vez por algum caso de teste deT .
• todos-usos-dependentes-de-exceção (Todos-Usosed)
– Π satisfaz o critério todos-usos-dependentes-de-exceção se para cada nói ∈ N e para
cadax ∈ def(i), Π inclui um caminho livre de definição parax do nói a todos os ele-
mentos de dcued(x, i) e a todos os elementos de dpued(x, i). Em outras palavras, esse
critério requer que cada par def-c-uso dependente de exceção(i, j, x)|j ∈ dcued(x, i) e
cada par def-p-uso dependente de exceção(i, (j, k), x)|(j, k) ∈ dpued(x, i) seja exerci-
tado ao menos uma vez para algum caso de teste deT .
Um ponto interessante a ser notado sobre o fluxo de dados de um programa orientado a aspectos
é que um aspecto e uma unidade afetada por seus adendos podem trocar dados (por exemplo,
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 59
quando variáveis de contexto são passadas para algum adendo). Dessa forma, outra idéia de critério
para programas orientados a aspectos seria exercitar os pares Def-Uso cujos usos estão em nós
transversais – porque esses usos são evidências de troca de dados entre classes e aspectos. Esse
critério é interessante pois interações de dados errôneas entre classes e aspectos são uma possível
fonte de erros.
Quando variáveis de contexto são passadas a algum adendo, em bytecode, essas variáveis são
empilhadas na pilha de execução (e de maneira geral isso é feito no mesmo bloco da execução
do adendo, ou seja, em um nó transversal). Na Tabela3.1 pode ser notado que essas instruções
de pilha caracterizam o uso das variáveis empilhadas. Assim, quando variáveis de contexto –
e qualquer outra informação – são passadas para adendos, no modelo utilizado neste trabalho é
caracterizado um uso da variável em um nó transversal.
A partir daí define-se o critério todos-usos-transversais:
• todos-usos-transversais (Todos-UsosC)
– Π satisfaz o critério todos-usos-transversais se para cada nói ∈ def(i), Π inclui um ca-
minho livre de definição parax dei a cada elemento de dcu(x, i) que é um nó transver-
sal e a todos elementos de dpu(x, i) nos quais o nó início da aresta é um nó transversal.
Em outras palavras esse critério requer que cada par def-c-uso(i, j, x)|j ∈ dcu(x, i)
tal quej ∈ C e cada par def-p-uso(i, (j, k), x)|(j, k) ∈ dpu(x, i) tal quej ∈ C seja
exercitado pelo menos uma vez por algum caso de teste deT .
3.3.4 Exemplo
Na Tabela3.2 são mostrados os requisitos de teste para cada critério OA definido, para o
métodoaffectedMethod apresentado na Figura3.2. Pela simplicidade do exemplo, o conjunto
de casos de teste Todos-NósC-adequado gerado é também adequado para os outros critérios.
Tabela 3.2:Requisitos de teste para cada critério OA definido, para o métodoaffectedMethod .
Critério Conjunto de Requisitos Conjunto de Casos de Teste
Todos-NósC Rn = {0, 39, 127, 148} Rn = {((0, 0), 7, 6),((0, 0),−3, 7)}
Todas-ArestasC Re = {(0, 21), (21, 127)∗, (30, 39), Rn
(30, 127)∗, (39, 109), (39, 127)∗, (109, 148),(127, 135), (135, 148), (148, 160)}
Todos-UsosC Ru = {(_y, 0, 39), (_x, 0, 39), (p, 0, 39), Rn
(this, 0, 39), (this, 0, 127), (L@83, 0, 148),(L@9, 0, 148), (L@10, 0, 148)}
∗ Requisitos não-executáveis
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 60
3.3.5 Comparação entre os Critérios de Teste de Unidade
Na Figura3.7 é mostrada a hierarquia de inclusão considerando os critérios definidos no tra-
balho deVincenzi (2004) e os critérios OA definidos neste trabalho (Todos-UsosC , Todos-NósCe Todas-ArestasC). A seguir, é feita a demonstração da não inclusão Todos-UsosC ; Todas-
ArestasC e da inclusão Todas-ArestasC ⇒ Todos-NósC .
Todos-C a m in h os
Todos-Usos
Todos-Usosei Todos-Usosed Todos-U sosC
Toda s-Aresta s
Toda s-Aresta sei Toda s-Aresta sedTodas-ArestasC
Todos-Nos
Todos-Nosei Todos-Nosed Todos-N osC
Figura 3.7: Hierarquia dos critérios definidos no trabalho deVincenzi (2004) e neste trabalho.
SejaT um conjunto de casos de teste para um programaP (AODU sendo o grafo de fluxo de
controle deP ) e sejaΠ o conjunto de caminhos executados porT .
• Todos-UsosC ; Todas-ArestasCPara provar que o critério Todos-UsosC não inclui o critério Todas-ArestasC , basta considerar
o grafoAODU da Figura3.8(a). O conjuntod corresponde às definições de variáveis,c aos
c-usos ep aos p-usos.{(0, 1, 4, 5, 6)} é Todos-UsosC-adequado mas não é Todas-ArestasC-
adequado, pois as arestas transversais(2, 3) e (3, 6) não são cobertas.
• Todas-ArestasC ⇒ Todos-NósCSuponha queT é Todas-ArestasC-adequado para P. Seja(i, j) ∈ Ec uma aresta transversal
de P. ComoT é Todas-ArestasC-adequado, isso implica que∀(n1, n2) ∈ Ec, existe um
caminho(n1, ..., nm) emΠ, tal quen1 = nj e n2 = nj+1, para algumj, 1 ≤ j ≤ m − 1.
SeT não é Todos-NósC-adequado, então existe pelo menos um nónc ∈ C tal quenc não
está incluído em nenhum dos caminhos deΠ. Entretanto, por definição, todo nó transversal
nc ∈ C é origem ou destino de uma aresta transversal. Desse modo, existe uma aresta
3Os nomes de variáveisL@N são gerados pela ferramenta JaBUTi/AJ (Capítulo4). Nesse caso, as variáveiscorrespondem a dados inseridos pelo compilador do AspectJ, e por isso o nomeL@N não foi substituído na tabelapelo nome das variáveis encontradas no código fonte.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 61
transversal(n1, nc) ∈ Ec ou uma aresta transversal(nc, n2) ∈ Ec. Portanto, seT é Todas-
ArestasC-adequado, entãoT é também Todos-NósC-adequado.
Para provar que Todos-NósC ; Todas-ArestasC , basta considerar oAODU apresentado
na Figura3.8(b). {(0, 1, 2)} satisfaz o critério Todos-NósC , porém não satisfaz o critério
Todas-ArestasC , pois não inclui a aresta transversal(0, 2).
<<before−AnAspect>><<before−AnAspect>>
0
c={X}
2
1
3
4
5
6
c={X}
d={X,Y}
p={Y} p={Y}
(a) Todos-UsosC ; Todas-ArestasC
2
<<before−AnAspect>>
1
0
(b) Todos-NósC ; Todas-ArestasC
Figura 3.8: GrafosAODU utilizados como contra-exemplos.
3.4 Teste Estrutural de Integração
Os diferentes tipos de teste de módulo definidos na Seção3.3 podem ser muito caros para
serem aplicados se todas as chamadas indiretas e interações indiretas com adendos forem consi-
deradas (a complexidade pode ser exponencial). Dessa forma, pode-se restringir a profundidade
das chamadas e interações com adendos, para apoiar uma atividade de teste praticável – um tipo
de teste similar ao teste par a par (pairwise).
Neste trabalho foca-se o teste do tipo Método-adendo, considerando somente as interações
diretas com adendos. Em trabalhos futuros esses tipos de teste podem ser mais detalhados e re-
finados, explorando, por exemplo, o teste intra/inter classes, considerando ou não a presença dos
aspectos e também o teste de aspectos isolados.
3.4.1 Teste Método-adendo
Para tornar possível a aplicação do teste de integração de fluxo de dados a programas orientados
a aspectos, mais especificamente ao teste Método-adendo, são estendidas as definições para o teste
de unidade apresentadas anteriormente. Evidentemente, esse tipo de teste é importante, já que,
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 62
como apontado porAlexander (2003), Alexander e Bieman (2002b) e Alexander et al. (2004),
dependências de dados emergentes entre classes e aspectos nos artefatos combinados podem ser
possíveis fontes de erros. Um exemplo desse tipo de defeito seria um conjunto de junção pouco
restrito que captura mais pontos de junção do que deve, aplicado a um adendo anterior de chamada
que altera os parâmetros passados para o método chamado. As chamadas a métodos às quais o
adendo não deveria ser aplicado serão feitas com valores de parâmetros errados.
O modelo base para o teste de fluxo de dados de integração Método-adendo é o grafoMADU(Method-Advice Def-Use). OMADU é composto peloAODU do método em si, juntamente
com osAODUs dos adendos que o afetam diretamente. Os nós transversais doAODU do mé-
todo são quebrados em um nó de aprimoramento (enh– enhancement node) e um nó de retorno
(ret – return node). Esse mecanismo é similar àquele utilizado para o teste inter-procedimental,
na representação de chamadas e retornos de procedimentos (Harrold e Soffa, 1989). Arestas adi-
cionais são criadas para conectar os nósenh com os nós de entrada dosAODUs dos adendos e
para conectar os nós de saída dosAODUs dos adendos com os nósret. O grafoMADU deve ser
construído para cada método afetado por adendos.
O escopo do métodocorresponde aoAODU do método (incluindo os nósenh e ret) e os
escopos de adendoscorrespondem aosAODUs dos adendos. Cada interação de um adendo em
um determinado ponto de junção começa um novo escopo de adendo indexado, e os rótulos dos
nós dosAODUs dos adendos são prefixados com o índice do escopo correspondente. Isso é feito
porque a numeração das instruções de bytecode se repetem em cada unidade, iniciando sempre de
0.
Na Figura3.9é mostrado um exemplo de um grafoMADU do métodoaffectedMethod
mostrado anteriormente (Figura3.2), construído a partir da análise do bytecode combinado. Pode
ser notado que cada par de nósenh e ret substitui cada nó transversal doAODU do método
affectedMethod (Figura3.5). Além disso, informação de fluxo de dados é obtida para cada
nó e aresta, para os escopos do método e dos adendos. Na computação de definições e usos,
variáveis são renomeadas quando necessário – por exemplo, o argumento_x que é utilizado no
adendo posterior do aspectoAnAspect como variável locali .
Pares Def-Uso Aspectuais Baseando-se nas diferentes interações de dados que podem ocor-
rer em um método afetado por aspectos, e baseando-se nos diferentes escopos representados no
grafoMADU , quatro tipos de pares Def-Usoaspectuaispodem ser definidos. SejaM um mé-
todo afetado por adendos,d uma sentença contendo uma definição de uma variávelx e u uma
sentença contendo um uso dex, os seguintes pares são definidos:
• componente-componente: Sed eu estão emM , e existe um caminho livre de definição para
x ded au, então(x, d, u) é um par Def-Uso componente-componente.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 63
39
0
0
method−scope
pu={p.x, p}
d={p.x}
d={p.y}
advice−scope 1
advice−scope 4
pu={p.x,p} pu={p.x,p}
cu={p}
pu={p.y,p} pu={p.y,p}
cu={p}
pu={p.y,p} pu={p.y,p}
cu={p,_x,_y}pu={_x}pu={_x}
pu={p.x,p} pu={p.x,p}
cu={p.x,p}
pu={p.x, p}
127
127
148
148
d={p,p.x,p.y,_x,_y}
advice−scope 2cu={a}d={a.a, a.b}
advice−scope 3
d={p.x}cu={p}
d={p.x, p.y}
21
30
ret
ret
ret
39
1.15
1.22
1.29
1.36
1.0
4.0
4.14 4.25
4.33
4.42 4.53
4.61
3.0
ret
109
2.0
enh
enh
enh
enh
Figura 3.9: GrafoMADU do métodoaffectedMethod .
• componente-aspecto: Seja{A1, A2, ..., An} o conjunto de adendos que interagem comM
em uma invocação deM . Sed está emM , u está emAi, e existe um caminho livre de
definição parax ded au então(x, d, u) é um par Def-Uso componente-aspecto.
• aspecto-componente: Seja{A1, A2, ..., An} o conjunto de adendos que interagem comM
em uma invocação deM . Sed está emAi, tal queAi ∈ {A1, A2, ..., An}, u está emM ,
e existe um caminho livre de definição parax de d a u, então(x, d, u) é um par Def-Uso
aspecto-componente.
• aspecto-aspecto: Seja{A1, A2, ..., An} o conjunto de adendos que interagem comM em
uma invocação deM . Sed está emAi, tal queAi ∈ {A1, A2, ..., An}, u está emAj, tal
queAj ∈ {A1, A2, ..., An}, e existe um caminho livre de definição parax ded a u, então
(x, d, u) é um par Def-Uso aspecto-aspecto.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 64
Critérios de Teste de Integração Baseados no Fluxo de Dados
Antes da definição dos critérios são necessários alguns conceitos. Como no teste de fluxo de
dados de unidade, para cada nó do grafoMADU é associado um conjunto c-uso e um conjunto
def, e para cada aresta do grafo é associado um conjunto p-uso. Além disso são definidos os
conjuntos dcu e dpu:
• dcu(x, i) é o conjunto de todos os nósj de um dado grafoMADU , tal quex ∈ c-uso(j) e
existe um caminho livre de definição parax dei a j.
• dpu(x, i) é o conjunto de todas as arestas de um dado grafoMADU , tal quex ∈ p-uso(j)
e existe um caminho livre de definição parax dei a j.
SejaT um conjunto de casos de teste para o teste de um programaP , eΠ o conjunto de cami-
nhos exercitados a partir da execução deT . Baseado nos tipos de pares Def-Uso definidos anteri-
ormente, e no critério Todos-Usos, são definidos os seguintes critérios de teste Método-adendo:
• todos-cc-usos (todos-usos-componente-componente):T satisfaz o critério todos-cc-usos se
para cada nói no escopo do método do grafoMADU e para cadax ∈ def(i), Π inclui um
caminho livre de definição parax dei para cada elemento dedcu(x, i) e para cada elemento
dedpu(x, i) que estão no escopo do método.
• todos-ca-usos (todos-usos-componente-aspecto):T satisfaz o critério todos-ca-usos se para
cada nói no escopo do método do grafoMADU e para cadax ∈ def(i), Π inclui um
caminho livre de definição parax dei para cada elemento dedcu(x, i) e para cada elemento
dedpu(x, i) que estão em escopo de adendos.
• todos-ac-usos (todos-usos-aspecto-componente):T satisfaz o critério todos-ac-usos se para
cada nói em escopo de adendo do grafoMADU e para cadax ∈ def(i), Π inclui um
caminho livre de definição parax dei para cada elemento dedcu(x, i) e para cada elemento
dedpu(x, i) que estão no escopo de método.
• todos-aa-usos (todos-usos-aspecto-aspecto):T satisfaz o critério todos-aa-usos se para cada
nó i em escopo de adendo do grafoMADU e para cadax ∈ def(i), Π inclui um caminho
livre de definição parax de i para cada elemento dedcu(x, i) e para cada elemento de
dpu(x, i) que estão em escopo de adendo.
Também é definido o critério tradicional Todos-Usos para esse contexto, que inclui os quatro
critérios definidos acima:
• todos-usos: Um conjuntoΠ de caminhos executados por um conjunto de casos de teste
satisfaz o critério todos-usos se para cada nói do grafoMADU e para cadax ∈ def(i), Π
inclui um caminho livre de definição parax dei para cada elemento dedcu(x, i) e para cada
elemento dedpu(x, i).
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 65
Exemplo
Na Figura3.9apresentada anteriormente, foi mostrado o grafoMADU do métodoaffect-
edMethod . Na Tabela3.3 são mostrados os conjuntos de pares Def-Uso aspectuais – todos os
requisitos de teste de cada um dos critérios definidos anteriormente coletados a partir de uma
análise doMADU –, juntamente com conjuntos de casos de teste adequados para cada critério.
A entrada para os casos de teste é composta de um ponto e dois inteiros, as entradas do método
affectedMethod . Por exemplo, para exercitar o par Def-Uso aspecto-aspecto (1.15, (4.33,
4.53), p.x), pode ser utilizada a entrada ((0, 1), 7, 10), que faria com que o programa exercitasse
o seguinte caminho: (0 enh, 1.0, 1.15, 1.22, 1.29, 1.36, 0 ret, 21, 109, 148 enh, 4.0, 4.25, 4.33,
4.53, 4.61, 148 ret). Na Figura3.10é mostrado este caminho no grafoMADU . A variável p.x
é definida no nó 1.15 e usada na aresta (4.33, 4.53) sem ser redefinida, assim exercitando o par
Def-Uso. Também pode ser notado que alguns requisitos de teste não são executáveis, isto é, não
existem entradas que podem exercitá-los.
Para dar uma idéia do custo relacionado com a abordagem, na Tabela3.4é mostrado o total de
casos de teste requeridos em um conjunto de casos de teste adequado para cada um dos critérios
definidos, para o métodoaffectedMethod integrado com os adendos que o afetam diretamente.
Os totais de requisitos não-executáveis também são apresentados.
Tabela 3.3:Pares Def-Uso aspectuais coletados a partir do grafoMADU do métodoaffectedMethod e conjuntos de casos de teste adequados para cada critério.
Critério Conjunto de Requisitos Conjunto de Casos de Teste
todos-ac-usos Rac = {(1.15, (21, 30), p.x), (1.15, (21, 109), p.x), Tac = {((0, 1), 7, 4), ((0, 1), 8, 4),(1.15, 109, p.x), (1.29, (30, 109), p.y), ((0, 1), 7, 10)}(1.29, (30, 39 enh), p.y)}
todos-ca-usos Rca = {(0 enh, (4.0, 4.25), _x), (0 enh, (4.0, 4.14), _x), Tca = {((0, 1), 10, 4),(0 enh, (4.33, 4.42), p.x)∗, (0 enh, (4.33, 4.53), p.x), ((0, 1), 11, 4), ((−1, 1), 7, 10),(39 enh, (4.33, 4.42), p.x)∗, (39 enh, (4.33, 4.53), p.x)} ((0, 1), 7, 4)}
todos-aa-usos Raa = {(1.15, (4.33, 4.42), p.x), (1.15, (4.33, 4.53), p.x), Taa = {((0, 1), 8, 10),(3.0, (4.33, 4.42), p.x)∗, (3.0, (4.33, 4.53), p.x)∗} ((0, 1), 7, 10)}
todos-cc-usos Rcc = {(0 enh, (21, 30), p), (0 enh, (21, 30), p.x), Tcc = Tac ∪ {((0,−1), 7, 4),(0 enh, (21, 109), p), (0 enh, (21, 109), p.x)∗, ((−1, 1), 7, 4)}(0 enh, (30, 39 enh), p), (0 enh, (30, 39 enh), p.y),(0 enh, (30, 109), p), (0 enh, (30, 109), p.y)∗,(0 enh, 39 enh, p), (0 enh, 39 enh, _x), (0 enh, 39 enh, _y),(0 enh, 109, p), (0 enh, 109, p.x)∗}
todos-usos Rac ∪Rca ∪Raa ∪Rcc Tac ∪ Tca ∪ Taa ∪ Tcc
* Requisitos não-executáveis
3.5 Comparação e Avaliação da Abordagem Proposta
Comparando a abordagem proposta neste trabalho com as outras propostas, algumas diferen-
ças e vantagens podem ser notadas. Em comparação com o trabalho deZhao (2003), no que diz
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 66
39
0
0
method−scope
pu={p.x, p}
d={p.y}
advice−scope 1
advice−scope 4
pu={p.x,p} pu={p.x,p}
cu={p}
pu={p.y,p} pu={p.y,p}
cu={p}
pu={p.y,p} pu={p.y,p}
cu={p,_x,_y}pu={_x}pu={_x}
pu={p.x,p}
pu={p.x, p}
127
127
148
148
d={p,p.x,p.y,_x,_y}
advice−scope 2cu={a}d={a.a, a.b}
advice−scope 3
d={p.x}cu={p}
d={p.x, p.y}
cu={p.x,p}
pu={p.x,p}
d={p.x}
21
30
ret
ret
ret
39
1.15
1.22
1.29
1.36
1.0
4.0
4.14 4.25
4.33
4.42 4.53
4.61
3.0
ret
109
2.0
enh
enh
enh
enh
Figura 3.10: Caminho percorrido no grafoMADU do métodoaffectedMethod , a partir daentrada ((0, 1), 7, 10).
Tabela 3.4:Total de casos de teste e de requisitos não-executáveis para cada critério definido,para o métodoaffectedMethod .
Critério Casos de Teste Requisitos Não-executáveis
todos-ac-usos 3 0todos-ca-usos 4 2todos-aa-usos 2 2todos-cc-usos 5 3todos-usos 9 7
respeito ao teste de unidade, a abordagem proposta aqui oferece a vantagem de permitir a realiza-
ção do teste de unidade de adendos e métodos isoladamente. O teste intra-módulo proposto por
Zhao (2003) já inclui a interação entre métodos e adendos, sendo que o teste isolado de adendos e
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 67
métodos não pode ser conduzido. Na Tabela3.5 são relacionadas as fases de teste da abordagem
de Zhao (2003, 2002) e deste trabalho. Como comentado na Seção2.3.5, o conceito demódulo
utilizado porZhao (2003) refere-se aos c-métodos e c-adendos, ou seja, aos métodos juntamente
com os adendos que o afetam e aos adendos juntamente com os métodos afetados por eles. Por
exemplo, analisando a Figura3.11na qual é mostrado umAOCFG de um dado método, na abor-
dagem deZhao (2003) um dos grafos dos c-métodos a serem testados seria referente a este método
somado com os grafos de fluxo de cada um dos adendos que o estão afetando (o que é feito na
abordagem proposta neste trabalho somente na fase de integração). Dessa forma, nem o método
nem os adendos podem ser testados em isolamento na fase de unidade. Na Figura3.12é mostrado
como ficaria esse grafo na abordagem deZhao (2003), com os adendos envolvidos por retângulos
tracejados, e utilizando o conceito de blocos de comandos ao invés de criar um nó para cada linha
de código, como é feito porZhao (2003)4. Outro fator que pode ser notado é a dificuldade em dis-
tinguir as partes relacionadas com os adendos das partes relacionadas com o método no grafo de
um c-método (na figura isso foi feito de manualmente), ao passo que na abordagem deste trabalho
as partes estão bem separadas e a análise de cobertura também é feita separadamente.
Tabela 3.5:Relação entre as fases de teste no teste de programas OA na abordagem destetrabalho e na abordagem deZhao (2003).
Menor Unidade: Método/Adendo
Fase Teste de Software Orientado a AspectosUnidade Intra-método e Intra-adendo
Integração Inter-método, Método-adendo, Adendo-Método,Adendo-Adendo, Intra-classe e Inter-classe
Sistema Toda a aplicaçãoMenor Unidade: C-Classe/C-Aspecto
Fase Teste de Software Orientado a AspectosUnidade Intra-módulo, Inter-módulo, Intra-classe ou Intra-Aspecto
Integração Inter-classeSistema Toda a aplicação
É importante notar também queZhao (2003) não define nenhum critério de teste em seus ar-
tigos, e a abordagem proposta neste trabalho define nove critérios de teste diferentes, focando
propriedades diversas.Zhao (2003) também não comenta sobre o teste baseado em fluxo de con-
trole.
No que diz respeito ao trabalho deXie et al. (2004), pode ser notado um problema no algoritmo
de detecção de casos de teste relevantes para o teste dos aspectos. O ponto é que não se pode inferir
4Na verdade, o grafo de um c-método é quase o mesmo que umMADU de um método afetado por adendos, coma diferença que na proposta deste trabalho, o teste Método-adendo é feito depois de se testar as unidades isoladas, ouseja, na fase de integração.
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 68
<<before−FigureLogging>>
<<before−FigureLogging>>
<<afterReturning−FigureLogging>>
<<afterReturning−FigureLogging>>
0
4
60 81
200
224
227
239
248
251
257
8
Figura 3.11: GrafoAOCFG para um dado método.
que um caso de teste sensibilizou um dado aspecto pelo simples fato do caso de teste chamar um
método que é afetado pelo aspecto, pois isso pode depender da entrada do caso de teste.
Por exemplo, analisando a Figura3.11novamente, se um caso de teste para o método exercitar
o caminho(0, 4), tal caso de teste nem sequer faria com que os adendos que afetam o método
fossem executados, e portanto seria irrelevante para o teste do aspecto. No entanto, no algoritmo
para seleção de casos de teste relevantes proposto porXie et al. (2004), tal caso de teste seria
considerado relevante para o teste do aspecto, o que seria um equívoco.
Quanto ao trabalho deXu et al. (2004a), no que diz respeito à parte relacionada ao teste estru-
tural, não são definidos critérios de teste. Além disso, como acontece também com a proposta de
Zhao (2003), o teste isolado de adendos e métodos não pode ser realizado.
A justificativa para a definição dos critérios de teste de integração para programas OA é similar
à justificativa para a definição dos critérios de teste de integração baseados em fluxo de dados
propostos para o teste de programas procedimentais e orientados a objetos, quando procedimentos
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 69
0
4
81
8
248
60
1.0
60
1.361.60
1.88
1.151
224
2.0
3.0
3.363.69
3.88 3.121
3.151
4.0
1.121
Figura 3.12: Grafo do c-método baseado no grafo do método apresentado na Figura3.11.
(métodos) interagem com outros procedimentos (métodos) que chamam. A principal diferença
é que em um programa orientado a aspectos essas interações são mais sutis, porque, de forma
geral, os desenvolvedores não sabem exatamente onde e como os aspectos vão interagir com os
componentes. O modelo e os critérios propostos expõem essas interações, facilitando não somente
o teste mas também o entendimento de programas OA.
Os critérios de teste de integração específicos para programas OA são partições do critério
tradicional Todos-Usos definido nesse contexto. A razão para particionar esse critério em quatro é
a informação particular que cada um oferece, que pode ser valiosa para o desenvolvedor/testador.
Por exemplo, os requisitos de teste expostos pelo critério todos-aa-usos podem expor interações de
dados entre diferentes adendos que afetam o mesmo método, e essas interações podem ser muito
CAPÍTULO 3. TESTE ESTRUTURAL DE PROGRAMAS ORIENTADOS A ASPECTOS 70
sutis para o desenvolvedor/testador. Se fosse definido somente um critério – o Todos-Usos – essa
informação somente poderia ser obtida de manualmente.
Comparando o critério Todos-Usos (definido nesse contexto) com cada um dos quatro critérios
de teste OA, utilizando a relação de inclusão (Zhu et al., 1997), pode ser provado que os últi-
mos são mais fracos, pois requerem subconjuntos dos requisitos de teste do critério Todos-Usos.
Porém, como apontado por Weyuker (Weyuker, 2002), um critério incluído não é necessariamente
menos eficiente em encontrar defeitos que o critério que o inclui, pois tipicamente existem diversos
conjuntos de casos de teste adequados para um dado critério.
A abordagem de teste de integração proposta pode auxiliar o testador a encontrar defeitos
particulares aos programas OA. Por exemplo, os defeitos do tipo 3 e 4 do modelo de defeitos
proposto porAlexander et al. (2004) (Seção2.3.5) são fortemente relacionados com interações
de dados que ocorrem em programas OA, isto é, um defeito em respeitar pós-condições definidas
ou em preservar invariantes de estado causadas por aspectos deve ser relacionado com aspectos
alterando dados nos componentes quando não deveriam. Dessa forma, o testador pode detectar tais
defeitos no exercício dos pares Def-Uso aspectuais nos quais as definições de variáveis ocorrem
em escopos de adendos (pares Def-Uso aspecto-componente e aspecto-aspecto).
3.6 Considerações Finais
Neste capítulo foi apresentada uma abordagem de teste estrutural para programas orientados a
aspectos, para o teste de unidade e de integração. O grafo de fluxo de controle/dados foi estendido
para representar as unidades e módulos de programas OA e critérios de teste baseados em fluxo
de controle e de dados foram definidos a partir dos grafos, incluindo critérios específicos para
programas OA.
Com a abordagem de teste definida, é necessário implementá-la em uma ferramenta de teste,
para facilitar a análise de cobertura a partir dos critérios de teste. No próximo capítulo são definidas
as extensões da ferramenta JaBUTi (Vincenzi, 2004), que levaram à criação da versão JaBUTi/AJ
(AspectJ), para apoiar o teste de unidade de programas orientados a aspectos escritos em AspectJ.
Para ilustrar a aplicabilidade da abordagem ainda são apresentados alguns exemplos de aplicação
da ferramenta no teste de programas OA.
CAPÍTULO
4Teste de Unidade: Automatização e
Exemplos
4.1 Considerações Iniciais
Como observado no Capítulo2, a atividade de teste, se realizada manualmente, é geralmente
propensa a erros e limitada a aplicações de pequeno porte (dos Santos Domingues, 2001). Nesse
contexto, ferramentas de teste podem auxiliar a automatizar essa tarefa, permitindo a aplicação dos
critérios de teste estruturais de maneira mais eficiente e prática. Além disso, as ferramentas de teste
possibilitam a realização detestes de regressão, quando manutenções são feitas no programa e os
casos de teste armazenados são executados novamente para a validação da aplicação modificada
(dos Santos Domingues, 2001).
Para implementar a abordagem de teste de unidade de programas AspectJ proposta no capítulo
anterior, é necessária uma ferramenta que possibilite a instrumentação do código-objeto de Java –
o bytecode – e derive os modelos de fluxo de controle e de dados a partir da análise estática do
bytecode. A ferramenta JaBUTi proposta porVincenzi (2004) e discutida na Seção2.3.4oferece
essas funcionalidades, necessitando de algumas extensões para possibilitar o teste de programas
OA de acordo com a abordagem proposta neste trabalho.
Na Seção4.2são descritas as alterações da ferramenta JaBUTi que resultaram na versão JaBU-
Ti/AJ (Java Bytecode UnderstandiIng and Testing/ AspectJ) para o teste de unidade de programas
OA baseados na linguagem AspectJ. Na Seção4.3, a operação da ferramenta é ilustrada por meio
de três exemplos e, por fim, na Seção4.5são apresentadas as considerações finais do capítulo.
71
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 72
4.2 Extensão da Ferramenta JaBUTi para Apoio ao Teste
de Unidade de Programas OA
A ferramenta JaBUTi foi estendida para possibilitar a utilização dos critérios orientados a as-
pectos propostos neste trabalho, ou seja, os critérios todos-nós-transversais, todas-arestas-transver-
sais e todos-usos-transversais. Nessa versão a ferramenta foi batizada de JaBUTi/AJ. Primeira-
mente a implementação do grafo Def-Uso definido para o teste de programas Java foi estendida
para representar o grafoAODU . O pacotegraph foi utilizado para essa atividade e, em parti-
cular, a classeCFGNodeque implementa um nó CFG de um grafo, foi estendida em uma classe
CFGCCNodepara representar os nós transversais (crosscutting nodes) com as informações extras
(o tipo do adendo que afeta o ponto e a qual aspecto ele pertence). Um nó transversal é identificado
sempre que existe uma chamada a um método referente a um adendo no bloco de bytecode relativo
ao nó (ver Seções2.2.1e3.3.1).
O pacotecriteria foi utilizado para a implementação dos critérios OA. Três classes novas
foram criadas:AllCrosscuttingNodes , AllCrosscuttingEdges e AllCrosscut-
tingUses , cada uma estendendo a classeAbstractCriterion que implementa métodos
comuns de critérios de teste estrutural. Para a classeAllCrosscuttingNodes , a alteração
feita teve o objetivo de filtrar os requisitos de teste, incluindo como requisitos somente os nós
transversais. Para a classeAllCrosscuttingEdges , uma estratégia similar foi seguida, in-
cluindo como requisitos somente as arestas cujo nó de início ou de destino são nós transversais.
Para a classeAllCrosscuttingUses os requisitos de teste também foram filtrados, incluindo
somente os requisitos nos quais os usos estão em nós transversais.
As outras alterações realizadas dizem respeito a: representação gráfica dos grafos, para mostrar
os nós transversais com as informações extras; mudança das nomenclaturas utilizadas na interface
(incluindo também os aspectos e adendos como unidades a serem testadas); e inclusão dos critérios
novos na interface gráfica. Para isso foram estendidos os pacotesgvf , project e gui . Em
termos quantitativos foram escritas em torno de 1000 linhas de código, além das alterações dos
códigos pré-existentes (Tabela4.1).
Tabela 4.1:Arquivos criados na extensão da JaBUTi para JaBUTi/AJ.Pacote Arquivo LOCcriteria AllCCNodes.java 225
AllCCEdges.java 157AllCCUses.java 350
graph CFGCCNode.java 118gvf GVFCCNode.java 146
Total 996
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 73
4.3 Funcionamento da Ferramenta JaBUTi/AJ e Exem-
plos de Aplicação
Nesta seção são apresentados alguns exemplos de teste de aplicações OA utilizando a ferra-
menta JaBUTi/AJ, tanto para ilustrar a operação da ferramenta quanto para evidenciar a aplicabili-
dade da abordagem proposta neste trabalho. As características de operação da ferramenta não são
aprofundadas, já que poucas alterações foram feitas no que diz respeito a essa propriedade. Mais
informações sobre as demais funcionalidades da ferramenta JaBUTi podem ser obtidas a partir do
Manual do Usuário (Vincenzi et al., 2004).
4.3.1 Aplicação de Figuras 2D
O primeiro exemplo refere-se a uma aplicação simples de Figuras 2D, dividida em dois pacotes
– components e aspects – contendo os componentes e os aspectos da aplicação. Na Figura
4.1 é mostrado o diagrama UML da aplicação com estereótipos para representar os entrecortes e
os aspectos.
−x−y
+setX()+setY()+getX()+getY()+moveBy()+distance()+rotate
+moveBy()+distance()
+setP1()+setP2()+getP1()+getP2()
−p2−p1
FigureLogging
+<<pointcut>>pointConstr()+<<pointcut>>setY()+<<pointcut>>setX()
+indentationLevel
FigureElement<<interface>>
<<crosscuts>>
<<crosscuts>> <<crosscuts>>
Point LineSegment
+moveBy()
PointBoundsChecking
+<<pointcut>>setX()+<<pointcut>>setY()+<<pointcut>>pointConstr()
<<aspect>>
<<aspect>>
Figura 4.1: Diagrama UML da aplicação de figuras 2D orientada a aspectos.
O pacotecomponents contém:
• Uma interfaceFigureElement que possui um método para mover o elemento de figura;
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 74
• Uma classePoint que modela pontos e uma classeLineSegment que modela segmentos
de reta, ambas implementando a interfaceFigureElement (Figura4.2);
package components;
public class Point implements FigureElement {private double x = 0, y = 0;public Point( double _x, double _y) {
x = _x;y = _y;
}public void setX( double _x) {
x = _x;}public void setY( double _y) {
y = _y;}public double getX() {
return x;}public double getY() {
return y;}public void moveBy( double dx, double dy) {
x = x + dx;y = y + dy;
}public double distance(Point pt) {
double d = java.lang.Math.sqrt(java.lang.Math.pow((x - pt.x),2) +java.lang.Math.pow((y - pt.y),2));
return d;}public void rotate( Point center, double angle ) {
double radius = this .distance( center );double deltaY = y - center.getY();double deltaX = x - center.getX();double theta = Math.atan( deltaY/deltaX );
x = center.getX() + radius * Math.cos( angle +theta );
y = center.getY() + radius * Math.sin( angle +theta );
}}
import java.lang.Double;import java.lang.Math;
public class LineSegment implements FigureElement {private Point p1, p2;public LineSegment (Point _p1, Point _p2) {
p1 = _p1;p2 = _p2;
}public Point getP1() {
return p1;}public Point getP2() {
return p2;}void setP1(Point p1) {
this .p1 = p1;}void setP2(Point p2) {
this .p2 = p2;}public void moveBy( double dx, double dy) {
p1.moveBy(dx, dy);p2.moveBy(dx, dy);
}double distance(Point pt) {
double t, bot, dot, xn, yn;if (pt == null )
return java.lang.Double.POSITIVE_INFINITY;
bot = java.lang.Math.pow((p2.getX() -p1.getX()),2) + java.lang.Math.pow(
(p2.getY() - p1.getY()),2);if ( bot == 0.0 ) {
xn = p1.getX();yn = p1.getY();
}else {
dot = (pt.getX() - p1.getX()) * (p2.getX() -p1.getX()) +
(pt.getY() - p1.getY()) * (p2.getY() -p1.getY());
t = dot / bot;xn = p1.getX() + t * (p2.getX() - p1.getX());yn = p1.getY() + t * (p2.getY() - p1.getY());
}
Point Pn = new Point(xn,yn);double dist = pt.distance(Pn);return dist;
}public Point intersection(LineSegment l2) {
double x1 = this .getP1().getX();double y1 = this .getP1().getY();double x2 = this .getP2().getX();double y2 = this .getP2().getY();double x3 = l2.getP1().getX();double y3 = l2.getP1().getY();double x4 = l2.getP2().getX();double y4 = l2.getP2().getY();double uaN = (x4 - x3)*(y1 - y3) -
(y4 - y3)*(x1 - x3);double ubN = (x2 - x1)*(y1 - y3) -
(y2 - y1)*(x1 - x3);double den = (y4 - y3)*(x2 - x1) -
(x4 - x3)*(y2 - y1);if (den == 0)
return null ;else {
double ua = uaN/den;double ub = ubN/den;double x = x1 + ua*(x2 - x1);double y = y1 + ua*(y2 - y1);
Point p = new Point(x, y);return p; // intersection
}}public void rotate( Point center, double angle ) {
p1.rotate( center, angle );p2.rotate( center, angle );
}public void scale( double factor ) {
double newXsubA = factor * p1.getX();double newYsubA = factor * p1.getY();double newXsubB = factor * p2.getX();double newYsubB = factor * p2.getY();p1.setX( newXsubA );p1.setY( newYsubA );p2.setX( newXsubB );p2.setY( newYsubB );
}}
public class Main {public static void main(String[] args ) {
Point p1 = new Point(10, 10);Point p2 = new Point(11, 11);LineSegment l1 = new LineSegment(p1, p2);LineSegment l2 = new LineSegment(p1, p2);
}}
Figura 4.2: Código das classesPoint , LineSegment eMain do pacotecomponent .
O pacoteaspects contém os aspectos (Figura4.3):
• O aspectoFigureLogging , para o registro na tela dos métodos que são chamados durante
a execução do programa;
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 75
• O aspectoPointBoundsChecking , que define limites no plano onde os pontos podem
estar (nesse caso entre 100 e -100 tanto para o eixo das ordenadas quanto para o eixo das
abscissas);
package aspects;
import components.*;
public aspect FigureLogging {declare precedence: FigureLogging,PointBoundsChecking, *;
private int indentationLevel = 0;
public pointcut loggedOperations(): call (* components.*.*(..)) &&
! call (* components.*. get *(..));
before () : loggedOperations() {indentationLevel++;
Signature sig =thisJoinPointStaticPart.getSignature();
if (thisJoinPointStaticPart.getSignature().getDeclaringType().getName()
== "components.LineSegment")PrintIndented("LineSegment <" + sig.getName() +
">");else
if (thisJoinPointStaticPart.getSignature().getDeclaringType().getName()
== "components.Point")PrintIndented("Point <" + sig.getName() + ">");
elsePrintIndented("other <" + sig.getName() + ">");
}
after () returning (): loggedOperations() {indentationLevel--;
}
void PrintIndented(String s) {for ( int i = 0, spaces = indentationLevel * 4;
i < spaces; ++i) {System.out.print(" ");
}System.out.println(s);
}}
public aspect PointBoundsChecking {
pointcut setX( double x):execution ( void Point.setX( double )) && args (x);
pointcut setY( double y):execution ( void Point.setY( double )) && args (y);
pointcut pointConstr( double x, double y):execution (Point. new(..)) && args (x, y);
before ( double x): setX(x) {System.out.println("Checking bounds for x...");if (assertCoord(x) == 0)
throw newIllegalArgumentException("Coordinate x
is out of bounds (< -100)");else if (assertCoord(x) == 1)
throw newIllegalArgumentException("Coordinate x
is out of bounds (> -100)");}
before ( double y): setY(y) {System.out.println("Checking bounds for y...");if (assertCoord(y) == 0)
throw newIllegalArgumentException("Coordinate y
is out of bounds (< -100)");else if (assertCoord(y) == 1)
throw newIllegalArgumentException("Coordinate y
is out of bounds (> -100)");}before ( double x, double y): pointConstr(x, y) {
int xok = 2, yok = 2;System.out.println("Checking bounds for x...");xok = assertCoord(x);yok = assertCoord(y);if (xok == 0) {
if (yok == 0)throw new
IllegalArgumentException("Coordinates x,y are out of bounds" +
" (x < -100, y < -100)");else if (yok == 1)
throw newIllegalArgumentException("Coordinates x,
y are out of bounds" +" (x < -100, y > 100)");
elsethrow new
IllegalArgumentException("Coordinate xis out of bounds" +
" (< -100)");} else if (xok == 1) {
if (yok == 0)throw new
IllegalArgumentException("Coordinates x,y are out of bounds" +
" (x > 100, y < -100)");else if (yok == 1)throw new
IllegalArgumentException("Coordinates x,y are out of bounds" +
" (x > 100, y > 100)");elsethrow new
IllegalArgumentException("Coordinate xis out of bounds" +
" (> 100)");} else {
if (yok == 0)throw new
IllegalArgumentException("Coordinate yis out of bounds" +
" (< -100)");else if (yok == 1)throw new
IllegalArgumentException("Coordinates yis out of bounds" +
" (> 100)");}
}
public int assertCoord( double i) {if ( i < -100)
return 0;if ( i > 100)
return 1;return 2;
}}
Figura 4.3: Código dos aspectosFigureLogging ePointBoundsChecking .
Para a utilização da ferramenta JaBUTi/AJ, é necessário criar um projeto de teste. Para isso o
testador deve, primeiramente, fornecer o nome de uma classe base, ou seja, o nome de uma classe
do tipo aplicação a partir da qual as demais classes relacionadas serão derivadas1.
1Parte desta seção foi retirada/adaptada da tese deVincenzi (2004).
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 76
No caso da aplicação para figuras 2D, pode ser escolhida como classe base a classeMain
que constrói pontos e segmentos de reta. Como os aspectos interagem com as classes de ponto e
segmento de reta, eles também são identificados como possíveis integrantes do projeto. Na Figura
4.4 é mostrada a tela do gerenciador de projetos da JaBUTi/AJ, após ter sido escolhida a classe
Main como classe base. A partir daí podem ser selecionadas todas as classes e aspectos a serem
instrumentados e testados.
Figura 4.4: Janela do gerenciador de projetos da JaBUTi/AJ.
JaBUTi/AJ cria então um novo projeto, construindo o grafo Def-Uso orientado a aspectos
(AODU) para cada método de cada classe a ser testada e para cada adendo de cada aspecto a ser
testado. Os requisitos de teste de cada critério são derivados e o peso dos requisitos é calculado. A
partir desse peso, a ferramenta utiliza diferentes cores para destacar os requisitos, para auxiliar o
testador a cobrir os de maior peso. Além disso, para cada requisito, a ferramenta calcula o número
de outros requisitos que seriam cobertos a partir da cobertura daquele requisito, e mostra esse
número juntamente com a cor relacionada.
Inicialmente, a ferramenta apresenta a tela com o bytecode de uma das classes em teste, como
ilustrado na Figura4.5(a). Além da visualização do bytecode, é oferecida ainda a visualização do
grafoAODU de cada método (adendo), conforme ilustrado na Figura4.5(b), e também do código
fonte correspondente ao bytecode, conforme ilustrado na Figura4.5(c) (quando o código fonte
encontra-se disponível).
Uma vez que o conjunto de requisitos de teste de cada critério foi determinado, tais requi-
sitos podem ser utilizados para avaliar a qualidade de um conjunto de teste existente e/ou para
desenvolver novos casos de teste visando a melhorar a cobertura dos requisitos pelo conjunto de
teste. Por exemplo, o testador pode decidir criar um conjunto de teste com base em critérios de
teste funcionais ou mesmo gerar um conjunto de teste sem nenhum método pré-definido e ava-
liar a cobertura desse conjunto de teste em relação a cada um dos critérios de teste estruturais da
ferramenta. Por outro lado, o testador pode visualizar o conjunto de requisitos de teste de cada
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 77
(a) Bytecode do aspectoPointBoundsChecking . (b) AODU do métododistance .
(c) Código fonte do aspectoFigureLogging .
Figura 4.5: Telas da JaBUTi/AJ.
critério gerados para cada um dos métodos/adendos das classes/aspectos em teste, verificar quais
deles ainda não foram cobertos por algum caso de teste e então desenvolver um novo caso de teste
que cubra tais requisitos. Nas Figuras4.6(a), 4.6(b)e 4.6(c)são ilustrados os requisitos de teste
do métodoLineSegment.distance gerados pelos critérios Todos-NósC , Todas-ArestasC , e
Todos-UsosC , respectivamente.
Como pode ser observado na Figura4.6, a ferramenta permite ao testador ativar/desativar di-
ferentes combinações de requisitos de teste, bem como marcar um determinado requisito de teste
como não-executável quando não existir um caso de teste capaz de cobri-lo.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 78
(a) Todos-NósC (b) Todas-ArestasC
(c) Todos-UsosC
Figura 4.6: Requisitos de teste do métodoLineSegment.distance gerados pelos critériosTodos-NósC , Todas-ArestasC , e Todos-UsosC .
A partir dos requisitos dos critérios OA o testador pode então criar conjuntos de casos de
teste adequados para os critérios. Por exemplo, no caso do métodoLineSegment.distance ,
para cobrir todos os nós transversais pode ser criado um caso de teste a partir de 3 pontos:
P1 = (0, 1), P2 = (0,−1) e P3 = (4, 3). Cria-se um segmento de reta do pontoP1 ao P2 e
depois calcula-se a distância do pontoP3 à reta que contém o segmento de retaP1P2. Como o as-
pectoFigureLogging define adendos em todas as chamadas a métodos do pacotecomponent
(exceto chamadas aos métodosget ), as execuções dos adendos anteriores e posteriores à chamada
ao métodoPoint.distance são cobertas a partir da execução desse caso de teste. Na Figura
4.7(a)é mostrado o grafoAODU do método após a execução do caso de teste. Os requisitos de
teste cobertos – que são os próprios nós transversais, no caso do critério Todos-NósC – aparecem
no grafo com a cor branca. O caso de teste escrito utilizando o JUnit é mostrado na Figura4.8.
A partir da execução do caso de teste mostrado na Figura4.8, quando se analisa o critério
Todas-ArestasC , nota-se que a aresta transversal(60, 200) não é coberta. Na Figura4.7(b)é mos-
trado oAODU do método com base nesse critério. O nó60 aparece pintado de amarelo pelo
fato da aresta(60, 200) não ter sido coberta. Dessa forma, um conjunto de caso de teste Todas-
ArestasC-adequado poderia incluir o caso de teste anterior e mais um que faça com que a aresta
(60, 200) seja executada. Na Figura4.9é mostrado um caso de teste que exercita tal aresta, que é
o caso em que o segmento de linha para o qual se quer calcular a distância é formado por pontos
coincidentes.
Outra atividade importante no teste de programas OA é o teste dos próprios aspectos e, mais
especificamente, o teste dos adendos isoladamente. Os critérios OA geralmente não são apli-
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 79
(a) Todos-NósC (b) Todas-ArestasC
Figura 4.7: GrafosAODU gerados pela ferramenta JaBUTi/AJ baseados nos critériosTodos-NósC e Todas-ArestasC , após a execução do caso de teste.
public void testCoverLineSegmentDistanceCCNodes() {Point p1 = new Point(0, 1);Point p2 = new Point(0, -1);Point p3 = new Point(4, 3);LineSegment l = new LineSegment(p1, p2);
double d = l.distance(p3);
System.out.println(d);
assertEquals(d, 4, .0);}
Figura 4.8: Caso de teste para cobrir os nós transversais do métodoLineSegment.distance .
public void testCoverLineSegmentDistanceNodes0Length() {Point p1 = new Point(0, 0);Point p2 = new Point(0, 0);Point p3 = new Point(4, 3);LineSegment l1 = new LineSegment(p1, p2);
double d = l1.distance(p3);
assertEquals(5, d, .0);}
Figura 4.9: Caso de teste para cobrir a aresta transversal(60, 200) do métodoLineSegment.distance .
cados aos próprios aspectos, já que de maneira geral não existem muitas interações adendo-
adendo. Apesar disso o restante dos critérios geralmente são aplicáveis. Por exemplo, na Figura
4.10 é mostrado o grafoAODU gerado a partir da JaBUTi/AJ do adendo anterior do aspecto
PointBoundsChecking que é aplicado ao conjunto de junçãopointConstr . A partir dos
requisitos gerados para o critério Todos-Nós, por exemplo, poderiam ser criados casos de teste para
cobrir todos os comandos do adendo. Na Figura4.11é mostrado um conjunto de casos de teste
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 80
Todos-Nós-adequado escritos no JUnit, para o adendo mostrado anteriormente. Cada caso de teste
testa um dos casos no qual os limites estabelecidos pelo aspecto são extrapolados (por exemplo,
quando a coordenadax do ponto extrapola o limite inferior –< 100 – e a coordenaday extrapola
o limite superior –> 100). É interessante notar que, por sua própria definição, os adendos não po-
dem ser executados em isolamento (pelo menos sem alguma infra-estrutura especial). Dessa forma
os casos de teste para os adendos devem ser aplicados nos pontos de junção afetados por eles. Por
exemplo, no caso do adendo em questão é necessário criar e modificar pontos que extrapolem os
limites para que ele seja sensibilizado e testado.
Figura 4.10:AODU do adendo anterior do aspectoPointBoundsChecking .
4.3.2 Aplicação de Simulação de Telefonia
Este exemplo refere-se a um sistema simples que modela conexões telefônicas para as quais
os interesses de temporização e faturamento são implementados utilizando aspectos. O aspecto
de faturamento depende do aspecto de temporização. O exemplo simula um sistema de telefonia
no qual clientes fazem, aceitam, juntam e concluem ligações de longa distância ou locais. A
arquitetura da aplicação contém três camadas. Os objetos básicos oferecem a funcionalidade para
simular clientes, chamadas e conexões2.
As classes do sistema são:2Parte desta seção e o exemplo demonstrado foram baseados no texto da página mantida porAspectJ Team
(2004): http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/doc/progguide/index.html .
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 81
public void testCoverPointBoundsCheckingCheckPointConstrOK() {Point p = new Point(1, 1);
assertEquals(1.0, p.getX(), 0.0);assertEquals(1.0, p.getY(), 0.0);
}
public void testCoverPointBoundsCheckingCheckPointConstrOnlyXLTNodes() {try {
Point p1 = new Point(-101, 0);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrOnlyYLTNodes() {try {
Point p1 = new Point(-100, -101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrOnlyYGTNodes() {try {
Point p1 = new Point(100, 101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrOnlyXGTNodes() {try {
Point p1 = new Point(101, 100);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrBothLTNodes() {try {
Point p1 = new Point(-101, -101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrBothGTNodes() {try {
Point p1 = new Point(101, 101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrXLTYGTNodes() {try {
Point p1 = new Point(-101, 101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
public void testCoverPointBoundsCheckingCheckPointConstrXGTYLTNodes() {try {
Point p1 = new Point(101, -101);fail();
} catch (java.lang.IllegalArgumentException e) {}}
Figura 4.11: Conjunto de casos de teste Todos-Nós-adequado para o adendo anterior do aspectoPointBoundsChecking .
• Customer , que modela clientes e possui atributos para o nome e o código de área do
cliente;
• a classe abstrataConnection com suas duas classes concretasLocal eLongDistance ,
que modelam conexões locais e de longa distância;
• Call , que modela chamadas;
• Timer , que modela temporizadores.
Os aspectos do sistema são:
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 82
• Timing , que implementa o interesse de temporização e se encarrega de medir os tempos
das conexões por cliente, iniciando e parando um temporizador associado a cada conexão;
• Billing , que implementa o interesse de faturamento e se encarrega de declarar que cada
conexão tem um cliente pagador – o cliente que inicia a chamada – e também que as chama-
das locais e de longa distância devem ser cobradas com taxas diferentes;
• TimerLog , que implementa um registro que imprime na tela os horários em que um tem-
porizador inicia e pára.
Na Figura4.12 é apresentado o diagrama de classes UML das classes-base da aplicação de
telefonia. Na Figura4.13é apresentado o código das classes-base e na Figura4.14é apresentado
o código dos aspectos.
Call
+hangUp()
Connection−state
+complete()
+pickUp()
+drop()+merge()
*−areaCode
receiver
caller
+call()
LongDistance Local
Customer
calls
−name
Figura 4.12: Diagrama UML das classes-base da aplicação de telefonia (adaptado da páginamantida porAspectJ Team (2004)).
Chamadas são criadas a partir da classeCall , com um cliente como chamador (caller )
e outro como chamado (receiver ). Se chamado e chamador têm o mesmo código de área,
a chamada é estabelecida a partir de uma conexão local, caso contrário é requerida uma cone-
xão de longa distância. Uma chamada é composta por um número de conexões entre clientes.
Inicialmente existe apenas uma conexão entre o chamador e o chamado, porém outras conexões
podem ser adicionadas se chamadas são juntadas para formarem conferências. A classe de cone-
xão (Connection ) modela os detalhes físicos do estabelecimento de uma conexão entre clien-
tes. Para isso é utilizada uma máquina de estados simples (conexões iniciam como pendentes –
PENDING–, depois são completadas –COMPLETED– e finalmente desligadas –DROPPED).
Para o teste estrutural dessa aplicação, primeiramente poder-se-ia criar um projeto na ferra-
menta JaBUTi/AJ, escolhendo todos os arquivos relevantes, inclusive os aspectos. Como os cri-
térios OA geralmente geram menos requisitos de teste do que os outros critérios (por causa da
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 83
public class Customer {private String name;private int areacode;private Vector calls = new Vector();
protected void removeCall(Call c){calls.removeElement(c);
}protected void addCall(Call c){
calls.addElement(c);}public Customer(String name, int areacode) {
this .name = name;this .areacode = areacode;
}public String toString() {
return name + "(" + areacode + ")";}public int getAreacode(){
return areacode;}public boolean localTo(Customer other){
return areacode == other.areacode;}public Call call (Customer receiver) {
Call call = new Call( this , receiver);addCall( call );return call ;
}public void pickup(Call call ) {
call .pickup();addCall( call );
}public void hangup(Call call ) {
call .hangup( this );removeCall( call );
}public void merge(Call call1, Call call2){
call1.merge(call2);removeCall(call2);
}}
public class Call {private Customer caller, receiver;private Vector connections = new Vector();public Call(Customer caller, Customer receiver) {
this .caller = caller;this .receiver = receiver;Connection c;if (receiver.localTo(caller)) {
c = new Local(caller, receiver);} else {
c = new LongDistance(caller, receiver);}connections.addElement(c);
}public void pickup() {
Connection connection =(Connection)connections.lastElement();connection.complete();
}public boolean isConnected(){
return((Connection)connections.lastElement()).getState()
== Connection.COMPLETE;}public void hangup(Customer c) {
for (Enumeration e = connections.elements();e.hasMoreElements();) {((Connection)e.nextElement()).drop();
}}
public boolean includes(Customer c){boolean result = false ;
for (Enumeration e = connections.elements();e.hasMoreElements();) {result = result ||((Connection)e.nextElement()).connects(c);
}return result;
}public void merge(Call other){
for (Enumeration e = other.connections.elements();e.hasMoreElements();){Connection conn = (Connection)e.nextElement();other.connections.removeElement(conn);connections.addElement(conn);
}}
}
abstract class Connection {public static final int PENDING = 0;public static final int COMPLETE = 1;public static final int DROPPED = 2;Customer caller, receiver;private int state = PENDING;Connection(Customer a, Customer b) {
this .caller = a;this .receiver = b;
}public int getState(){
return state;}public Customer getCaller() { return caller; }public Customer getReceiver() { return receiver; }void complete() {
state = COMPLETE;System.out.println("connection completed");
}void drop() {
state = DROPPED;System.out.println("connection dropped");
}public boolean connects(Customer c){
return (caller == c || receiver == c);}
}class Local extends Connection {
Local(Customer a, Customer b) {super (a, b);System.out.println("[new local " +"connection from " + a + " to " + b + "]");
}}class LongDistance extends Connection {
LongDistance(Customer a, Customer b) {super (a, b);System.out.println("[new long distance " + "connection from " + a + " to " + b + "]");
}}class Timer {
long startTime, stopTime;public void start() {
startTime = System.currentTimeMillis();stopTime = startTime;
}public void stop() {
stopTime = System.currentTimeMillis();}public long getTime() {
return stopTime - startTime;}
}
Figura 4.13: Código das classes-base da aplicação de telefonia.
quantidade de interações com aspectos), é interessante que se comece por eles, cobrindo as partes
dos métodos e adendos relacionadas com os aspectos. Neste caso, os módulos interessantes são: a
classeCall e o aspectoTiming , pois eles são afetados por adendos. Na Figura4.15são mos-
trados os elementos requeridos para o critério todos-nós-transversais (All-Nodes-c), para todas as
unidades sendo testadas. A classeCall aparece com cinco requisitos de teste, o que quer dizer
que ela é afetada por adendos em cinco pontos. O aspectoTiming aparece com dois elementos
requeridos. É interessante notar que a informação dada por esse critério é muito valiosa, não só
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 84
public aspect Timing {
public long Customer.totalConnectTime = 0;
public long getTotalConnectTime(Customer cust) {return cust.totalConnectTime;
}private Timer Connection.timer = new Timer();public Timer getTimer(Connection conn)
{ return conn.timer; }
after (Connection c): target (c) && call ( voidConnection.complete()) {
getTimer(c).start();}
pointcut endTiming(Connection c): target (c) &&call ( void Connection.drop());
after (Connection c): endTiming(c) {getTimer(c).stop();c.getCaller().totalConnectTime +=
getTimer(c).getTime();c.getReceiver().totalConnectTime +=
getTimer(c).getTime();}
}
public aspect TimerLog {
after (Timer t): target (t) && call (* Timer.start()) {System.err.println("Timer started: " + t.startTime);
}
after (Timer t): target (t) && call (* Timer.stop()) {System.err.println("Timer stopped: " + t.stopTime);
}}
public aspect Billing {declare precedence: Billing, Timing;
public static final long LOCAL_RATE = 3;public static final long LONG_DISTANCE_RATE = 10;
public Customer Connection.payer;public Customer getPayer(Connection conn)
{ return conn.payer; }
after (Customer cust) returning (Connection conn):args (cust, ..) && call (Connection+. new(..)) {conn.payer = cust;
}
public abstract long Connection.callRate();
public long LongDistance.callRate(){ return LONG_DISTANCE_RATE; }
public long Local.callRate(){ return LOCAL_RATE; }
after (Connection conn): Timing.endTiming(conn) {long time =
Timing. aspectOf ().getTimer(conn).getTime();long rate = conn.callRate();long cost = rate * time;getPayer(conn).addCharge(cost);
}
public long Customer.totalCharge = 0;public long getTotalCharge(Customer cust)
{ return cust.totalCharge; }
public void Customer.addCharge( long charge){totalCharge += charge;
}}
Figura 4.14: Código dos aspectos da aplicação de telefonia.
para o testador mas também para o desenvolvedor. Com os elementos requeridos, o testador/desen-
volvedor sabe exatamente em quais classes/aspectos os adendos estão definindo comportamento e
também a quantidade de adendos afetando as classes/aspectos.
Para testar a classeCall a partir do critério todos-nós-transversais, é necessário saber quais
métodos estão sendo afetados pelos adendos. Para isso, o testador pode visualizar tanto o byte-
code quanto o código fonte e observar onde a ferramenta indica que estão os requisitos de teste.
Na Figura4.16é mostrada a tela da ferramenta JaBUTi/AJ com o código fonte da classeCall ,
com os requisitos de teste referentes ao critério todos-nós-transversais destacados no código. Na
verdade, percebe-se que, nesse caso, os elementos requeridos acabam sendo os pontos de junção
afetados pelos adendos. Por exemplo, no caso do construtor deCall , deve haver algum adendo
que define comportamento nas chamadas aos construtores deLongDistance e Local . Isso
pode ser conferido visualizando o grafoAODU do construtor deCall (Figura4.17(a)). Os nós
transversais33 e 68 indicam que um adendo posterior do aspectoBilling afeta aqueles pontos
(esse adendo é responsável por atribuir a fatura pela ligação ao cliente chamador). A partir daí o
critério todos-nós-transversais requer que esses nós sejam cobertos, ou seja, que existam casos de
teste que os exercitem.
Analisando a lógica do construtor deCall , para cobrir os nós transversais são necessários
dois casos de teste: um que realize uma chamada local e outro que realize uma chamada de longa
distância. Como o adendo que atribui a fatura ao cliente chamador deve distinguir entre chamadas
locais e de longa distância, aqui o testador já pode ter evidências de que o aspecto está implemen-
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 85
Figura 4.15: Elementos requeridos para o critério todos-nós-transversais para a aplicação detelefonia.
tado corretamente, já que o adendo afeta o ponto onde uma chamada local é criada e também o
ponto onde uma chamada de longa distância é criada. A partir dos dois casos de teste, que testarão
se o método realmente cria uma chamada local quando os clientes possuem o mesmo código de
área e uma chamada de longa distância quando os clientes possuem códigos de área diferentes, é
possível cobrir os dois nós transversais referentes às execuções do adendo para os dois casos.
Quanto ao métodohangup , também afetado por adendos, a lógica é a seguinte: quando a
ligação é concluída, é necessário terminar todas as conexões relacionadas a ela, o que é feito por
meio de um laço. Como o adendo do aspecto de temporização deve parar o temporizador toda
vez que uma conexão é terminada, ele é executado depois de cada chamada ao métododrop .
Já no caso do aspecto de faturamento, este também deve afetar o mesmo ponto, pois cada vez
que uma conexão é terminada, também é necessário calcular o custo da chamada e atribuí-lo à
fatura do cliente chamador. Na figura4.17(b)é mostrado o grafoAODU do métodohangup ,
com as execuções dos adendos representadas pelos nós transversais11 e32. Para cobrir esses nós
transversais é necessário um caso de teste que inicie uma chamada e a termine. Quando o método
hangup é chamado, os adendos são executados no término de cada conexão relacionada com a
chamada.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 86
Figura 4.16: Código-fonte da classeCall com os elementos requeridos do critériotodos-nós-transversais em destaque.
Aqui o testador também pode ter evidências de que os aspectos estão implementados corre-
tamente, e além disso que estão interagindo de forma correta, já que o aspecto de faturamento é
acionado após o aspecto de temporização. Note-se que a precedência dos aspectos é importante
nesse caso, pois se o aspecto de faturamento agisse antes do aspecto de temporização, o sistema
falharia. Esse tipo de defeito faz parte do modelo de defeitos definido porAlexander et al. (2004),
discutido na Seção2.3.5. É importante notar que esse caso evidencia a eficiência da abordagem
proposta neste trabalho, no que diz respeito a auxiliar na descoberta desse tipo de defeito.
O métodopickup , que é utilizado para simular o momento em que o cliente tira o telefone
do ganho e completa a ligação, é afetado pelo adendo posterior do aspectoTiming . Isso é feito
para que o aspecto inicie o temporizador toda vez que uma conexão é completada, por meio do
adendo. Para cobrir o nó transversal é necessário apenas um caso de teste que execute o método,
completando uma ligação e verificando que o temporizador é iniciado.
No caso do aspectoTiming , os dois adendos posteriores que iniciam e terminam o tempo-
rizador são afetados pelos adendos posteriores do aspectoTimerLog , para que seja registrado
o tempo de início e parada do temporizador. Nesses pontos podem ser observadas interações
aspecto-aspecto, pois o aspectoTimerLog afeta o aspectoTiming . Para cobrir os nós trans-
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 87
(a) GrafoAODU do construtor da classeCall . (b) GrafoAODU do métodohangup .
Figura 4.17: GrafosAODU de construtor e método da classeCall .
versais é necessário um caso de teste que inicie e termine uma ligação, sensibilizando os adendos
do aspectoTiming que iniciam e terminam o temporizador, e por conseqüência, sensibilizando
também o aspectoTimerLog .
Na Figura4.18 é mostrada a classeTelecomTestCase , escrita utilizando o JUnit e que
contém os casos de teste utilizados no teste da aplicação de telefonia. Os primeiros casos de teste
referem-se aos casos de teste comentados anteriormente, utilizados para cobrir os nós transversais
contidos na aplicação. Os outros casos de teste cobrem o restante dos nós. Em razão da simplici-
dade da aplicação, esse conjunto de casos de teste também é adequado para os outros dois critérios
OA definidos.
public class TelecomTestCase extends TestCase {...public void testCoverCCNodesCallConstrLongDistance() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 500);Call c1 = new Call(jim, mik);
}public void testCoverCCNodesCallConstrLocal() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 650);Call c1 = new Call(jim, mik);
}public void testCoverCCNodesCallHangup() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 650);Call c1 = new Call(jim, mik); c1.hangup(jim);
}public void testCoverCCNodesCallPickup() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 650);Call c1 = new Call(jim, mik); c1.pickup();
}public void testCoverNodesTimingGetTotalTime() {
Customer jim = new Customer("Jim", 650);
Customer mik = new Customer("Mik", 650);Call c1 = new Call(jim, mik);c1.pickup();wait(2.0);c1.hangup(jim);Timing t = Timing. aspectOf ();System.out.println(t.getTotalConnectTime(jim));
}public void testCoverNodesBillingGetPayer() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 650);Connection c = new Local(jim, mik);Billing b = Billing. aspectOf ();Customer payer = b.getPayer(c);System.out.println(payer);
}public void testCoverNodesBillingGetRateLocal() {
Customer jim = new Customer("Jim", 650);Customer mik = new Customer("Mik", 650);Connection c = new Local(jim, mik);long l = c.callRate();System.out.println(l);
}}
Figura 4.18: Classe contendo um conjunto de casos de teste para o teste da aplicação de telefonia.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 88
4.4 Exemplo de Fluxo de Dados
Os dois exemplos apresentados anteriormente não exploram os critérios de fluxo de dados de-
finidos neste trabalho, pela simplicidade e tamanho das aplicações. O seguinte exemplo demonstra
o uso do critério Todos-UsosC . A aplicação não tem sentido, apenas ilustra interações de fluxo de
dados que podem ocorrer em uma unidade afetada por aspectos.
Na Figura4.19é mostrado o código do exemplo. A classeAClass é o alvo da atividade de
teste, ela possui um método chamadoaMethod que recebe quatro parâmetros: uma cadeia de
caracteres e três inteiros. O método cria um objeto da classeASecClass e, em alguns pontos,
chama o métodoaSecMethod , que recebe um parâmetro do tipo cadeia de caracteres. O as-
pectoAnAspect possui um adendo posterior que define comportamento toda vez que o método
aSecMethod é chamado e, além disso, obtém o parâmetro cadeia de caracteres que é passado
para esse método. O adendo apenas imprime uma mensagem, mostrando o valor da cadeia de
caracteres que foi passada como parâmetro para o método. Na Figura4.20é mostrado o bytecode
do métodoaMethod .
15 public class AClass {1617 public void aMethod(String s, int x, int y, int z) {1819 String str1 = new String("str1");20 String str2 = new String("str2");21 String str3 = new String("str3");2223 ASecClass asc = new ASecClass();2425 if (x > 0) {26 System.out.println("x > 0");27 s = str1;28 }29 if (y > 0) {30 System.out.println("y > 0");31 asc.aSecMethod(s);32 }33 if (y >= 1)34 if (z > 10)35 s = str2;36 else37 asc.aSecMethod(s);38 for ( int i = 0; i < x; i++) {39 if (z > 1) {40 asc.aSecMethod(s);41 z--;42 } else {43 s = str3;44 }45 }46 asc.aSecMethod(s);47 }48 }4950 public class ASecClass {5152 public int a = 0;53 public int b = 0;5455 public void aSecMethod(String s) {56 System.out.println("A Second Method... " + s);57 }58 }5960 public aspect AnAspect {6162 after (String s) returning () :63 call (* aSecMethod(String))64 && args (s){65 System.out.println("s = " + s);66 }67 }
Figura 4.19: Código fonte da aplicação para ilustração do critério de fluxo de dados.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 89
0 new #20 <Class java.lang.String>3 dup4 ldc #22 <String "str1">6 invokespecial #25 <Method String(java.lang.String)>9 astore 511 new #20 <Class java.lang.String>14 dup15 ldc #27 <String "str2">17 invokespecial #25 <Method String(java.lang.String)>20 astore 622 new #20 <Class java.lang.String>25 dup26 ldc #29 <String "str3">28 invokespecial #25 <Method String(java.lang.String)>31 astore 733 new #31 <Class components.ASecClass>36 dup37 invokespecial #32 <Method ASecClass()>40 astore 842 iload_243 ifle 5746 getstatic #38 <Field java.io.PrintStream
java.lang.System.out>49 ldc #40 <String "x > 0">51 invokevirtual #45 <Method void println(java.lang.String)>54 aload 556 astore_157 iload_358 ifle 8861 getstatic #38 <Field java.io.PrintStream
java.lang.System.out>64 ldc #47 <String "y > 0">66 invokevirtual #45 <Method void println(java.lang.String)>69 aload 871 aload_172 astore 1074 aload 1076 invokevirtual #50 <Method void
aSecMethod(java.lang.String)>79 invokestatic #98 <Method component.AnAspect
component.AnAspect.aspectOf()>82 aload 1084 invokevirtual #101 <Method void
ajc$afterReturning$component_AnAspect$1$7d6a5258(java.lang.String)>
87 nop88 iload_389 iconst_190 if_icmplt 12593 iload 495 bipush 1097 if_icmple 106100 aload 6102 astore_1103 goto 125
106 aload 8108 aload_1109 astore 11111 aload 11113 invokevirtual #50 <Method void
aSecMethod(java.lang.String)>116 invokestatic #98 <Method component.AnAspect
component.AnAspect.aspectOf()>119 aload 11121 invokevirtual #101 <Method void
ajc$afterReturning$component_AnAspect$1$7d6a5258(java.lang.String)>
124 nop125 iconst_0126 istore 9128 goto 168131 iload 4133 iconst_1134 if_icmple 162137 aload 8139 aload_1140 astore 12142 aload 12144 invokevirtual #50 <Method void
aSecMethod(java.lang.String)>147 invokestatic #98 <Method component.AnAspect
component.AnAspect.aspectOf()>150 aload 12152 invokevirtual #101 <Method void
ajc$afterReturning$component_AnAspect$1$7d6a5258(java.lang.String)>
155 nop156 iinc 4 -1159 goto 165162 aload 7164 astore_1165 iinc 9 1168 iload 9170 iload_2171 if_icmplt 131174 aload 8176 aload_1177 astore 13179 aload 13181 invokevirtual #50 <Method void
aSecMethod(java.lang.String)>184 invokestatic #98 <Method component.AnAspect
component.AnAspect.aspectOf()>187 aload 13189 invokevirtual #101 <Method void
ajc$afterReturning$component_AnAspect$1$7d6a5258(java.lang.String)>
192 nop193 return
Figura 4.20: Bytecode do métodoaMethod da classeAClass .
Para o teste dessa aplicação, são abordados apenas os critérios orientados a aspectos definidos
neste trabalho. Inicialmente poder-se-ia analisar o critério mais fraco, passando para os outros
dois mais fortes, em uma atividade de teste incremental. Na Figura4.21 é mostrado o sumário
dos requisitos de teste para os critérios Todos-NósC , Todos-ArestasC e Todos-UsosC , para o mé-
todo aMethod da classeAClass . É importante notar que a maioria dos requisitos de teste do
critério Todos-UsosC referem-se à variávels (identificada porL@1 na ferramenta), pois o adendo
do aspectoAnAspect utiliza essa variável de contexto. Já os requisitos do critério Todos-NósC
indicam o número de pontos onde os adendos do aspectoAnAspect definem comportamento na
classeAClass . Ao utilizar a ferramenta para observar o código da classe pode-se notar esses
pontos, que são as chamadas ao métodoaSecMethod da classeASecClass (Figura4.22). O
grafoAODU do método é mostrado na Figura4.23.
A partir dos requisitos do critério Todos-NósC , observando o código e os pesos atribuídos pela
ferramenta, poderia ser criado um caso de teste para cobrir todos os nós transversais, analisando
as condições contidas no método. Um caso de teste com entradas nas quaisx > 0, y > 0 e
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 90
(a) Todos-NósC (b) Todas-ArestasC
(c) Todos-UsosC
Figura 4.21: Requisitos de teste dos critérios Todos-NósC , Todos-ArestasC e Todos-UsosC , parao métodoaMethod da classeAClass .
z > 1, faz com que todos os nós transversais sejam cobertos. Um desses casos de teste poderia
ser (s1, 1, 1, 9), ondes1 é uma cadeia de caracteres qualquer. Observando a ferramenta após a
execução desse caso de teste, percebe-se que o mesmo caso de teste também cobre todas as arestas
transversais e, portanto, um conjunto de teste com apenas esse caso de teste é adequado para ambos
os critérios Todos-NósC e Todas-ArestasC .
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 91
Figura 4.22: Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, com baseno critério Todos-NósC .
Passando para o critério de fluxo de dados Todos-UsosC , analisando a cobertura a partir da
execução do caso de teste comentado anteriormente, nota-se que 9 dos 17 requisitos de teste (52%)
foram cobertos. A partir daí, os requisitos que sobraram podem ser analisados um a um, para
auxiliar na construção de casos de teste para completar a cobertura.
Analisando a Figura4.24, que mostra o código fonte3 do métodoaMethod com base no
critério Todos-UsosC , podem ser visualizados os pontos onde existem definições de variáveis –
partes vermelhas – que são subseqüentemente usadas em nós transversais (e portanto formam pa-
res Def-Uso que são requisitos do critério em questão). É importante notar que os usos não são
explicitamente aparentes no código fonte, já que são caracterizados a partir do bytecode e nas
partes adicionadas pelo aspecto. Porém, a ferramenta identifica os pontos dos usos geralmente
como sendo os pontos de junção (como acontece nesse exemplo, destacando a chamada ao mé-
todoaSecMethod ). As partes em branco no código fonte indicam definições para as quais todos
os subseqüentes usos foram cobertos pelos casos de teste ativos. Clicando com o botão direito do
mouse sobre um dos pontos onde as variáveis são definidas, o testador pode escolher a variável que
3Para a visualização do código com melhor definição pode ser utilizada a Figura4.20, que utiliza a mesma nume-ração de linhas mostrada na ferramenta.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 92
Figura 4.23:AODU do métodoaMethod da classeAClass .
deseja analisar, e a partir daí a ferramenta mostra os pontos que tal definição pode alcançar, por
meio de um caminho livre de definição. Por exemplo, se o testador clicar sobre a parte vermelha
indicada na figura na linha 19 do código fonte, e escolher a variávels – que é a cadeia de caracteres
passada para o métodoaSecMethod –, a ferramenta indicará todos os usos em nós transversais
que ainda não foram cobertos, utilizando a cor vermelha; e também os que foram cobertos, utili-
zando a cor branca. Na Figura4.25é mostrada a tela da ferramenta, depois do testador completar
esses passos. A mesma funcionalidade pode ser explorada a partir do grafo Def-Uso, clicando nos
nós nos quais existe definição de alguma variável.
A partir dos pares não cobertos, podem ser construídos casos de teste para percorrer caminhos
nos quais as definições alcançam os usos sem passarem por nenhuma outra definição da mesma
variável. Por exemplo, para cobrir o par(s, 0, 61) representado na Figura4.25pelas linhas19–31
do código fonte, é necessário um caso de teste que faça com que a definição des no início do
método alcance o uso na linha31, sem passar pela definição que ocorre na linha27. Para que o
caminho percorrido pelo caso de teste não passe pela linha27, é necessário que a entradax seja
menor do que zero. Sendo assim, um caso de teste formado pela entrada(s1,−1, 1, 9), no quals1
é qualquer cadeia de caracteres, e capaz de cobrir esse par Def-Uso. Além disso, executando esse
mesmo caso de teste e analisando a cobertura na ferramenta, percebe-se que o par representado
pelas linhas19–37 também é coberto, já que a definição no início do método também alcança a
linha 37. Assim, para a definição des no nó inicial doAODU do método, resta apenas o par
representado pelas linhas19–40. Analisando a lógica do método, para cobrir esse par é necessário
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 93
Figura 4.24: Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, com baseno critério Todos-UsosC .
que a entradax seja maior do que zero, para que o laço que inicia na linha38 seja executado pelo
menos uma vez, alcançando a linha40. Porém, sex > 0, s é redefinida na linha27 e, portanto, a
definição do início do nó não alcança o uso em40. Assim, tem-se que o par(s, 0, 137) representado
no código fonte pelas linhas19–40 não é executável. Os requisitos não-executáveis podem ser
marcados como tais na ferramenta JaBUTi/AJ, para que a cobertura deles seja desprezada.
Depois da construção desses outros dois casos de teste, ainda resta cobrir os usos da variável
s cujas definições encontram-se nos nós100 e 162, representados no código fonte pelas linhas35
e 43. Os pares Def-Uso cujas definições encontram-se no nó100 são(s, 100, 137) e (s, 100, 174).
O primeiro, representado no código fonte pelas linhas35–40, pode ser coberto com um caso de
teste no qual as entradas respeitem as condições:x > 0, para que o laço seja executado pelo
menos uma vez,y ≥ 1 para que a condição em33 seja satisfeita ez > 10, para que a condição
em 35 seja satisfeita. Já o segundo caso de teste pode ser coberto quando as condiçõesx < 0,
y ≥ 1 e z > 10 sejam satisfeitas. A partir daí podem ser construídos dois casos de teste, com as
entradas(s1, 1, 1, 11) e (s1,−1, 1, 11). Após a construção desses casos de teste, resta a cobertura
dos pares(s, 162, 137) e (s, 162, 174) representados pelas linhas43–40 e 43–46, respectivamente.
O primeiro par Def-Uso não é executável, pois as condiçõesz > 1 ez ≤ 1 deveriam ser satisfeitas
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 94
Figura 4.25: Código fonte da aplicação exemplo mostrado na ferramenta JaBUTi/AJ, após aseleção da definição da variávels no início do método.
com uma iteração do laço, e comoz decresce a cada iteração, essas condições nunca poderiam
ser satisfeitas de maneira que a definição em46 alcance o uso em43. Já o par representado pelas
linhas43–46 pode ser coberto com um caso de teste no qual as entradas satisfaçam as seguintes
condições:x > 0 e z ≤ 1. A entrada(s1, 1, 1,−1) satisfaz essas condições. Na Figura4.26são
mostrados todos os casos de teste construídos utilizando o framework JUnit.
public void testCoverCCNodes1() {String s1 = new String("");AClass ac = new AClass();ac.aMethod(s1, 1, 1, 9);
}
public void testCoverCCUses19_31() {String s1 = new String("");AClass ac = new AClass();ac.aMethod(s1, -1, 1, 9);
}
public void testCoverCCUses35_40() {String s1 = new String("");AClass ac = new AClass();ac.aMethod(s1, 1, 1, 11);
}
public void testCoverCCUses35_46() {String s1 = new String("");AClass ac = new AClass();ac.aMethod(s1, -1, 1, 11);
}
public void testCoverCCUses43_46() {String s1 = new String("");AClass ac = new AClass();ac.aMethod(s1, 1, 1, -1);
}
Figura 4.26: Casos de teste para a aplicação exemplo.
CAPÍTULO 4. TESTE DE UNIDADE: AUTOMATIZAÇÃO E EXEMPLOS 95
4.5 Considerações Finais
Neste capítulo foi apresentada uma implementação da abordagem de teste estrutural de unidade
proposta neste trabalho para o teste de programas OA escritos na linguagem AspectJ. A ferramenta
implementada chama-se JaBUTi/AJ por estender a ferramenta JaBUTi, resultado do trabalho de
Vincenzi (2004) para o teste estrutural de programas Java. O capítulo deu enfoque às mudanças da
ferramenta, deixando de lado detalhes já abordados no trabalho deVincenzi (2004). Em particular,
os critérios orientados a aspectos definidos na Seção3.3foram mais enfatizados.
É importante ressaltar também que a ferramenta irá auxiliar em experimentos futuros, para
avaliar a habilidade dos critérios em encontrar erros específicos de programas OA. Além disso, de
maneira geral, uma atividade de teste estrutural de programas OA seria impraticável, se realizada
manualmente, o que mostra a importância da ferramenta.
No próximo capítulo são apresentadas as conclusões finais do trabalho, apresentando as con-
tribuições e os trabalhos futuros a serem realizados.
CAPÍTULO
5Conclusão
5.1 Considerações Finais
Neste trabalho foi investigada uma abordagem de teste estrutural baseada em fluxo de controle
e no fluxo de dados para programas orientados a aspectos, adaptando as abordagens existentes para
programas procedimentais e orientados a objetos. O grafo de fluxo foi estendido para contemplar
as peculiaridades existentes no fluxo de controle das unidades dos programas OA em que aspectos
definem comportamento por meio de adendos. Grafo de Fluxo de Controle Orientado a Aspectos
(AOCFG) foi o nome dado ao grafo de fluxo de controle estendido, e grafo Def-Uso Orientado
a Aspectos (AODU) foi o nome dado ao grafo de fluxo de dados estendido. Com base nesses
modelos foram definidos critérios de teste estruturais, entre eles, três específicos para programas
OA: todos-nós-transversais, todas-arestas-transversais e todos-usos-transversais.
Para o teste de integração baseado em fluxo de dados foi concebido o Grafo Def-Uso Método-
Adendo (MADU), que consiste noAODU do método juntamente com osAODUs dos adendos
que definem comportamento no método. A partir desse grafo foi concebido o conceito de pares
Def-Uso aspectuais, com base nos diferentes escopos representados no grafo – escopos de aden-
dos e escopo do próprio método. Critérios de teste baseados no fluxo de dados foram definidos
tomando como base os pares Def-Uso aspectuais.
A abordagem de teste estrutural de unidade foi implementada em um protótipo de ferramenta
estendido da ferramenta JaBUTi, resultado do trabalho deVincenzi (2004). Algumas restrições
podem ser notadas na implementação:
• A ferramenta é restrita à linguagem AspectJ e às versões mais recentes;
96
CAPÍTULO 5. CONCLUSÃO 97
• Apenas os aspectos do tiposingleton, ou seja, que possuem somente uma instância durante
toda a execução do programa, foram investigados. Porém, os grafos também podem ser
derivados a partir de aplicações que contêm aspectos do tipoper*, mas essas ocorrências
devem ser investigadas com mais profundidade.
5.2 Contribuições
No que diz respeito ao teste de unidade, as contribuições de ordem teórica do trabalho referem-
se a concepção de uma abordagem de teste de unidade, a partir da adaptação dos modelos de
fluxo de controle e de dados e da definição de critérios estruturais baseados nesses modelos. A
contribuição de ordem prática é a extensão da ferramenta JaBUTi em um protótipo de ferramenta
chamado JaBUTi/AJ, que implementa a abordagem de teste de unidade proposta.
No que diz respeito ao teste de integração, algumas considerações teóricas foram feitas, com a
definição do grafoMADU para representar o fluxo de controle e de dados das unidades integradas
com os adendos que as afetam, e com a definição de cinco critérios baseados nesse modelo.
Em suma, este trabalho contribui com uma adaptação da técnica de teste estrutural para pos-
sibilitar o seu uso no teste de programas OA tanto em unidade quanto em integração, porém com
ênfase no teste de unidade.
5.3 Pesquisas Futuras
Outros critérios mais fortes, ou que explorem diferentes propriedades, podem ser investigados
no contexto da POA, tanto para o teste de unidade quanto para o teste de integração. Por exemplo,
os critérios Potenciais-Uso deMaldonado (1991) poderiam ser explorados no contexto de progra-
mas OA. Além disso, pouco foi feito na direção da avaliação dos critérios propostos, tanto no que
se refere ao custo de aplicação quanto ao que se refere à habilidade em encontrar erros. O tra-
balho deAlexander et al. (2004) pode ser explorado nesse sentido, investigando os tipos de erros
específicos de programas OA que podem ser encontrados com o auxílio dos critérios propostos, e
também com a ajuda de estudos de caso maiores e/ou mais complexos e de experimentos.
O teste de integração foi investigado de maneira superficial e pode ser aprofundado em outro
trabalho. Percebe-se um nicho a ser explorado nesse sentido, principalmente no que diz respeito
ao teste baseado em fluxo de dados, explorando as outras fases de teste definidas neste trabalho e
também a implementação de ferramentas automatizadas.
Referências Bibliográficas
ADRION, W. R.; BRANSTAD, M. A.; CHERNIAVSKY, J. C. Validation, Verification, and Testing
of Computer Software.ACM Comput. Surv., v. 14, n. 2, p. 159–192, 1982.
AGRAWAL , H. Dominators, super blocks, and program coverage. In:Proceedings of the 21st
ACM SIGPLAN-SIGACT symposium on Principles of programming languages, ACM Press, p.
25–34, 1994.
AGRAWAL , H.; ALBERI, J. L.; HORGAN, J. R.; LI , J. J.; LONDON, S.; WONG, W. E.; GHOSH,
S.; WILDE , N. Mining system tests to aid software maintenance.Computer, v. 31, n. 7,
p. 64–73, 1998.
ALEXANDER, R. Aspect-Oriented Programming: the Real Costs?IEEE Software, v. 20, n. 6,
p. 90–93, 2003.
ALEXANDER, R. T.; BIEMAN , J. M. Challenges with aspect-oriented technology. In:Procee-
dings of the ICSE Workshop on Software Quality, Orlando, Florida, USA, 2002a.
ALEXANDER, R. T.; BIEMAN , J. M. Will aspect-oriented programming improve software qua-
lity? In: Proceedings of the 13th International Conference on Software Reliability Engineering
(ISSRE 2002), Annapolis, Maryland, 2002b.
ALEXANDER, R. T.; BIEMAN , J. M.; ANDREWS, A. A. Towards the Systematic Testing of
Aspect-Oriented Programs. Relatório Técnico, Colorado State University, Fort Collins, CO,
2004.
ASPECTJ TEAM Aspectj programming guide. [On-line].
Disponível em http://dev.eclipse.org/viewcvs/indextech.cgi/
~checkout~/aspectj-home/doc/progguide/index.html (Acessado em
21/09/2004)
98
REFERÊNCIAS BIBLIOGRÁFICAS 99
BANIASSAD, E.; CLARKE , S. Theme: An Approach for Aspect-Oriented Analysis and Design.
In: Proceedings of the 26th International Conference on Software Engineering, IEEE Computer
Society, p. 158–167, 2004.
BECK, K.; GAMMA , E. JUnit cookbook. [On-line].
Disponível emwww.junit.org (Acessado em 27/11/2003)
BECKER, U. D2AL: A design-based aspect language for distribution control. In:Proceedings of
the 12th International Workshop on Aspect-Oriented Programming at ECOOP, Brussels, Bel-
gium, 1998.
BINDER, R. V. Testing Object-Oriented Systems: Models, Patterns, and Tools. 1 ed. Massa-
chusetts: Addison Wesley, 1191 p., 1999.
BODKIN , R. Commercialization of AOSD: The Road Ahead. In:Proceedings of the Workshop
on Commercialization of AOSD Technology, at the 2nd International Conference on Aspect-
Oriented Software Development (AOSD), Boston, Massachussets, USA, 2003.
CAPRETZ, L. F. A brief history of the object-oriented approach.SIGSOFT Softw. Eng. Notes,
v. 28, n. 2, p. 6, 2003.
CLARKE , S.; WALKER , R. J. Towards a standard design language for AOSD. In:Proceedings
of the 1st international conference on Aspect-oriented software development, ACM Press, p.
113–119, 2002.
DIJKSTRA, E. W. A Discipline of Programming. 1 ed. New Jersey – USA: Prentice-Hall, 217
p., 1976.
ELRAD , T.; FILMAN , R. E.; BADER, A. Aspect-oriented programming: Introduction.Commun.
ACM, v. 44, n. 10, p. 29–32, 2001a.
ELRAD , T.; KICZALES, G.; AKSIT, M.; L IEBERHER, K.; OSSHER, H. Discussing Aspects of
AOP. Communications of the ACM, v. 44, n. 10, p. 33–38, 2001b.
FONTOURA, M. The UML Profile for Frameworks Architectures. 1 ed. Addison-Wesley, 352
p., 2002.
FRANKL , P. G.; WEYUKER, E. J. An Applicable Family of Data Flow Testing Criteria. In:
IEEE Transactions on Software Engineering, p. 1483–1498, 1988.
FRANKL , P. G.; WEYUKER, E. J. Testing Software to Detect and Reduce Risk.Journal of
Systems and Software, v. 53, n. 3, p. 275–286, 2000.
HARROLD, M. J. Testing: a roadmap. In:Proceedings of the conference on The future of
Software engineering, ACM Press, p. 61–72, 2000.
REFERÊNCIAS BIBLIOGRÁFICAS 100
HARROLD, M. J.; ROTHERMEL, G. Performing Dataflow Testing on Classes. In:Proceedings of
the ACM SIGSOFT ’94 Symposium on the Foundations of Software Engineering, New Orleans,
LA, USA: ACM, p. 154–163, 1994.
HARROLD, M. J.; SOFFA, M. L. Interprocedual data flow testing. In:Proceedings of the ACM
SIGSOFT ’89 third symposium on Software testing, analysis, and verification, ACM Press, p.
158–167, 1989.
HILSDALE , E.; HUGUNIN, J. Advice Weaving in AspectJ. In:Proceedings of the 4th Inter-
national Conference on Aspect-oriented software development 2004, Lancaster, UK, p. 26–35,
2004.
HOWDEN, W. E. Functional Program Testing and Analisys. New York: McGraw-Hill, 1987.
HUANG, J. Experience Using AspectJ to Implement Cord. In:Proc. OOPSLA 2000 Workshop
on Advanced Separation of Concerns in Object-Oriented Systems, 2000.
HUTCHINS, M.; FOSTER, H.; GORADIA , T.; OSTRAND, T. Experiments of the effectiveness of
dataflow- and controlflow-based test adequacy criteria. In:Proceedings of the 16th internatio-
nal conference on Software engineering, IEEE Computer Society Press, p. 191–200, 1994.
KARR, D. A.; RODRIGUES, C.; LOYALL , J. P.; SCHANTZ, R. E.; KRISHNAMURTHY, Y.; PYA -
RALI , I.; SCHMIDT, D. C. Application of the QuO Quality-of-Service Framework to a Distri-
buted Video Application. In:Proceedings of the 3rd International Symposium on Distributed
Objects and Applications, 2001.
KERSTEN, M. AspectJ: The Language and Development Tools. [On-line].
Disponível em http://www.parc.com/research/csl/projects/aspectj/
downloads/OOPSLA2002-demo.ppt (Acessado em 06/12/2003)
K ICZALES, G.; HILSDALE , E.; HUGUNIN, J.; KERSTEN, M.; PALM , J.; GRISWOLD, W. G.
An Overview of AspectJ.Lecture Notes in Computer Science, v. 2072, p. 327–355, 2001.
K ICZALES, G.; LAMPING, J.; MENHDHEKAR, A.; MAEDA , C.; LOPES, C.; LOINGTIER, J.-
M.; I RWIN, J. Aspect-Oriented Programming. In: AKSIT, M.; MATSUOKA, S., eds.Procee-
dings European Conference on Object-Oriented Programming, v. 1241, Berlin, Heidelberg, and
New York: Springer-Verlag, p. 220–242, 1997.
LADDAD , R. AspectJ in Action: Practical Aspect-Oriented Programming. 1 ed. Greenwich,
Connecticut – USA: Manning Publications Company, 512 p., 2003.
LEMOS, O. A. L.; MALDONADO , J. C.; MASIERO, P. C. A Contribution to Data Flow Based
Integration Testing of Aspect-Oriented Programs, a ser submetido, 2004a.
REFERÊNCIAS BIBLIOGRÁFICAS 101
LEMOS, O. A. L.; MALDONADO , J. C.; MASIERO, P. C. Data-flow integration testing criteria
for aspect-oriented programs. In:Anais do 1o Workshop de Desenvolvimento de Software
Orientado a Aspectos (WASP’2004) – realizado em conjunto com o SBES’2004, Brasília, DF,
Brasil, 2004b.
LEMOS, O. A. L.; VINCENZI, A. M. R.; MALDONADO , J. C.; MASIERO, P. C. Teste de uni-
dade de programas orientados a aspectos. In:Anais do 18o Simpósio Brasileiro de Engenharia
de Software (indicado para Best Paper), Brasília, DF, Brasil, p. 55–70, 2004c.
LEMOS, O. A. L.; VINCENZI, A. M. R.; MALDONADO , J. C.; MASIERO, P. C. Unit Testing
of Aspect-Oriented Programs: the Impact of the Underlying Control and Data Flow Models, a
ser submetido, 2004d.
LOPES, C. V. D: A Language Framework for Distributed Programming. Tese de Doutoramento,
College of Computer Science, Northeastern University, Boston, MA, 1997.
MALDONADO , J. C. Critérios potenciais usos: Uma contribuição ao teste estrutural de software.
Tese de Doutoramento, DCA/FEE/UNICAMP, Campinas, SP, 1991.
MALDONADO , J. C.; BARBOSA, E. F.; VINCENZI, A. M. R.; DELAMARO , M. E.; DO ROCIO
SENGER DESOUZA, S.; JINO, M. Teste de Software: Teoria e prática. In:Minicurso – XVII
Simpósio Brasileiro de Engenharia de Software (SBES 2003), Manaus – AM, 2003.
MALDONADO , J. C.; FABBRI , S. C. P. F. Teste de Software. In:DA ROCHA, A. R. C.;
MALDONADO , J. C.; WEBER, K. C., eds.Qualidade de Software – Teoria e Prática, 1 ed, São
Paulo: Prentice Hall, p. 73–84, 2001.
MURPHY, G. C.; WALKER , R. J.; BANIASSAD, E. L. A.; ROBILLARD , M. P.; LAI , A.; KERS-
TEN, M. A. K. Does aspect-oriented programming work?Commun. ACM, v. 44, n. 10,
p. 75–77, 2001.
MYERS, G. J. The Art of Software Testing. 1 ed. New York: Wiley, 177 p., 1979.
NTAFOS, S. C. A Comparison of Some Structural Testing Strategies.IEEE Transactions on
Software Engineering, v. 14, n. 6, p. 868–874, 1988.
PANDE, H.; LANDI , W.; RYDER, B. Interprocedural def-use associations in C programs.IEEE
Transactions on Software Engineering, v. 20, n. 5, p. 385–403, 1994.
PRESSMAN, R. S. Engenharia de Software. 5 ed. Rio de Janeiro: McGraw-Hill, 843 p., 2002.
RAPPS, S.; WEYUKER, E. J. Data flow analysis techniques for test data selection. In:Proc. of
the 6th International Conference on Software Engineering, Tokio, Japan, p. 272–278, 1982.
REFERÊNCIAS BIBLIOGRÁFICAS 102
RYDER, B. G.; SOFFA, M. L. Influences on the design of exception handling: ACM SIGSOFT
project on the impact of software engineering research on programming language design.SIG-
PLAN Not., v. 38, n. 6, p. 16–22, 2003.
DOS SANTOS DOMINGUES, A. L. Avaliação de Critérios e Ferramentas de Teste para Progra-
mas OO. Dissertação de Mestrado, ICMC-USP, São Carlos - SP, Brasil, 2001.
SINHA , S.; HARROLD, M. J. Analysis of programs with exception-handling constructs. In:
Proceedings of the International Conference on Software Maintenance, Bethesda, MD, p. 348–
357, 1998.
SOMMERVILLE , I. Software engineering. 6 ed. Addison Wesley, 693 p., 2000.
STANDARDS COORDINATING COMITTEE OF THE COMPUTER SOCIETY OF THE IEEE IEEE
Standard Glossary of Software Engineering Terminology. 345 East 47th Street, New York, NY
10017, USA, 1990.
STEIN, D.; HANENBERG, S.; UNLAND , R. A uml-based aspect-oriented design notation for
aspectj. In:Proceedings of the 1st international conference on Aspect-oriented software deve-
lopment, ACM Press, p. 106–112, 2002.
STÖRZER, M.; KRINKE, J.; BREU, S. Trace Analysis for Aspect Application. In:Analysis of
Aspect-Oriented Software (AAOS), Germany, 2003.
V INCENZI, A. M.; WONG, E. W.; DELAMARO , M. E.; SIMÃO , A. S.; MALDONADO , J. C.
JaBUTi – Java Bytecode Understanding and Testing – user’s guide – version 1.0. Relatório
Técnico, ICMC/USP, em preparação, 2004.
V INCENZI, A. M. R. Orientação a objeto: Definição, implementação e análise de recursos de
teste e validação. Tese de Doutoramento, Universidade de São Paulo, 2004.
V INCENZI, A. M. R.; WONG, W. E.; DELAMARO , M. E.; MALDONADO , J. C. JaBUTi:
A Coverage Analysis Tool for Java Programs. In:Sessão de Ferramentas do 17o Simpósio
Brasileiro de Engenharia de Software, Manaus, AM, Brasil, 2003.
WALIGORA , S.; COON, R. Improving the Software Testing Process in NASA’s Software Engi-
neering Laboratory. In:Proceedings of the Twentieth Annual Software Engineering Workshop,
1995.
WALKER , D.; ZDANCEWIC, S.; LIGATTI , J. A theory of aspects. In:Proceedings of the eighth
ACM SIGPLAN international conference on Functional programming, ACM Press, p. 127–139,
2003.
REFERÊNCIAS BIBLIOGRÁFICAS 103
WALKER , R. J.; BANIASSAD, E. L. A.; MURPHY, G. C. An initial assessment of aspect-
oriented programming. In:Proceedings of the 21st international conference on Software engi-
neering, IEEE Computer Society Press, p. 120–130, 1999.
WEYUKER, E. J. Thinking formally about testing without a formal specification. In:Proce-
edings of the Formal Approaches to Testing of Software (FATES’02), Brno, Czech Republic:
INRIA Press, p. 1–10, 2002.
X IE, T.; ZHAO, J.; MARINOV, D.; NOTKIN , D. Detecting Redundant Unit Tests for AspectJ
Programs. Relatório Técnico UW-CSE-04-10-03, Department of Computer Science and Engi-
neering, University of Washington, Seattle, WA, 2004.
XU, D.; XU, W.; NYGARD, K. A State-Based Approach to Testing Aspect-Oriented Programs.
Relatório Técnico NDSU-CS-TR04-XU03, Computer Science Department, North Dakota State
University, Fargo, ND, 2004a.
XU, W.; XU, D.; GOEL, V.; NYGARD, K. Aspect flow graph for testing aspect-oriented pro-
grams. In:Proc. of the 8th IASTED International Conference on Software Engineering and
Applications, 2004b.
ZHAO, J. Tool Support for Unit Testing of Aspect-Oriented Software. In:OOPSLA 2002
Workshop on Tools for Aspect-Oriented Software Development, Seattle, WA, 2002.
ZHAO, J. Data-Flow-Based Unit Testing of Aspect-Oriented Programs. In:Proceedings of
the 27th Annual IEEE International Computer Software and Applications Conference, Dallas,
Texas, USA, 2003.
ZHOU, Y.; RICHARDSON, D.; ZIV, H. Towards a practical approach to test aspect-oriented
software. In:Proc. of the 2004 Workshop on Testing Component-based Systems (TECOS 2004),
Net.ObjectiveDays, 2004.
ZHU, H.; HALL , P. A. V.; MAY, J. H. R. Software Unit Test Coverage and Adequacy.Computer
Survey, v. 29, n. 4, p. 367–427, 1997.