Você está na página 1de 59

Teste de Software Orientado a Objetos e a Aspectos: Teoria e Prtica

Paulo Cesar Masiero, Otvio Augusto Lazzarini Lemos, Fabiano Cutigi Ferrari e Jos Carlos Maldonado

Abstract Software testing has been shown to be important for reducing the number of product faults, attracting interest from both academy and industry. As novel development approaches and support technologies are proposed, new challenges arise in the testing of software developed in these new contexts. For instance, object-oriented (OO) programming, already adopted by the developer community, and, more recently, aspect-oriented (AO) programming, have motivated various efforts for the establishment of novel testing approaches and criteria. The goal of this chapter is to present theoretical and practical aspects of the software testing activity in the OO and AO software development context. In particular, we emphasize structural and fault-based techniques with the support of JaBUTi and JaBUTi/AJ testing tools, developed by the software engineering research group at ICMC/USP. Resumo O teste de software tem se mostrado importante para reduzir a quantidade de defeitos presentes nos produtos, atraindo o interesse tanto da academia quanto da indstria. medida que surgem novas abordagens de desenvolvimento e tecnologias de apoio, surgem tambm novos desaos relacionados ao teste de software aplicado nesses contextos. Por exemplo, o paradigma orientado a objetos (OO) j consolidado entre a comunidade desenvolvedora e, mais recentemente, a programao orientada a aspectos (POA) tm motivado diversos esforos dedicados ao estabelecimento de abordagens e critrios de teste nesses contextos. O objetivo deste texto apresentar os aspectos tericos e prticos relacionados atividade de teste de software, tanto no contexto de software OO quanto orientado a aspectos (OA). Em particular, so enfatizadas as tcnicas estrutural e baseada em defeitos com o apoio das ferramentas de teste JaBUTi e JaBUTi/AJ, desenvolvidas pelo grupo de pesquisas em Engenharia de Software do ICMC/USP.

13

Masiero, Lemos, Ferrari e Maldonado

1.1. Introduo
Muitas propostas de tcnicas e ferramentas para a vericao, validao e teste de software tm sido apresentadas no contexto da rea agregada sob o nome de Garantia de Qualidade de Software (GQS). Dentre elas, o teste uma das mais utilizadas e consiste na execuo de um produto com a inteno de revelar defeitos. Sua importncia deve-se ao fato das outras atividades de GQS serem insucientes para a descoberta dos erros introduzidos ao longo do desenvolvimento do software [Pressman 2000, Myers et al. 2004]. Apesar de ser impossvel provar que um software est absolutamente correto por meio de testes, a sua utilizao fornece evidncias da conformidade com as funcionalidades especicadas [Myers et al. 2004]. Alm disso, uma atividade de teste conduzida de forma sistemtica e criteriosa auxilia no entendimento dos artefatos testados e evidencia as caractersticas mnimas do ponto de vista da qualidade do software [Harrold 2000, Weyuker 1996]. A aplicao de um critrio de teste est fortemente condicionada sua automatizao, sendo ento de fundamental importncia o desenvolvimento de ferramentas de teste. Sem a utilizao de ferramentas que automatizem a aplicao das tcnicas e critrios associados, a atividade de teste tornase onerosa, propensa a erros e limitada a programas muito simples. Dessa forma, a qualidade do teste, alm de depender das tcnicas e critrios sendo utilizados, tambm depende da disponibilidade de ferramentas de apoio. Alm disso, ferramentas de teste facilitam a conduo de estudos experimentais que visam avaliar e comparar os diversos critrios de teste [Vincenzi 2004]. Com a consolidao do paradigma orientado a objetos (OO) para desenvolvimento de software durante os anos 80, diversos esforos tm sido dedicados ao estabelecimento de tcnicas e critrios de teste no contexto desse paradigma. Embora a orientao a objetos tenha trazido benefcios para o projeto e desenvolvimento de software, oferecendo uma nova viso de como decompor problemas e novas tcnicas para modelar e implementar solues para esses problemas [Booch 1994], o teste de software OO apresenta um novo conjunto de desaos ao engenheiro de software, relacionados s caractersticas e construes especcas do paradigma OO como, por exemplo, encapsulamento, herana, polimorsmo e acoplamento dinmico [McDaniel and McGregor 1994, Binder 1999]. Alm disso, mesmo com o desenvolvimento de tcnicas para construo de software OO, observa-se que independentemente da tcnica utilizada ou do modo como o sistema decomposto, alguns interesses pertinentes encontramse espalhados ou entrelaados nas vrias unidades do cdigo desenvolvido, no estando portanto decompostos de forma organizada e localizada em mdulos separados [Elrad et al. 2001]. A Programao Orientada a Aspectos (POA) surgiu no nal da dcada de 90 como uma opo para solucionar essas diculdades, permitindo que interesses que se encontram espalhados ou entrelaados possam ser implementados to separadamente quanto possvel [Kiczales et al. 1997]. Contudo, assim como para a orientao a objetos, a
14

Teste de Software OO e OA: Teoria e Prtica

adoo da POA como tcnica de desenvolvimento no evita que defeitos especcos relacionados s novas caractersticas e construes relacionadas sejam introduzidos no software. Ressalte-se que a maioria dos esforos dedicados pesquisa envolvendo orientao a aspectos concentrada no estabelecimento dos conceitos e em como implement-los nas tecnologias de apoio. Poucas ainda so as iniciativas para estabelecer critrios e estratgias de teste para programas orientados a aspectos (OA) Diversos grupos de pesquisa na rea de teste vm desenvolvendo atividades de pesquisa concentradas no estudo de princpios, estratgias, mtodos e critrios de teste e validao de software, bem como na especicao e implementao de ferramentas de teste que apiem a realizao das atividades de teste e viabilizem a avaliao do aspecto complementar dos critrios de teste por meio de estudos empricos. Dentre os critrios de teste investigados pelo grupo de Engenharia de Software do ICMC/USP destacam-se os critrios estruturais e baseados em mutao, explorados nos contexto de software procedimental e, mais recentemente, para programas OO e OA. O presente texto busca abordar os aspectos tericos e prticos relacionados atividade de teste de software OO e OA, enfatizando o teste de unidade. Nesta seo foi apresentado o contexto no qual este texto est inserido. As prximas sees esto organizadas da seguinte forma: na Seo 1.2 so introduzidos os conceitos bsicos e a terminologia pertinentes ao teste de software, incluindo os critrios de teste mais difundidos das tcnicas estrutural e baseada em defeitos. Alm disso, so apresentados os principais conceitos relacionados programao OO e OA. Na Seo 1.3 so apresentados os conceitos envolvendo a aplicao de critrios estruturais e baseados em mutao aplicados nesses contextos. Em seguida, na Seo 1.4, apresentam-se exemplos da aplicao dos critrios abordados. Para os critrios estruturais, so discutidas as ferramentas JaBUTi (OO) e JaBUTi/AJ (OA), incluindo uma breve apresentao de seus aspectos operacionais. Apresenta-se tambm uma aplicao prtica do teste de mutao em programas OO e OA. Por m, na Seo 1.5, so apresentadas as concluses e perspectivas de trabalhos futuros na rea de teste de software.

1.2. Terminologia e Conceitos Bsicos


Em um projeto de software, os artefatos submetidos ao teste podem incluir programas, especicaes de requisitos e de projeto, estruturas de dados ou quaisquer outros artefatos conceitualmente executveis utilizados no desenvolvimento da aplicao. De maneira geral, cada um desses artefatos representa uma funo que, por sua vez, descreve a relao entre um elemento de entrada (chamado de elemento do domnio) e um elemento de sada. Para se testar o software so necessrios cinco elementos: o artefato conceitualmente executvel, a descrio do comportamento esperado (obtida por meio de um orculo), a observao da execuo do artefato em teste, a descrio do domnio funcional e um mtodo para determinar se o comportamento observado corresponde
15

Masiero, Lemos, Ferrari e Maldonado

ao esperado [Adrion et al. 1982]. Dessa forma, podem ser denidas quatro atividades no teste de software: planejamento, projeto dos casos de teste, execuo dos casos de teste e avaliao dos resultados [Pressman 2000]. Essas atividades podem ser realizadas em cada uma das seguintes fases, nas quais se divide tradicionalmente o teste de um software: Teste de unidade: busca identicar defeitos1 na lgica e na implementao de cada unidade do software, isoladamente. Segundo o padro IEEE 610.12 de 1990 [IEEE 1990], uma unidade um componente de software indivisvel. Para se testar as unidades de um sistema, tem-se a necessidade da implementao de pseudo-controladores (drivers) e pseudo-controlados (stubs). Os drivers so responsveis por coordenar e ativar a unidade que est sendo testada, passando a ela os dados de teste. Os stubs, por sua vez, consistem em implementaes simplicadas que substituem entidades que interagem com a unidade em teste. Teste de integrao: visa a descobrir defeitos nas interfaces das unidades, durante a integrao da estrutura do programa. No caso de um programa OO, considerando-se o mtodo como uma unidade, um tipo de teste de integrao consiste em testar cada mtodo juntamente com os mtodos chamados direta ou indiretamente por ele dentro da mesma classe. Para um dado paradigma, a denio do que consiste uma unidade implica em diferentes denies de teste de unidade e de integrao. Neste texto, essas denies so apresentadas na Seo 1.3.1. Teste de sistema: quer identicar defeitos nas funes e caractersticas de desempenho do sistema como um todo. Alm disso, procura-se vericar se todos os elementos do sistema (por exemplo, hardware, pessoal e bases de dados) combinam adequadamente e se a funo/desempenho global do sistema alcanada. Um caso de teste um par ordenado (d , S(d )) tal que d um elemento de um determinado domnio D (d D) e S(d ) a sada esperada para uma dada funo da especicao, quando d utilizado como entrada. Uma vericao completa de um determinado programa P poderia ser obtida testandose P com um conjunto de casos de teste T que inclui todos os elementos do domnio. Entretanto, como geralmente o conjunto de elementos do domnio innito ou muito grande, torna-se necessria a obteno de subconjuntos desses casos de teste. Para isso, podem ser utilizados critrios de teste que auxiliam o testador, fornecendo um mtodo para a avaliao de conjuntos de
1

Neste texto procura-se seguir o padro IEEE 610.12-1990 [IEEE 1990] nas denies utilizadas para os termos defeito, erro e falha. Defeito denido como uma divergncia entre o produto de software desenvolvido e o produto supostamente correto; erro denido como um estado interno do produto de software que diverge do estado correto, gerado pela execuo de um defeito presente nesse produto; e falha denida como a manifestao externa de um erro quando esse interfere nos comandos que produzem a sada do produto em execuo.

16

Teste de Software OO e OA: Teoria e Prtica

casos de teste e uma base para a seleo de casos de teste. No primeiro caso os critrios de adequao servem para evidenciar a sucincia da atividade de teste e, no segundo caso, para ajudar na construo de casos de teste [Frankl and Weyuker 2000]. Os critrios de teste so geralmente derivdos a partir de quatro tcnicas conhecidas: funcional, estrutural, baseada em defeitos e baseada em estados, que diferem pela abordagem de teste subjacente utilizada para gerar e avaliar os casos de teste [Zhu et al. 1997]. A tcnica funcional, tambm chamada de teste caixa-preta, tem o objetivo de determinar se o programa satisfaz aos requisitos funcionais e no-funcionais encontrados na especicao. A tcnica estrutural ou teste caixa-branca, por sua vez, concentra-se na cobertura de partes do cdigo-fonte a partir dos casos de teste, sendo portanto baseada em uma implementao especca. Os critrios estruturais so baseados na idia de que no se pode conar em um programa se alguns elementos estruturais nunca foram executados durante a atividade de teste. O teste baseado em defeitos utiliza informaes sobre os tipos de erros freqentemente cometidos no processo de desenvolvimento de software para derivar os requisitos de teste. Nesse contexto, modelos de defeitos caractersticos tecnologia de implementao do software geralmente so utilizados, sendo elaborados a partir da experincia dos engenheiros de software e de dados experimentais. Por m, o teste baseado em estados utiliza uma representao baseada em estados para modelar o comportamento do sistema ou unidade que ser testada. Com base nesse modelo, critrios de gerao de seqncias de teste podem ser utilizados de modo a garantir o correto funcionamento do sistema ou unidade. Essas tcnicas so consideradas complementares, sendo de fundamental importncia o estabelecimento de estratgias de teste capazes de explorar as vantagens de cada critrio. Nesse contexto, tm sido realizados vrios estudos tericos e experimentais envolvendo a aplicao das diferentes tcnicas de teste e a denio dessas estratgias [Zhu et al. 1997]. Neste texto so enfatizadas as tcnicas de teste estrutural e baseada em defeitos devido sua grande relevncia, demonstrada pela extensa investigao na comunidade de Engenharia de Software e grande quantidade de publicaes sobre essas tcnicas. Alm disso, elas constituem os principais tpicos investigados pelo grupo de pesquisa dos autores na rea de teste. Em particular, abordada a aplicao dos critrios estruturais baseados em uxo de controle e em uxo de dados e, para a tcnica baseada em defeitos, critrios baseados em mutao.

Teste Estrutural
A tcnica estrutural vista como complementar tcnica funcional e baseia-se na estrutura de um programa para derivar seus casos de teste. Em geral, os critrios dessa tcnica utilizam o grafo de uxo de controle ou grafo de programa. Esse grafo ilustra o uxo de controle lgico de um programa utilizando uma notao-padro [Pressman 2000].
17

Masiero, Lemos, Ferrari e Maldonado

Considerando um programa P, constitudo de vrios comandos, um bloco consiste em um conjunto de comandos no qual a execuo do primeiro comando do bloco acarreta a execuo de todos os outros comandos do mesmo bloco, na seqncia determinada. Sendo assim, cada comando de um bloco, possivelmente com exceo do primeiro, tem um nico predecessor e exatamente um sucessor, exceto possivelmente o ltimo comando. Um grafo de uxo de controle consiste em uma correspondncia entre ns e blocos de um programa, que indica possveis uxos de controle entre esses blocos atravs de arestas [Zhu et al. 1997]. No que diz respeito ao grafo de uxo de controle, alguns conceitos so importantes para o seu uso na tcnica estrutural. Dado um grafo de uxo de controle G = (N , E , s) em que N o conjunto de ns, E o conjunto de arestas e s o n de entrada, um caminho corresponde a uma seqncia nita de ns (n1 , n2 , ..., nk ), para k 2, tal que existam arestas de ni para ni+1 para i = 1, 2, ..., k 1. Um caminho dito simples se todos os ns que compem esse caminho, exceto possivelmente o primeiro e o ltimo, forem distintos. Se todos forem distintos, incluindo o primeiro e o ltimo, o caminho dito livre de lao. Os ns de entrada e ns de sada correspondem, respectivamente, aos ns que no possuem nenhum antecessor e aos ns que no possuem nenhum sucessor. Em outras palavras, os ns de entrada no possuem nenhuma aresta de entrada e os ns de sada no possuem nenhuma aresta de sada [Zhu et al. 1997]. Um caminho completo um caminho que vai do n de entrada a um n de sada do grafo de uxo de controle. Outro termo importante no teste estrutural refere-se aos caminhos no-executveis. Um caminho no-executvel um caminho do grafo de uxo de controle impossvel de ser coberto para qualquer elemento do domnio de entrada. Isso acontece quando as condies lgicas que deveriam ser satisfeitas para que a seqncia de ns do caminho fosse executada so contraditrias [Howden 1986]. A partir do grafo de uxo de controle, podem ser escolhidos os caminhos a serem executados, com o apoio dos diferentes critrios da tcnica estrutural. Na Figura 1.1(a) mostrado o cdigo Java de um mtodo para validao de identicadores, e na Figura 1.1(b) o seu respectivo grafo de uxo de controle adicionado tambm de informaes de uxo de dados. Os critrios de teste estrutural baseiam-se na idia de que no se pode conar em um programa P se existem certos caminhos que ainda no foram percorridos. Esses critrios geralmente associam um conjunto T de casos de teste (que contm um subconjunto das entradas do programa) com um conjunto de caminhos no grafo de uxo de controle de P, que so percorridos quando esses casos de teste so executados. T satisfaz o critrio C para P se e somente se todo caminho requerido por C um subcaminho de um dos caminhos de [Rapps and J.Weyuker 1982]. Nesse caso, diz-se que T Cadequado para o teste de P. Os primeiros critrios estruturais propostos so baseados apenas no uxo
18

Teste de Software OO e OA: Teoria e Prtica

1
up={s}

d ={s} up={s}

d=definio uc=cuso up=puso

/* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /*

01 02 03 03 03 03 03 03 04 05 06 07 07 08 09 09 10 11 12 12

*/ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */

public static boolean verify(String s) { if (s == null || s.length() == 0) return false; char achar; boolean valid_id; valid_id = true; achar = s.charAt(0); valid_id = valid_s(achar); if (s.length() == 1 && valid_id) return true; int i = 1; while (i < s.length()) { achar = s.charAt(i); if (!valid_f(achar)) valid_id = false; i++; } if (valid_id && (s.length() <= 6)) return true; return false; }

2
up={s,valid_id}

d={achar, valid_id} uc={achar, s} up={s,valid_id} d={i}

6
up={i,s} d={achar} uc={achar, s} up={i,s}

7
up={s}

10
up={s}

d={valid_id}

11

12

d={i} uc={i}

13

Figura 1.1. Grafo Def-Uso do mtodo para validao de identicadores


de controle da aplicao e, entre eles, os mais conhecidos so: Todos-Ns: Este critrio exige que cada comando do programa seja executado ao menos uma vez, ou seja, que cada n do grafo de uxo de controle seja coberto. Em geral esse um critrio tido como fraco, pois, na prtica, no revela nem mesmo a existncia de defeitos mais simples [Myers et al. 2004]. Todas-Arestas: Todas-Arestas requer que cada desvio do uxo de controle do programa seja exercitado pelo menos uma vez, ou seja, que cada aresta do grafo seja coberta. No grafo de uxo de controle/dados para o mtodo de validao de identicadores apresentado na Figura 1.1, fcil ver que, para este caso, a cobertura de todos os ns do grafo no implica a cobertura de todos as arestas. 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 ns, porm no exercitem a aresta 7 9. Todos-Caminhos: O critrio Todos-Caminhos requer que todos os caminhos possveis do grafo de uxo de controle sejam executados. Esse
19

Masiero, Lemos, Ferrari e Maldonado

critrio o mais exigente do teste caixa-branca e o nmero de requisitos de teste gerados para ele, mesmo em um programa simples, pode ser demasiadamente grande (possivelmente innito) [Myers et al. 2004]. Na Figura 1.1, por exemplo, em decorrncia do lao (6, 7, 8, 9, 6), tem-se inmeros caminhos no grafo. Alm dos critrios baseados em uxo de controle, existem ainda os baseados em uxo de dados, os quais utilizam o grafo Def-Uso, construdo a partir do grafo de uxo de controle, estendido com informaes adicionais sobre as denies e subseqentes usos das variveis contidas em um programa (como o exemplo apresentado na Figura 1.1 (b)). Uma denio de varivel ocorre toda vez que um valor atribudo a uma varivel. O uso de variveis, por sua vez, pode ser de dois tipos: uso computacional (c-uso), em que o valor da varivel utilizado em uma computao; e uso predicativo (p-uso), em que o valor da varivel utilizado em uma estrutura condicional ou de repetio. A partir da, se existe alguma sentena em um bloco contendo um c-uso ou denio de uma varivel, adiciona-se essa informao ao n referente no grafo. J os pusos so associados s arestas do grafo, visto que o valor das variveis afeta a seqncia de execuo do programa. Um caminho livre de denio para uma varivel x dos ns i a j, um caminho (i, n1 , ..., nm , j), m 1, no qual no h denies de x nos ns n1 , ..., nm [Rapps and J.Weyuker 1982]. Outro conceito importante o de par Def-Uso, que se refere a um par de denio e subseqente c-uso ou p-uso de uma varivel. O critrio mais bsico da famlia de critrios denidos por Rapps e Weyuker [1984] o critrio Todas-Denies. Entre os critrios dessa famlia, o critrio Todos-Usos tem sido um dos mais utilizados e investigados. Para o propsito deste texto, podem ser destacados os seguintes critrios baseados em uxo de dados: Todas-Denies: Requer que cada denio de varivel seja exercitada pelo menos uma vez, no importa se por um c-uso ou por um p-uso. Todos-Usos: Requer que todas as associaes entre uma denio de varivel e seus subseqentes usos (c-usos e p-usos) sejam exercitadas pelos casos de teste, atravs de pelo menos um caminho livre de denio. Todos-Potenciais-Usos: Requer que pelo menos um caminho livre de denio de uma varivel denida em um n i para todo n e todo arco possvel de ser alcanado a partir de i seja exercitado.

Teste de Mutao
Um dos critrios mais investigados da tcnica baseada em defeitos o teste baseado em Anlise de Mutantes, ou teste de mutao. Esse critrio tem um forte relacionamento com o modelo de teste de falha nica de circuitos digitais, para a deteco de defeitos lgicos [Friedman 1975, Acree 1980]. A idia bsica gerar vrias verses do programa original ligeiramente modicado, com o objetivo de revelar os defeitos mais comuns introduzidos nos progra20

Teste de Software OO e OA: Teoria e Prtica

mas pelos programadores [DeMillo 1978]. Dessa forma, o conjunto de casos de teste tambm avaliado quanto sua capacidade de revelar esses defeitos. Se as sadas obtidas na execuo de um mutante so iguais s sadas do programa original, cabe ao testador determinar se o mutante equivalente ao programa original ou se existe a necessidade de se projetar novos casos de teste que resultem em uma sada diferente para o mutante em questo. A gerao dos mutantes tem como base duas hipteses: a hiptese do programador competente e a hiptese do efeito de acoplamento [DeMillo 1978]. A primeira assume que programas a serem testados esto corretos ou prximos do correto. A segunda, por sua vez, assume que casos de teste capazes de revelar defeitos mais simples tambm revelam defeitos mais complexos. Um defeito simples est associado a uma alterao sinttica simples, enquanto que um defeito complexo pode ser considerado como uma composio de defeitos simples. Para a aplicao do teste de mutao, dados um programa P e um conjunto casos de teste T, quatro passos so necessrios: 1. Gerao do conjunto M de mutantes: Operadores de mutao so aplicados em P para gerar um conjunto de mutantes M. Os operadores implementam as regras de alterao que so aplicadas no programa original. Ressalta-se que a denio dos operadores de mutao depende da linguagem de programao ou de especicao em que os artefatos a serem testados esto escritos. 2. Execuo de P com T : A execuo de P com T realizada e verica-se se o resultado o esperado. O processo encerrado caso alguma sada seja incorreta. Caso isso no ocorra, o prximo passo realizado. Geralmente cabe ao prprio testador decidir se a sada ou no a esperada para cada caso de teste, fazendo portanto o papel de orculo [Weyuker 1982]. 3. Execuo dos mutantes de M com T : Cada um dos mutantes em M executado com os casos de teste em T. Se um mutante m apresenta uma sada diferente de P executado com o mesmo caso de teste, conclui-se que o conjunto de casos de testes sensvel e consegue expor a diferena entre m e P, e m considerado morto. Caso contrrio, duas possibilidades so consideradas: ou T de baixa qualidade, no conseguindo revelar a diferena entre m e P, ou m equivalente a P. Encerrada a execuo dos mutantes, o escore (score) de mutao pode ser calculado pela seguinte frmula: ms(P, T ) = Sendo: ms(P,T): escore da mutao (mutation score); DM(P,T): nmero de mutantes mortos por T;
21
DM (P,T ) M (P)EM (P)

Masiero, Lemos, Ferrari e Maldonado

M(P): nmero total de mutantes gerados; EM(P): nmero de mutantes equivalentes identicados. O resultado desse clculo sempre um valor no intervalo [0, 1], fornecendo uma medida de adequao de T, sendo que quanto mais prximo de 1 for o resultado, mais adequado T. 4. Anlise dos mutantes: Esse o passo que requer mais interveno humana. Inicialmente, decide-se se o teste deve ou no continuar, dependendo do escore da mutao. Resultados prximos a 1 sugerem um conjunto de casos de teste T sucientemente bom e pode-se optar por encerrar o teste. Caso decida-se por continuar o teste, cada um dos mutantes m vivos analisado se ele ou no equivalente a P. O problema de determinar a equivalncia entre dois programas, nesse caso P e um mutante m, geralmente indecidvel. Nesse contexto, embora a automatizao completa dessa atividade no seja possvel, alguns mtodos e heursticas tm sido propostos para a determinao da equivalncia entre P e m [Budd 1981, Simo and Maldonado 2000, Vincenzi et al. 2002]. A aplicao da Anlise de Mutantes apresenta um alto custo devido principalmente grande quantidade de mutantes gerados at mesmo para programas pequenos e diculdade na identicao dos mutantes equivalentes ao programa original. Diversos trabalhos tm investigado a adoo de estratgias de aplicao do critrio de forma a reduzir seu custo, principalmente relacionado ao nmero de mutantes gerados, entretanto sem perdas signicativas na eccia em revelar defeitos. Dentre elas, destacam-se a Mutao Aleatria, que consiste em gerar apenas uma porcentagem de mutantes para cada operador selecionado [Acree et al. 1979]; a Mutao Restrita, em que um subconjunto de operadores de mutao selecionado e aplicado na gerao dos mutantes [Mathur and Wong 1993]; e a Mutao Seletiva, na qual os operadores responsveis pela gerao do maior nmero de mutantes no so utilizados [Offutt et al. 1993]. Os resultados obtidos com a aplicao dessas estratgias de mutao apontam que possvel reduzir expressivamente o custo de aplicao do critrio (ganhos prximos a 80%) sem redues signicativas no escore de mutao, que pode car muito prximo a 1, ou seja, quase 100% de mutantes no-equivalentes mortos. Para o contexto de teste de integrao, Delamaro et al. [2001a] estenderam o critrio Anlise de Mutantes, denindo o critrio Mutao de Interface, que busca assegurar que as interaes entre as diferentes unidades sejam testadas adequadamente. Foi proposto um conjunto de operadores de mutao cuja principal diferena para os operadores de mutao de unidade est nos pontos do cdigo que so enfatizados para serem mutados. Para esses novos operadores, esses pontos correspondem aos pontos de comunicao entre duas unidades.
22

Teste de Software OO e OA: Teoria e Prtica

Conforme destacado anteriormente, novas abordagens e tecnologias de desenvolvimento requerem a adaptao ou proposio de novas tcnicas e critrios de teste adequados para esses contextos. Em particular, isso se aplica s tcnicas e critrios de teste enfatizados nesta seo, no que diz respeito ao teste de software OO e OA. Assim, faz-se necessrio o entendimento dos principais conceitos e construes introduzidos pelo paradigma OO e pela POA.

1.2.1. Programao Orientada a Objetos


O paradigma OO consolidou-se nos anos 80 como o paradigma de escolha para muitos desenvolvedores de produtos de software [Pressman 2000]. Seus conceitos foram estendidos alm das tecnologias de apoio programao, resultando desde em linguagens de modelagem at em modelos de processos de desenvolvimento, como a UML e o Processo Unicado. Os objetos constituem o principal elemento da orientao a objetos. Um objeto pode ser denido como uma estrutura que retm informaes e um conjunto de operaes, disponibilizando funcionalidades especcas para seus usurios [Wirfs-Brock et al. 1990, Cox and Novobilski 1991]. As informaes retidas em um objeto so conhecidas como atributos e as operaes so conhecidas como mtodos (ou funes-membro em algumas linguagens como, por exemplo, C++ [Stroustrup 1986]). Um objeto solicitado para realizar uma determinada operao por meio do envio de uma mensagem (chamada ao respectivo mtodo) que informa a esse objeto o que realizar. A resposta gerada primeiramente pela escolha do mtodo que implementa a operao solicitada; em seguida, a operao executada e o controle retornado ao solicitante juntamente com uma possvel resposta mensagem recebida [Cox and Novobilski 1991]. Uma classe consiste em uma entidade esttica que contm a denio dos atributos de um objeto e a implementao das operaes disponveis para ele. Um objeto uma instncia de uma classe gerada em tempo de execuo, sendo que cada um dos objetos possui, a princpio, suas prprias instncias2 de cada um dos atributos e operaes. O conjunto corrente dos valores dos atributos de um objeto caracteriza o estado desse objeto. A visibilidade do estado do objeto controlada pelos mecanismos de ocultamento de informaes, que esto diretamente relacionados com os mecanismos de encapsulamento. O encapsulamento permite que um objeto responda chamada de uma determinada operao, isentando o usurio de precisar saber como essa operao executada e quais dados sero manipulados para realizar essa operao. Em conjunto com o conceito de ocultamento de informaes, que possibilita a remoo da visibilidade de determinados dados e operaes encapsuladas em uma classe [Wirfs-Brock et al. 1990], pode-se isolar a implementao dessa classe das demais. Dessa forma, permite-se
2

Em geral, as linguagens OO permitem a denio de atributos e mtodos estticos, que so compartilhados por todos os objetos instanciados a partir da classe em que so denidos.

23

Masiero, Lemos, Ferrari e Maldonado

a realizao de manuteno no cdigo dessa classe sem a necessidade de mudanas em outras classes do sistema que dependem dos seus servios. A identicao de atributos e operaes comuns a vrias classes de objetos permite a denio de uma nova classe que includa em um nvel superior de uma hierarquia [Booch 1994]. Todas as classes que aparecem no nvel inferior da hierarquia possuem, a princpio, todas as informaes e operaes de classes que esto em nveis superiores do mesmo ramo da hierarquia. Temse ento uma relao de herana, sendo que as classes em um nvel inferior herdam as informaes e operaes denidas nas classes em nveis superiores. Assim, novas classes no precisam ser completamente desenvolvidas, mas podem herdar partes de funcionalidades de outras classes. A partir da somente as funcionalidades especcas devem ser implementadas na classe herdeira [Cox and Novobilski 1991]. Denidas as hierarquias de classes utilizando-se os mecanismos de herana, o desenvolvedor pode usufruir de tipos polimrcos, em geral apoiado pelas tecnologias de desenvolvimento de software OO. De acordo com a denio de Booch [1994] , polimorsmo representa o conceito da teoria dos tipos na qual um simples nome pode denotar objetos instanciados a partir de diferentes classes que so relacionadas a uma superclasse comum. Dessa forma, um identicador de objeto do tipo de uma superclasse pode ser atribudo para diferentes objetos instanciados a partir de suas subclasses. Isso permite que um objeto envie uma mensagem sem saber para qual objeto a mensagem est sendo enviada. Nesse caso, diferentes tipos de objetos podem estar denidos para responder mensagem, cada um sua maneira [Wirfs-Brock et al. 1990]. No caso do uso de tipos polimrcos, o mecanismo de acoplamento dinmico encarrega-se de resolver, em tempo de execuo, qual objeto responder mensagem e qual mtodo ser executado. Na Figura 1.2 apresentado um modelo de uma aplicao OO, adaptado do trabalho do AspectJ Team [2003]. As classes presentes no modelo representam a funcionalidade bsica de um sistema de simulao de telefonia no qual um cliente pode realizar e receber chamadas telefnicas locais ou de longa distncia, e pode atender mais de uma chamada simultaneamente, resultando em uma conferncia. Cada chamada simples (classes Local ou LongDistance) possui uma conexo (classe Connection), e uma conferncia possui duas ou mais conexes. Nesse exemplo, alguns dos conceitos introduzidos pela orientao a objetos podem ser observados. Entre eles, os conceitos de classe, mtodos e atributos, e a relao de herana estabelecida entre a superclasse Connection e suas subclasses Local e LongDistance. Embora no seja explcito no exemplo, o polimorsmo pode ser observado com a utilizao de um identicador declarado como sendo do tipo Connection, cuja instanciao pode ser tanto do tipo Local quanto LongDistance. O tipo corrente instanciado e atribudo ao identicador decidido em tempo de execuo, e os comportamentos apropriados so executados dependendo do tipo identicado.
24

Teste de Software OO e OA: Teoria e Prtica

1 * Call *

-connections

* Connection

1 Customer -receiver

-caller

-isMobile : Boolean -state : int +complete() : void +drop() : void +connects() : bool

-name : String -phoneNumber : String -areacode : int -password : String -calls +addCall() +removeCall() +localTo() +pickup() +hangup() +merge()

Local

LongDistance

-caller

-receiver

Figura 1.2. Modelo de aplicao OO de telefonia

A linguagem Java [Sun Microsystems 2006] uma das linguagens de desenvolvimento de software OO que possibilita a aplicao dos principais conceitos e recursos do paradigma. Java ser utilizada nos exemplos apresentados nas prximas sees deste texto.

1.2.2. Programao Orientada a Aspectos


Em meados dos anos 90, alguns pesquisadores constataram a existncia de certos interesses que, mesmo com a utilizao da programao OO, no se encaixavam em mdulos individuais, cando espalhados por vrias unidades do cdigo (tambm chamados de interesses transversais). A POA foi concebida como uma proposta de resoluo desse problema, a partir do uso de mecanismos que permitem o isolamento desses interesses. O mecanismo de quanticao, introduzido pela POA, possibilita a implementao de mdulos isolados, os aspectos, que tm a capacidade de afetar outros mdulos do sistema de forma transversal. Dessa forma, em POA um nico aspecto pode contribuir para a implementao de diversos outros mdulos que implementam as funcionalidades bsicas, chamados de mdulos base [Elrad et al. 2001]. A quanticao permite que a estrutura de implementao represente mais adequadamente o projeto do sistema, pois sem ela certos tipos de interesses tendem a emaranhar-se com outros e/ou espalharem-se por diversos mdulos. Por exemplo, na Figura 1.3 so representados por meio de barras os diferentes mdulos presentes na implementao OO do servidor Tomcat. As partes dos mdulos que implementam o interesse de registro (logging ) esto destacadas
25

Masiero, Lemos, Ferrari e Maldonado

nas barras. O espalhamento pode ser vericado pelo fato de diferentes mdulos possurem cdigo referente a esse interesse. O emaranhamento pode ser vericado pelo fato da existncia de cdigo que implementa mais de uma funcionalidade uma principal e a de registro em mdulos isolados. Dessa forma existe uma separao de interesses no rigorosamente adequada, na qual cada mdulo implementaria um nico interesse. Alguns tipos de emaranhamento e espalhamento podem ser resolvidos pelas tcnicas de software tradicionais, entretanto, existem outros que, por sua prpria natureza, no cam bem modularizados quando utilizados esses paradigmas.

Figura 1.3. Estrutura de mdulos do Tomcat

Para que os aspectos possam adicionar comportamento em outros mdulos por meio do mecanismo de quanticao necessrio que se possam identicar pontos concretos da execuo de um programa. Esses pontos so chamados pontos de juno (join points) do programa. Dessa forma, uma linguagem orientada a aspectos deve fornecer um modelo por meio do qual esses pontos possam ser identicados. Por exemplo, em AspectJ [Kiczales et al. 2001, Kiczales and Mezini 2005], que consiste em uma extenso de Java que apia a POA, o modelo de pontos de juno baseado nas construes da linguagem e os pontos so identicados por eventos do tipo: chamada ou execuo de um mtodo com um certo nome (ou padro de nome), leitura/escrita de atributos com um certo nome (ou padro de nome), entre outros. AspectJ ser utilizada nos exemplos apresentados nas prximas sees deste texto. Um conjunto de pontos de juno ou somente conjunto de juno (pointcut ) identica diversos pontos de juno em um sistema. Atravs do conjunto de juno, um comportamento transversal (crosscutting behavior ) pode ser denido nos pontos de juno identicados por ele. Esses comportamentos transversais so implementados por meio de construes similares aos mtodos chamadas de adendos (advice), que podem executar antes, depois ou no lugar dos pontos de juno identicados. Esses adendos so chamados de
26

Teste de Software OO e OA: Teoria e Prtica

adendos anteriores, posteriores e de contorno, respectivamente. Por exemplo, para implementar um interesse de registro que imprime uma mensagem toda vez que um mtodo chamado, poderia ser utilizado um aspecto com um adendo anterior que executaria toda vez que qualquer mtodo do sistema fosse chamado. Outro mecanismo importante introduzido pela POA a declarao intertipo (ou introduo). Esse tipo de declarao permite que um aspecto introduza membros (geralmente mtodos ou atributos) em algum outro tipo (classe ou interface, por exemplo). Essas introdues so utilizadas quando o interesse implementado no aspecto tambm utiliza atributos e mtodos que deveriam fazer parte dos mdulos que o aspecto afeta. Depois do cdigo-base (referente aos mdulos base) e os aspectos serem codicados, necessrio um processo de combinao (weaving ) dos mdulos em uma aplicao executvel. Assim, toda linguagem de programao orientada a aspectos deve contar tambm com um combinador (weaver ), responsvel por essa tarefa. A combinao pode ser feita em diversos momentos, dependendo da deciso de implementao tomada para cada linguagem de programao especca. Por exemplo, a combinao dinmica o processo de combinao que ocorre em tempo de execuo, enquanto que combinao esttica ocorre em tempo de compilao. Na Figura 1.4 apresentado o modelo da aplicao de telefonia mostrada anteriormente para ilustrar a POO, acrescido de uma classe e trs aspectos. A classe Timer implementa um cronmetro e utilizada no clculo do tempo de uma chamada. As funcionalidades adicionadas pelos aspectos so descritas abaixo: Timing implementa o interesse de cronometragem e responsvel por medir os tempos das conexes por cliente, iniciando e parando um cronmetro da classe Timer associado a cada conexo; Billing implementa o interesse de faturamento e responsvel por garantir que cada conexo tenha um cliente pagador o cliente que inicia a chamada e tambm que as chamadas locais, de longa distncia e de celulares devem ser cobradas com taxas diversas; TimerLog implementa um registro que imprime na tela os horrios em que um cronmetro inicia e pra. Ambos os aspectos Timing e Billing entrecortam a classe Call para marcar os tempos das ligaes e tarifar cada uma com base na durao. Eles possuem adendos posteriores que afetam os mtodos responsveis pelos incio e m das ligaes, j que esses so os pontos adequados para se marcar os tempos e atribuir as tarifas. O exemplo apresentado mostra os principais conceitos da POA. No entanto, pode-se observar que os aspectos afetam poucos pontos da aplicao. Em aplicaes maiores, em geral os aspectos afetam mais pontos, o que mais interessante do ponto de vista da moduralizao de interesses transversais. De
27

Masiero, Lemos, Ferrari e Maldonado

aspect Billing

crosscuts

crosscuts

aspect Timing

crosscuts

aspect TimerLog

* Call * Timer -startTime : long -stopTime : long -connections 1 -caller Connection Customer -receiver -name : String -phoneNumber : String -areacode : int -password : String -calls +addCall() +removeCall() +localTo() +pickup() +hangup() +merge() -isMobile : Boolean -state : int +complete() : void +drop() : void +connects() : bool *

* -caller

Local

LongDistance

-receiver

Figura 1.4. Modelo da aplicao de telefonia acrescido dos aspectos

qualquer modo, o mecanismo pode ser entendido mesmo quando um conjunto restrito de pontos afetado pelos aspectos.

AspectJ
A linguagem AspectJ uma extenso de Java criada para permitir a programao orientada a aspectos de maneira genrica, no contexto dessa linguagem. Basicamente, as novas construes do AspectJ consistem em: conjuntos de juno (pointcut) que identicam conjuntos de pontos de juno; adendos que denem o comportamento em um dado conjunto de juno; construes para afetar estaticamente a estrutura dos mdulos bsicos do programa (declaraes intertipos e que alteram a hierarquia de classes); e os aspectos (aspect) que encapsulam as construes novas e as tradicionais de uma classe Java3 . Na Figura 1.5 mostrado um exemplo de conjunto de juno nomeado. Um conjunto de juno pode ser denido como uma combinao de outros conjuntos de juno, utilizando operadores lgicos binrios e (&&) e ou (||). Alm disso, o operador unrio de negao (!) tambm pode ser usado quando no se quer capturar pontos de juno denidos por um conjunto de juno especco. O primeiro conjunto de juno que compe atualiza, por exemplo,
3

A maior parte desta seo foi baseada no livro de Laddad [Laddad 2003].

28

Teste de Software OO e OA: Teoria e Prtica

captura todas as chamadas ao mtodo move da classe ElementoDeFigura, que recebe dois inteiros como parmetros e no retorna nenhum valor.
modificador de acesso nome do conjunto de juno assinatura a casar

public pointcut atualiza():

call(public void ElementoDeFigura.move(int,int)) || call(public void ElementoDeFigura.set*(..));


operador de composio

palavra-chave para declarar um conjunto de juno

tipo do conjunto de juno

Figura 1.5. Exemplo de denio de conjunto de juno nomeado

As notaes coringa *, .. e +, denotam respectivamente: qualquer nmero de caracteres com exceo do ., qualquer nmero de caracteres incluindo o ., e qualquer subclasse ou subinterface de um dado tipo (tipo no AspectJ refere-se a uma classe, interface, tipo primitivo ou aspecto). Uma referncia completa sobre a linguagem AspectJ pode ser encontrada no website desenvolvido pelo AspectJ Team [2003]. A seguir so discutidas as aplicaes das tcnicas de teste estrutural e baseadas em defeitos no contexto de software OO e OA,enfatizando o teste de unidade. Para o teste estrutural, so abordados os critrios baseados em uxo de controle e uxo de dados, e para o teste baseado em defeitos, os critrios baseados em Anlise de Mutantes.

1.3. Teste de Software OO e OA


A POO e a POA acrescentaram novas construes e conceitos aos j conhecidos das linguagens de programao tradicionais e que, portanto, devem ser explorados em abordagens de teste adequadas para esses contextos. Neste texto so apresentadas tanto as aplicaes diretas das abordagens de teste que foram propostas para o paradigma procedimental, quanto algumas adaptaes necessrias para a aplicao de cada uma das tcnicas de teste nesses novos contextos. Particularmente, so tratadas as tcnicas estrutural (baseada em uxo de controle e de dados) e baseada em defeitos (teste de mutao). Em geral, os critrios estruturais denidos para teste de unidade do paradigma procedimental so diretamente aplicveis para o contexto de teste de unidade OO, considerando-se os mtodos como as unidades a serem testadas. No entanto, para o teste de integrao, as propriedades especcas ao paradigma OO tm maior impacto. Por exemplo, o mecanismo de acoplamento dinmico pode trazer indecidibilidade para o teste, pois pode ser impossvel
29

Masiero, Lemos, Ferrari e Maldonado

predizer em tempo de compilao qual trecho de cdigo ser executado em um determinado ponto. Como neste texto a nfase est no teste de unidade, questes envolvendo essas diculdades so discutidas em outros trabalhos [Binder 1999, Alexander and Offutt 2000, Vincenzi 2004]. Por outro lado, em POA um dos principais mecanismos que devem ser considerados nas abordagens de teste a quanticao. Quando aspectos adicionam comportamento por meio dos conjuntos de juno em diversos mdulos do programa, a estrutura original dos mdulos-base modicada aps a combinao, ou seja, novas interfaces so introduzidas dentro dos mdulos [Kiczales and Mezini 2005]. Como os mtodos afetados tm sua estrutura interna modicada, uma abordagem de teste adequada deve levar em conta e explicitar essas alteraes. Na abordagem estrutural apresentada neste captulo, o modelo de uxo de controle e de dados adaptado para explicitar os locais em que os adendos so executados nos mtodos. A partir desse modelo, critrios de teste foram concebidos para fazer com que o testador concentre-se nesses pontos, exercitando-os a partir dos casos de teste.

1.3.1. Fases do Teste de Software OO e OA


Com base nos conceitos de teste apresentados na seo anterior, pode-se considerar que em programas OO as menores unidades a serem testadas so os mtodos; e em programas OA, considerando-os como extenses de programas OO (como acontece nos escritos em AspectJ), os mtodos (inclusive os intertipo declarados) e os adendos. A classe qual o mtodo pertence pode ser vista como o driver do mtodo, pois sem ela no possvel execut-lo. Alm disso, o aspecto somado a um ponto de juno que faa com que o adendo seja executado podem ser vistos como o driver do adendo, pois sem eles no possvel executar o adendo (a no ser que haja alguma infra-estrutura especial que permita a execuo do adendo como se fosse um mtodo, o que descartaria a necessidade do ponto de juno). Alm disso, mtodos comuns e intertipo declarados pertencentes a aspectos podem tambm necessitar de infra-estrutura especial para serem executados em isolamento. Por denio, uma classe engloba um conjunto de atributos e mtodos que manipulam esses atributos; e um aspecto engloba basicamente conjuntos de atributos, mtodos, adendos e conjuntos de juno. Assim sendo, considerando uma nica classe ou um nico aspecto j possvel pensar em teste de integrao. Mtodos da mesma classe ou mesmo aspecto, bem como adendos e mtodos de um mesmo aspecto, podem interagir para desempenhar funes especcas, caracterizando uma integrao que deve ser testada. Levando em conta tais consideraes, e baseando-se no particionamento utilizado por Sommerville [2001] e na abordagem de Harrold e Rothermel [1994] , a atividade de teste de programas OO e OA poderia ser dividida nas seguintes fases [Lemos et al. 2004]: 1. Teste de Unidade: O teste de cada mtodo e adendo isoladamente, tambm chamado de teste intra-mtodo ou intra-adendo.
30

Teste de Software OO e OA: Teoria e Prtica

2. Teste de Mdulo: O teste de uma coleo de unidades dependentes unidades que interagem por meio de chamadas ou interaes com adendos. Essa fase pode ser dividida nos seguintes tipos de teste (considerando classes e aspectos como entidades diferentes): Inter-mtodo: Consiste em testar cada mtodo pblico juntamente com outros mtodos da mesma classe chamados direta ou indiretamente (chamadas indiretas so aquelas que ocorrem fora do escopo do prprio mtodo, dentro de um mtodo chamado em qualquer profundidade). Adendo-mtodo: Consiste em testar cada adendo juntamente com outros mtodos chamados por ele direta ou indiretamente. Mtodo-adendo: Consiste em testar cada mtodo pblico juntamente com os adendos que o afetam direta ou indiretamente (considerando que um adendo pode afetar outro adendo). Nesse tipo de teste no considerada a integrao dos mtodos afetados com os outros mtodos chamados por eles, nem com mtodos chamados pelos adendos. Inter-adendo: Consiste em testar cada adendo juntamente com outros adendos que o afetam direta ou indiretamente. Inter-mtodo-adendo: Consiste em testar cada mtodo pblico juntamente com os adendos que o afetam direta e indiretamente, e com mtodos chamados direta ou indiretamente por ele. Esse tipo de teste inclui os quatro primeiros tipos de teste descritos acima. Intra-classe: Consiste em testar as interaes entre os mtodos pblicos de uma classe quando chamados em diferentes seqncias, considerando ou no a interao com os aspectos. Inter-classe: Consiste em testar as interaes entre classes diferentes, considerando ou no a interao dos aspectos. 3. Teste de Sistema: A integrao de todos os mdulos, inclusive com o ambiente de execuo, forma um subsistema ou um sistema completo. Para essa fase geralmente utilizado o teste funcional.

1.3.2. Teste Estrutural de Programas OO


Neste texto, seguindo os trabalhos de Vincenzi [2004] e Vincenzi et al. [2005], a linguagem Java utilizada para demonstrar os critrios estruturais aplicados no contexto de programas OO. Mais especicamente, a idia viabilizar o teste estrutural de programas Java a partir do bytecode4 Java, permitindo tambm, com isso, o teste estrutural de componentes5 para os quais os
4

Cdigo objeto de Java que consiste em instrues similares s instrues de linguagens de montagem, porm que armazenam informaes de alto nvel das classes compiladas. Componente, nesse caso, denido como um conjunto de entidades de composio de software com interface bem denida e especicada.

31

Masiero, Lemos, Ferrari e Maldonado

cdigos-fonte nem sempre se encontram disponveis. Um modelo de uxo de dados subjacente foi denido, caracterizando as instrues de bytecode responsveis pela denio e/ou o uso de variveis. De posse do modelo de uxo de dados, um modelo de representao de programa o Grafo Denio-Uso (DU) construdo, considerando os mecanismos de tratamento de exceo, em geral, presentes nas linguagens OO. Desse modo, o grafo DU utilizado para representar o uxo de controle e o uxo de dados intra-mtodo, tanto da execuo normal do programa quanto na presena de excees. Um grafo DU de uma dada unidade u denido como um grafo dirigido DU (u) = (N , E , s, O), tal que: N representa o conjunto de ns de um grafo DU: N = {n|n corresponde a uma seqncia linear de computaes, ou bloco de instrues, de u}; E N N = Er Ee o conjunto completo de arestas do DU. Cada aresta e E representa a transferncia de controle que pode ocorrer entre dois ns, tal que: Er e Ee correspondem a dois subconjuntos disjuntos de arestas regulares e de exceo, respectivamente: Er o conjunto de arestas regulares denido como Er = {(ni , n j )| o bloco de instrues em n j pode ser executado imediatamente aps o bloco de instrues em ni e (ni , n j ) / Ee }. Ee o conjunto de arestas de exceo denido como Ee = {(ni , n j )| as instrues de ni esto no escopo de um tratador de exceo que inicia em n j }; s N o n de entrada de u. s o nico n do grafo que no possui nenhuma aresta de entrada; O N o conjunto (possivelmente vazio) de ns de sada. Ou seja, cada o O no possui nenhuma aresta de sada. Para cada DU construdo a partir do bytecode Java, as componentes do grafo so representadas da seguinte forma: Ns regulares so representados por crculos com o rtulo contendo a primeira instruo de bytecode do bloco; Ns de chamada so representados por crculos duplos; Ns de sada so representados por ns negritados; Arestas de exceo so representadas por arestas tracejadas, representando o uxo de controle do ponto onde uma exceo gerada at o primeiro n correspondente ao tratador daquela exceo. Na Figura 1.6 mostrada parte do cdigo-fonte da aplicao de telefonia discutida anteriormente nas sees 1.2.1 e 1.2.2. O construtor da classe Call (linhas 518) ser utilizado para demonstrar o DU. A lgica envolvida no construtor consiste em checar se a chamada de longa distncia ou local e instanciar um objeto Connection correspondente. Na Figura 1.7 mostrada parte
32

Teste de Software OO e OA: Teoria e Prtica

do bytecode gerado para o construtor da classe Call e o DU correspondente (porm sem informaes de uxo de dados).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Call { private Customer caller, receiver; private Vector connections = new Vector(); public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; if (receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); } public void pickup() { ... } public boolean isConnected(){ ... } public void hangup(Customer c) { for(Enumeration e = connections.elements(); e.hasMoreElements();) { ((Connection)e.nextElement()).drop(); } } public boolean includes(Customer c){ ... } public void merge(Call other){ ... } }

Figura 1.6. Parte do cdigo da classe Call da aplicao de telefonia

0: 1: 4: ... 30: 33: ... 48: 60: ...

aload_0 invokespecial java.lang.Object.<init> ()V (15) aload_0 ifeq new new aload_0 #48 <telecom.Local> (32) <telecom.LongDistance> (36)

Figura 1.7. Parte do bytecode e DU do construtor da classe Call


Uma vez que o grafo DU de cada mtodo tenha sido obtido, critrios po33

Masiero, Lemos, Ferrari e Maldonado

dem ser denidos para derivar diferentes requisitos de teste, os quais podem ser utilizados tanto para avaliar a qualidade de um determinado conjunto de teste quanto para a prpria gerao de dados de teste. Ao todo, oito critrios de teste estruturais foram denidos. Vincenzi [Vincenzi 2004] optou por separar os requisitos de teste em dois conjuntos disjuntos: (1) os que podem ser cobertos durante a execuo normal do programa, denominados independentes de exceo; e (2) os que para serem cobertos exigem, obrigatoriamente, que uma exceo tenha sido lanada, denominados dependentes de exceo. Desse modo, foram estabelecidos os seguintes critrios: Todos-Ns: Todos-Ns-Independentes-de-Exceo (Todos-Nsei ): Requer que cada n n N do grafo DU que alcanvel por pelo menos um caminho livre de exceo, ou seja, caminhos que no incluam arestas de exceo, seja exercitado por algum caso de teste. Todos-Ns-Dependentes-de-Exceo (Todos-Nsed ): Requer que cada n n N do grafo DU que no alcanvel por pelo menos um caminho livre de exceo seja exercitado por algum caso de teste. Todas-Arestas: Todas-Arestas-Independentes-de-Exceo (Todas-Arestasei ): Requer que cada aresta e E do grafo DU que alcanvel por pelo menos um caminho livre de exceo seja exercitada por algum caso de teste. Todas-Arestas-Dependentes-de-Exceo (Todas-Arestased ): Requer que cada aresta e E do grafo DU que no alcanvel por pelo menos um caminho livre de exceo seja exercitada por algum caso de teste. Todos-Usos: Todos-Usos-Independentes-de-Exceo (Todos-Usosei ): Requer que seja exercitado pelo menos um caminho livre de exceo e livre de denio para uma varivel denida em um n n para todo n e toda aresta que possui um uso da mesma varivel e que possa ser alcanada a partir de n. Todos-Usos-Dependentes-de-Exceo (Todos-Usosed ): Requer que seja exercitado pelo menos um caminho que no seja livre de exceo mas que seja livre de denio para uma varivel denida em um n n para todo n e toda aresta que possui um uso da mesma varivel e que possa ser alcanada a partir de n. Todos-Potenciais-Usos [Maldonado 1991]: Todos-Pot-Usos-Independentes-de-Exceo (Todos-Pot-Usosei ): Requer que seja exercitado pelo menos um caminho livre de exceo e livre de denio de uma varivel denida em um n n para todo n e toda aresta possvel de ser alcanada a partir de n.
34

Teste de Software OO e OA: Teoria e Prtica

Todos-Pot-Usos-Dependentes-de-Exceo (Todos-Pot-Usosed ): Requer que seja exercitado pelo menos um caminho que no seja livre de denio mas que seja livre de denio para varivel denida em um n n para todo n e toda aresta possvel de ser alcanada a partir de n. Apesar de se ter evidncias da viabilidade prtica da aplicao desses critrios no teste intra-mtodo, estudos experimentais e tericos ainda so necessrios para avaliar detalhadamente os resultados [Vincenzi 2004]. Alm disso, o teste estrutural por si s apresenta algumas limitaes em sua aplicao como, por exemplo, o problema da no-executabilidade, que acontece quando no existem dados de teste que faam com que requisitos de teste em particular sejam satisfeitos [Zhu et al. 1997]. Em particular o custo, eccia e diculdade de satisfao entre os critrios so propriedades interessantes para serem investigadas nesse contexto. A ferramenta JaBUTi que implementa os critrios apresentados aqui, e ser vista na Seo 1.4, pode auxiliar na conduo de tais estudos, j que fornece uma maneira automatizada de aplicao do teste estrutural.

Teste Estrutural de Integrao OO


At o presente momento, poucos trabalhos abordam o teste estrutural de integrao no contexto de programas OO. Entretanto, alguns esforos podem ser observados nessa direo [Harrold and Rothermel 1994, Alexander and Offutt 2000]. Em particular, pode ser destacado o trabalho de Harrold e Rothermel [1994], que adapta o teste de uxo de dados para classes. Assim como so testados os procedimentos de forma isolada (teste intraprocedimental) e a interao entre eles (teste interprocedimental), a mesma idia pode ser aplicada aos mtodos isolados de uma classe (teste intramtodo abordado anteriormente), e aos mtodos de uma classe que interagem entre si (teste inter-mtodo). Alm disso, no paradigma OO devem ser consideradas tambm as interaes de uxo de dados que acontecem quando usurios de uma classe invocam seqncias de mtodos de maneira arbitrria [Harrold and Rothermel 1994]. Por exemplo, em uma classe D, com atributo a e mtodos m1 , m2 , m3 , deve ser analisado se as diferentes seqncias de chamadas (por exemplo: m2 , m1 , m3 , m2 ) no provocam nenhum estado inconsistente na classe D com relao ao atributo a. Harrold e Rothermel consideram ainda o teste de uxo de dados na integrao das classes, que corresponde a um quarto nvel de teste, o inter-classe. Esse tipo de teste envolve os pares def-uso em que a denio de uma varivel se encontra em uma classe e o uso em outra. Detalhes sobre o teste de integrao no so tratados aqui, j que neste texto dada nfase ao teste de unidade. No entanto cabe destacar que esses problemas so objetos dos trabalhos em andamento de alguns grupos de pesquisas em Engenharia de Software. Assim como na POO, para que os critrios estruturais possam ser aplicados
35

Masiero, Lemos, Ferrari e Maldonado

na POA, necessrio que sejam denidos os grafos de uxo de controle e de dados adequados. A seguir denem-se genericamente os grafos de uxo para programas OA e sua instanciao para a linguagem AspectJ, seguido da denio dos critrios baseados nesses modelos.

1.3.3. Teste Estrutural de Programas OA


No uxo de controle de programas OA, quando os aspectos denem comportamento em algum ponto do sistema por meio dos adendos, pode ser detectado um novo tipo de interao. Quando o ponto de juno alcanado, o uxo de controle passado para o adendo do aspecto que afeta aquele ponto, retornando ao nal da execuo. Esse tipo de interao pode ser comparada com uma chamada a mtodo, na qual o uxo de controle passado para o mtodo chamado, retornando ao nal de sua execuo. Porm, a dependncia inversa, pois no primeiro caso o desenvolvedor do mtodo insere explicitamente a chamada ao mtodo para atender os propsitos do mtodo em programao, enquanto que no segundo caso so os aspectos os responsveis por denir o comportamento extra em pontos de juno especcos. Como comentado por Kiczales & Mezini [Kiczales and Mezini 2005], a composio de programas OA define o surgimento de novas interfaces em diversos mdulos do sistema. Dessa forma, esses novos elementos estruturais devem ser levados em conta durante o teste estrutural desses programas. O Grafo de Fluxo de Controle Orientado a Aspectos (AOCFG) um grafo denido com o objetivo de apoiar o teste de unidade de um programa OA [Lemos 2005]. Como possvel que em uma linguagem OA os adendos sejam afetados por outros adendos como, por exemplo, em AspectJ o AOCFG pode ser usado para representar tanto mtodos quanto adendos. Um grafo AOCFG de uma dada unidade u tem as mesmas componentes do DU denido para programas OO mais a componente C, ou seja, AOCFG (u) = (N , E , C, s, O). A componente C denida como segue: C N o conjunto (possivelmente vazio) de ns transversais que representam um n no qual ocorre uma interao com um adendo de um dado aspecto. Esses ns representam as interfaces adicionadas pelos aspectos para que os adendos sejam executados; O Grafo Def-Uso Orientado a Aspectos (AODU) o AOCFG com informaes de denies e usos de variveis, para a aplicao de critrios baseados em uxo de dados. Como o grafo AODU um AOCFG estendido, necessria apenas a construo do AODU para se derivar requisitos de teste tanto para o uxo de controle quanto para o uxo de dados do programa. Neste texto o grafo AODU denido a partir do bytecode Java produto da compilao/combinao de programas escritos na linguagem AspectJ, seguindo o trabalho de Vincenzi et al. [Vincenzi et al. 2005], como discutido na seo anterior. A representao grca do AODU semelhante do DU apresentado na seo anterior, porm, neste caso existe um tipo de n extra: Ns transversais so representados por elipses tracejadas, com infor36

Teste de Software OO e OA: Teoria e Prtica

mao 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 interao com um adendo posterior de um aspecto Aspect em um certo ponto, o n transversal correspondente adicionado com a informao a f ter Aspect .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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) returning () : target(c) && call(void Connection.complete()) { getTimer(c).start(); } pointcut endTiming(Connection c): target(c) && call(void Connection.drop()); after(Connection c) returning () : endTiming(c) { getTimer(c).stop(); c.getCaller().totalConnectTime += getTimer(c).getTime(); c.getReceiver().totalConnectTime += getTimer(c).getTime(); } } public aspect Billing { declare precedence: Billing, Timing; public static final long LOCAL_RATE = 3; public static final long LONG_DISTANCE_RATE = 10; public static final long MOBILE_LD_RECEIVER_RATE = 5; public Customer Connection.payer; public Customer getPayer(Connection conn) { return conn.payer; } 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 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) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf(). getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge (receiverCost); } } 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 1.8. Cdigo dos aspectos Timing e Billing da aplicao de telefonia


Na Figura 1.8 mostrada outra parte do cdigo-fonte da aplicao de telefonia apresentada nas Sees 1.2.1 e 1.2.2, referente aos aspectos Timing e Billing. O mesmo construtor da classe Call (Figura 1.6), utilizado na seo anterior para mostrar o DU, utilizado para demonstrar o AODU. Como o aspecto Billing possui um adendo para atribuir a tarifa da chamada ao cliente no momento da instanciao de alguma classe lha de Connection (linhas 4548), o construtor de Call afetado por esse adendo nos dois pontos em que as instncias de Local (linha 12 na Figura 1.6) e LongDistance (linhas 1415 na Figura 1.6) so criadas. Na Figura 1.9 mostrada parte do bytecode
37

Masiero, Lemos, Ferrari e Maldonado

gerado para o construtor da classe Call e o AODU correspondente (porm sem informaes de uxo de dados).
0 aload_0 1 invokespecial #15 <Method Object()> 4 aload_0 ... 27 invokevirtual #30 <Method boolean localTo(telecom.Customer)> 30 ifeq 78 33 aload_1 ... 52 invokespecial #34 <Method Local(telecom.Customer, telecom.Customer, boolean)> ... 69 invokevirtual #110 <Method void ajc$afterReturning$telecom_Billing$ 1$8a338795( telecom.Customer, telecom.Customer, boolean, telecom.Connection)> 72 nop 73 astore 4 75 goto 120 78 aload_1 ... 97 invokespecial #37 <Method LongDistance(telecom.Customer, telecom.Customer, boolean)> ... 114 invokevirtual #110 <Method void ajc$afterReturning$telecom_Billing$ 1$8a338795( telecom.Customer, telecom.Customer, boolean, telecom.Connection)> 117 nop 118 astore 4 120 aload_0 121 getfield #20 <Field java.util.Vector connections> 124 aload 4 126 invokevirtual #41 <Method void addElement(java.lang.Object)> 129 return

Figura 1.9. Bytecode e AODU do construtor da classe Call


Com base no AODU, trs critrios de teste estruturais foram denidos [Lemos 2005, Lemos et al. 2005]: Todos-Ns-Transversais (Todos-Nsc ): Requer que cada n c C seja exercitado por pelo menos um caso de teste. Ou seja, este critrio exige que cada execuo de adendo que ocorre na unidade afetada seja alcanada. Todas-Arestas-Transversais (Todas-Arestasc ): Requer que cada aresta transversal, ou seja, cada aresta que tem como n origem ou destino um n transversal, seja exercitada por pelo menos um caso de teste. Todos-Usos-Transversais (Todos-Usosc ): Requer que cada par def-uso cujo uso est em um n transversal seja exercitado por pelo menos um caso de teste. Os critrios denidos anteriormente para a POO continuam sendo vlidos tambm nesse contexto pois, no caso da linguagem AspectJ, programas OA so superconjuntos de programas OO. Assim, tambm se torna interessante denir uma estratgia de aplicao desses critrios, baseada na diculdade
38

Teste de Software OO e OA: Teoria e Prtica

de satisfao dos critrios e em outras propriedades. Porm, at o presente momento, no se observam investigaes nesse sentido, o que indica uma necessidade de trabalhos futuros. Alm disso, importante salientar que os mesmos problemas encontrados anteriormente para a POO continuam valendo para a POA (como, por exemplo, o problema da no-executabilidade).

Teste Estrutural de Integrao OA


Os mesmos problemas encontrados no teste de integrao de programas OO tambm so encontrados no contexto de software OA. Alm disso, com a introduo dos aspectos como mdulos de programao, novas questes devem ser investigadas no que diz respeito integrao interna desses mdulos (por exemplo, adendos de um aspecto que interagem com mtodos do mesmo aspecto); integrao com as classes; e integrao de aspectos com aspectos (quando esses interagem em pontos de juno comuns). Considerando um programa OA, um defeito em particular pode estar localizado em trs locais diferentes: na lgica de um adendo; na lgica dos mdulos base; ou na lgica de composio (no caso deste texto no conjunto de juno), que dene onde os adendos iro executar [Alexander 2003]. Como um adendo uma construo similar a mtodo, e o programa-base no geral composto de classes, poucas adaptaes so necessrias para a aplicao do teste estrutural nessas unidades, como visto anteriormente. Entretanto, nota-se uma necessidade maior de adaptaes no que diz respeito ao teste da lgica de composio (ou dos conjuntos de juno), para o teste ecaz das regras de composio, procurando identicar defeitos no que diz respeito seleo dos pontos de juno. Detalhes sobre o teste de integrao OA no so tratados aqui, j que neste texto dada nfase ao teste de unidade. No entanto, na Seo 1.3.5 algumas outras questes gerais so discutidas, sob a tica do teste de mutao. A seguir so discutidas as adaptaes do teste de mutao para sua aplicao nos contextos de software OO e OA.

1.3.4. Teste de Mutao de Programas OO


As linguagens de programao possuem caractersticas prprias que implicam na denio dos operadores de mutao, embora os mesmos conceitos possam ser aplicados independentemente da linguagem na qual o produto a ser testado esteja escrito [Delamaro et al. 2001]. Por outro lado, linguagens com construes sintticas semelhantes possibilitam que operadores de mutao possam ser aplicados indistintamente em produtos gerados nessas linguagens, ou requerem alteraes simples na descrio dos operadores. Por exemplo, as linguagens C e C++ permitem a declarao de variveis globais, porm o conceito de varivel global no existe na linguagem Java. Portanto, a princpio, operadores de mutao para programas C que fazem alteraes relacionadas a esses elementos so aplicveis a programas C++, mas no a programas Java. Por outro lado, tanto em Java quanto em C++, os atributos de
39

Masiero, Lemos, Ferrari e Maldonado

uma classe podem ser considerados como variveis globais, pois so visveis para todos os mtodos pertencentes classe. Dessa forma, o conjunto de variveis globais na aplicao de teste de mutao em programas Java pode ser restringido ao conjunto dos atributos da classe em teste. Para o teste de mutao de programas OO, escritos em C++ e Java, Vincenzi [2004] avaliou a aplicabilidade de conjuntos de operadores de mutao denidos originalmente para serem aplicados em teste de mutao de programas C [Agrawal et al. 1989, Delamaro 1997, Delamaro et al. 2001] e Java [Ma et al. 2002]. Vincenzi procurou adaptar a aplicao destes operadores para os nveis de teste intra-mtodo, inter-mtodo e inter-classe. Cada um dos conjuntos foi avaliado e, quando necessrio, foram propostas as adaptaes necessrias. Vale ressaltar que cada linguagem possui particularidades. Sendo assim, dependendo do tipo de defeito que se deseja enfatizar no teste, novos operadores de mutao podem ser denidos para model-los. Por exemplo, em alguns pontos, a linguagem C++ pode ser considerada mais exvel do que Java pela possibilidade de se denir heranas mltiplas e sobrecargas de operadores. Novos operadores de mutao poderiam ser propostos para tratar exclusivamente esses defeitos. A aplicao do teste de mutao de programas OO nos nveis de unidade e integrao discutida a seguir. Os conjuntos de operadores para cada fase de teste so apresentados, inclusive suas aplicabilidades em programas Java e C++.

Teste de Mutao de Unidade (Teste Intra-Mtodo)


Agrawal et. al [1989] deniram um conjunto de 80 operadores de mutao para teste de unidade de programas escritos na linguagem C. Os operadores so distribudos em quatro classes: cinco operadores para mutao de constantes, 16 operadores para mutao de comandos, 12 operadores para mutao de variveis e 47 operadores para mutao de operadores6 . Cada classe de operadores formada por um conjunto de operadores que modelam diferentes defeitos em estruturas de um mesmo tipo. Por exemplo, o operador SWDD (while Replacement by do-while), que pertence classe de operadores para mutao de comandos, substitui o comando de iterao while pelo comando do-while. A letra inicial do nome de cada operador possibilita a identicao da classe qual esse operador pertence, sendo elas C constante, S comando (statement ), V varivel e O operador. As demais letras so utilizadas para descrever o operador, como no exemplo apresentado. Analisando a aplicabilidade dos 80 operadores para as linguagens C++ e
6

Neste texto, o termo operador utilizado tanto para se referir a um operador de mutao quanto a um operador tradicional de uma linguagem de programao (por exemplo, operadores aritmticos ou lgicos). Se a utilizao do termo causar ambigidade, o termo completo (por exemplo, operador de mutao ou operador aritmtico) ser utilizado.

40

Teste de Software OO e OA: Teoria e Prtica

Java, Vincenzi [2004] concluiu que todos so aplicveis a C++, por se tratar de um superconjunto da linguagem C, e 59 so aplicveis a Java. Dentre os operadores no aplicveis a Java, esto um operador que trata do comando goto, cinco operadores que so especcos para mutao de ponteiros e registros (struct), e 15 operadores de mutao de operadores. Vincenzi tambm deniu sete novos operadores para tratar de caractersticas especcas da linguagem Java, sendo que trs desses tambm so aplicveis a C++. Os quatro novos operadores exclusivos para Java foram denidos para tratar dos comandos break e continue, enquanto os trs aplicveis tanto a Java quanto C++ foram denidos para tratar de variveis de referncia. A descrio desses operadores apresentada na Tabela 1.1. A descrio dos demais operadores pode ser encontrada no trabalho de Agrawal et al. [1989].

Tabela 1.1. Novos operadores de mutao de unidade de programas OO


Operador
VGCR VLCR VCAR SBLR SCLR SLBC SLCB

Descrio
Troca uma referncia de classe por todas as demais referncias de classe globais do mesmo tipo. Troca uma referncia de classe por todas as demais referncias de classe locais do mesmo tipo. Troca as referncias a um atributo da classe por todas as outras referncias a atributos do mesmo tipo. Troca os comandos break, com ou sem rtulos, pelos demais comandos break e break rotulados vlidos da unidade. Troca os comandos continue, com ou sem rtulos, pelos demais comandos continue e continue rotulados vlidos da unidade. Troca os rtulos dos comandos break pelos rtulos dos comandos continue vlidos da unidade. Troca os rtulos dos comandos continue pelos rtulos dos comandos break vlidos da unidade.

O conjunto nal de operadores cou totalizado em 66 operadores para Java e 83 operadores para C++. Na Tabela 1.2 (adaptada do trabalho de Vincenzi [2004]) est sumarizado o conjunto resultante de operadores. A primeira coluna da tabela contm as siglas que representam o nome dos operadores. O smbolo indica que esse operador no estava presente no conjunto inicial. A segunda e terceira coluna so utilizadas para indicar se o operador aplic vel para as linguagens Java e C++, respectivamente. O smbolo signica que o operador aplicvel para a respectiva linguagem, e signica que o operador no aplicvel. Ainda, signica que o operador no diretamente aplicvel, mas foi adaptado gerando um ou mais dos novos operadores propostos por Vincenzi. Na Figura 1.10 apresentado um exemplo de aplicao de um operador de mutao em um mtodo de um programa Java. O mtodo merge utilizado nesse exemplo foi obtido do classe Call, que compe a aplicao de telefonia introduzida na seo 1.2.1. O operador utilizado o operador SMVB (Move Brace Up or Down), que move o } que encerra um bloco de comandos de
41

Masiero, Lemos, Ferrari e Maldonado

Tabela 1.2. Operadores de mutao de unidade


Operador
CGCR CLCR CGSR CLSR CRCR Total 5/5 5/5 (a) Mutao de Constantes

Java

C++ Operador
OAAA OAAN OABA OABN OAEA OALN OARN OASA OASN OBAA OBAN OBBA OBBN OBEA OBLN OBNG OBRN OBSA OBSN OCNG OCOR OEAA OEBA OESA OIPM OLAN OLBN OLLN OLNG OLRN OLSN OMMO OPPO ORAN ORBN ORLN ORRN ORSN OSAA OSAN OSBA OSBN OSEA OSLN OSRN OSSA OSSN Total 32/47 47/47 (d) Mutao de Operadores

Java

C++

Operador

VASM VDTR VGAR VGPR VGCR VGSR VGTR VLAR VLPR VLCR VLSR VLTR VSCR VCAR VTWD Total 10/15 15/15 (b) Mutao de Variveis

Java

C++

Operador

SBRC SBRn SCRB SCRn SDWD SGLR SBLR SCLR SLBC SLCB SMTC SMTT SMVB SRSR SSDL SSOM SSWM STRI STRP SWDD Total 19/20 16/20 (c) Mutao de Comandos

Java

C++

42

Teste de Software OO e OA: Teoria e Prtica

um lao para cima e para baixo, excluindo ou incluindo comandos no bloco. O cdigo original do adendo apresentado na Figura 1.11(a), e os mutantes gerados so apresentados nas Figuras 1.11(b), 1.11(c) e 1.11(d).

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); } }

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); }

(a) Mtodo Original


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); }

(b) Mutante 1
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); }

(c) Mutante 2

(d) Mutante 3

Figura 1.10. Exemplo de mutao de um mtodo escrito em Java


Na Seo 1.4.3 apresentado um exemplo de aplicao do teste de mutao de unidade em programas OO, inclusive com os casos de teste que podem ser utilizados para matar os mutantes gerados e a identicao de possveis mutantes equivalentes.

Teste de Mutao de Integrao (Teste inter-mtodo e inter-classe)


Vincenzi [2004] adaptou o critrio Mutao de Interface [Delamaro et al. 2001] para o teste de mutao inter-mtodo. Conforme apresentado na Seo 1.2, a principal diferena entre os operadores de mutao de unidade e mutao de interface est nos pontos do cdigo que so enfatizados para serem mutados. Os operadores de mutao de interface esto relacionados aos pontos de comunicao entre duas unidades. Considerando duas unidades u1 e u2 , com u1 chamando u2 , as mutaes so realizadas tanto nos pontos em que u1 faz chamadas a u2 quanto nos pontos relacionados interface de u2 como, por exemplo, variveis de retorno de u2 e variveis globais. Os operadores foram agrupados de acordo com o local de aplicao, podendo ser tanto na unidade chamadora quanto na unidade chamada. Operadores do Grupo I, num total de 24, so aplicados em u2 . Operadores do Grupo II so aplicados em u1 , totalizando nove operadores nesse grupo. A descrio desses operadores pode ser encontrada no trabalho de Delamaro [1997] e Delamaro et al. [2001a]. Os operadores de mutao de interface foram originalmente denidos para
43

Masiero, Lemos, Ferrari e Maldonado

o teste de programas C. Assim, Vincenzi [2004] analisou sua aplicabilidade para o teste de programas OO escritos nas linguagens C++ e Java. De acordo com a anlise, todos os operadores so aplicveis para as duas linguagens, bastando redenir os conjuntos de variveis envolvidos na aplicao dos operadores. Ainda no contexto de teste de integrao, Ma et al. [2002] deniram um conjunto de operadores de mutao para o teste inter-classe. Esses operadores foram projetados para tratar de defeitos especcos aos novos conceitos e construes introduzidos pelo paradigma OO, entre eles encapsulamento, herana e polimorsmo. O conjunto, composto por 24 operadores, foi elaborado com base em taxonomias de defeitos apresentadas nos trabalhos de Kim et al. [2000], Offutt et al. [2001] e Chevalley e Thvenod-Fosse [2003]. Alm de apresentarem uma taxonomia de defeitos, Kim et al. e Chevalley e ThvenodFosse propuseram tambm operadores de mutao para modelar os defeitos presentes em suas taxonomias. Ressalta-se que os defeitos enfatizados por Kim et al. contemplam defeitos de programas OO em geral, embora tenham sido baseados na linguagem Java. J os defeitos enfatizados por Chevalley e Thvenod-Fosse referem-se exclusivamente a programas escritos em Java. Esses operadores foram includos no trabalho de Ma et al., que estenderam o conjunto denindo novos operadores, alm de redenir alguns j existentes. Os 24 operadores resultantes foram agrupados de acordo com as caractersticas e conceitos do paradigma OO e os defeitos relacionados, sendo eles: ocultamento de informao (um operador); herana (sete operadores); polimorsmo (quatro operadores); sobrecarga de mtodos (quatro operadores); caractersticas especcas de Java (quatro operadores); e enganos comuns de programao (quatro operadores). A descrio desses operadores pode ser encontrada no trabalho de Ma et al. [2002]. Analisando-se a aplicabilidade desses grupos de operadores em programas implementados em C++ e Java, Vincenzi [2004] constatou que apenas o grupo de operadores que tratam de caractersticas especcas da linguagem Java no aplicvel para programas escritos em C++. O restante dos operadores aplicvel tanto a C++ quanto a Java.

1.3.5. Teste de Mutao de Programas OA


Conforme enfatizado na seo 1.2, alm dos conceitos do teste de mutao poderem ser aplicados em tipos variados de artefatos conceitualmente executveis de software como, por exemplo, cdigo-fonte de programas e especicaes formais, as similaridades entre diferentes linguagens propiciam a reutilizao de operadores de mutao indistintamente entre as linguagens ou com pequenas alteraes entre suas implementaes. A aplicao do teste de mutao de programas OA nos nveis de unidade e integrao discutida a seguir. A linguagem utilizada como base a AspectJ, embora os conceitos possam ser aplicados a programas escritos em outras linguagens OA.
44

Teste de Software OO e OA: Teoria e Prtica

Teste de Mutao de Unidade OA


Embora a linguagem AspectJ introduza novas construes que implementam os conceitos relacionados orientao a aspectos, as unidades de cdigo executveis implementadas nos aspectos (adendos, mtodos e mtodos intertipo declarados) so escritas em cdigo Java nativo. Dessa forma, os operadores de mutao de unidade de programas Java, apresentados na seo 1.3.4, podem ser diretamente aplicados em unidades de programas AspectJ. Para efeito de ilustrao, na Figura 1.11 apresentado um exemplo de aplicao de um operador de mutao em um adendo escrito em AspectJ. O cdigo utilizado nesse exemplo foi obtido do aspecto Timing, que compe a aplicao de telefonia introduzida nas sees anteriores deste texto. O operador utilizado o operador OAAA, que realiza a troca de cada operador de atribuio aritmtica pelos demais operadores e atribuio aritmtica disponveis na linguagem. No caso da linguagem Java, os operadores de atribuio aritmtica disponveis so +=, -=, *=, /= e %=. O cdigo original do adendo apresentado na Figura 1.11(a), e os mutantes gerados so apresentados na Figura 1.11(b) (somente a linha alterada apresentada para cada mutante gerado).

1 2 3 4 5 6 7 8 9 10 11

public aspect Timing { ... after(Connection c) returning () : endTiming(c) { getTimer(c).stop(); c.getCaller().totalConnectTime += getTimer(c).getTime(); c.getReceiver().totalConnectTime += getTimer(c).getTime(); } }

linha 6: c.getCaller().totalConnectTime c.getCaller().totalConnectTime c.getCaller().totalConnectTime c.getCaller().totalConnectTime

-= *= /= %=

linha 8: c.getReceiver().totalConnectTime c.getReceiver().totalConnectTime c.getReceiver().totalConnectTime c.getReceiver().totalConnectTime

-= *= /= %=

(a) Adendo Original

(b) Mutantes Gerados

Figura 1.11. Exemplo de mutao de um adendo escrito em AspectJ


Na Seo 1.4.4 apresentado um exemplo de aplicao do teste de mutao de unidade em programas OA, inclusive com os casos de teste que podem ser utilizados para matar os mutantes gerados e a identicao de possveis mutantes equivalentes.

Teste de Mutao de Integrao OA


Em relao ao teste de integrao, novas questes esto envolvidas. Mais especicamente, essas questes esto relacionadas s construes introduzidas pelo POA, at ento no presentes no teste de programas desenvolvidos sob os paradigmas procedimental e OO. Critrios denidos para o teste de integrao, tanto para teste estrutural [Linnenkugel and Mllerburg 1990, Vilela et al. 1999] quanto para o teste de
45

Masiero, Lemos, Ferrari e Maldonado

mutao [Delamaro et al. 2001], enfatizam os pontos de comunicao entre duas unidades e as variveis envolvidas nessa comunicao. As interfaces entre essas unidades, no caso de programas procedimentais ou OO, so bem denidas, e os pontos de comunicao (por exemplo, uma chamada a um mtodo e o retorno aps sua execuo) so explcitos no cdigo. J no caso de programas OA, os conceitos de quanticao e de inconscincia [Filman and Friedman 2000] possibilitam a execuo dos comportamentos transversais (implementados em aspectos) em diversos pontos da execuo do programa, sem a necessidade da insero de chamadas explcitas a esses comportamentos. O prprio uxo de execuo do programa pode ser completamente alterado. A aplicao de um operador de mutao especco para atuar nos elementos que denem um conjunto de juno pode aumentar ou restringir a abrangncia desse conjunto, podendo implicitamente denir novas interfaces entre as unidades do programa, ou remover uma ou mais interfaces anteriormente estabelecidas. Nesse contexto, o teste de integrao pode ser observado sob dois pontos de vista. O primeiro como sendo uma adaptao do critrio Mutao de Interface [Delamaro et al. 2001], de forma a enfatizar os pontos de comunicao entre as unidades nos diversos tipos de interao apresentados na seo 1.3.1. A interface de um adendo, por exemplo, pode ser caracterizada pelos argumentos e objetos capturados pelo ponto de juno relacionado a esse adendo, e os operadores denidos para aplicao do critrio poderiam ser aplicados nesses pontos, alm dos pontos j denidos originalmente no critrio. O segundo, por sua vez, relaciona-se com outros conceitos e construes introduzidos pela orientao a aspectos, por exemplo, conjuntos de juno e precedncia de aspectos. Existe ainda a necessidade de se denir um conjunto de operadores de mutao especco para atuar sobre esses elementos. Nessa linha, uma proposta inicial foi apresentada por Mortensen e Alexander [2004], envolvendo a aplicao de uma abordagem mista de teste estrutural e de mutao para programas OA escritos em AspectJ, tentando mapear os tipos de defeitos enfatizados taxonomia de defeitos proposta por Alexander et al. [2004]. Especicamente em relao ao teste de mutao, so denidos trs operadores de mutao que atuam: na denio de conjuntos de juno; aumentando ou reduzindo sua abrangncia; e na precedncia de aspectos. No entanto, somente uma denio de alto-nvel dos operadores apresentada, no entrando em detalhes de como esses operadores podem ser aplicados. Ressalta-se ainda que essas so idias iniciais, existindo a necessidade de uma formalizao, de modo a possibilitar a aplicao sistemtica do critrio e o desenvolvimento de mecanismos de apoio automatizado. A seguir so apresentados exemplos da aplicao dos critrios discutidos no decorrer deste texto. Para os critrios estruturais, so discutidas as ferramentas JaBUTi (OO) e JaBUTi/AJ (OA), incluindo uma breve apresentao de seus aspectos operacionais. Apresenta-se tambm uma aplicao prtica do teste de mutao em programas OO e OA.
46

Teste de Software OO e OA: Teoria e Prtica

1.4. Automatizao e Exemplos de Aplicao


A atividade de teste, se realizada manualmente, geralmente propensa a erros e limitada a aplicaes de pequeno porte. Nesse contexto, ferramentas de teste podem auxiliar na automatizao dessa tarefa, permitindo a aplicao prtica de critrios de teste, o teste de programas maiores e mais complexos, o apoio a estudos experimentais e a transferncia das tecnologias de teste para a indstria. Alm disso as ferramentas de teste possibilitam a realizao de testes de regresso, utilizados quando evolues so feitas no programa e os casos de teste armazenados so executados novamente para a validao da aplicao modicada [Domingues 2002]. Para o teste de programas OO em C++ e Java, existem vrias ferramentas e, entre elas, podem ser citadas: C++ Test, JProbe Suite, JTest, Panorama C/C++, Panorama for Java e xSuds Toolsuite. A maioria delas apia o teste estrutural e algumas delas apiam o teste funcional, alm de apresentarem algumas outras funcionalidades de anlise do programa em teste [Domingues 2002]. Nesta seo so utilizadas as ferramentas JaBUTi e JaBUTi/AJ , construdas para apoiar o teste estrutural de unidade de programas OO e OA, para demonstrar a automatizao da atividade de teste utilizando critrios estruturais. Em seguida apresenta-se a aplicao do teste de mutao de unidade em programas OO e OA com o auxlio de exemplos.

1.4.1. A Ferramenta JaBUTi


A ferramenta JaBUTi (Java Bytecode Understanding and Testing ) [Vincenzi 2004, Vincenzi et al. 2005], desenvolvida no ICMC/USP em colaborao com a Fundao Eurpedes Soares da Rocha (UNIVEM) de Marlia/SP e a Universidade do Texas em Dallas/USA, visa a ser um ambiente completo para o entendimento e teste de programas e componentes Java. A idia bsica da ferramenta viabilizar o teste de programas Java em nvel de bytecode, possibilitando, com isso, no somente o teste de programas Java para os quais o cdigo-fonte esteja disponvel, mas tambm o teste de componentes Java. JaBUTi fornece ao testador diferentes critrios de teste estruturais para a anlise de cobertura, um conjunto de mtricas estticas para se avaliar a complexidade das classes que compem do programa/componente, e implementa, ainda, algumas heursticas de particionamento de programas que visam a auxiliar a localizao de defeitos. Neste texto, dada nfase parte responsvel pela anlise de cobertura. Mais informaes sobre as demais funcionalidades da ferramenta podem ser obtidas no trabalho de Vincenzi [2004]. Considerando o suporte anlise de cobertura de programas Java, a ferramenta implementa atualmente oito critrios de teste intra-mtodos denidos por Vincenzi [2004] , sendo quatro critrios de Fluxo de Controle (Todos-Nsei , Todos-Nsed , Todas-Arestasei ,Todas-Arestased ) e quatro critrios de Fluxo de Dados (Todos-Usosei , Todos-Usosed , Todos-Pot-Usosei e Todos-Pot-Usosed ). No que diz respeito a esses critrios, estudos demonstram que, para
47

Masiero, Lemos, Ferrari e Maldonado

Figura 1.12. Gerenciador de projetos de teste da JaBUTi


programas C, conjuntos de teste que determinam as maiores coberturas tm uma maior probabilidade de detectar defeitos no programa em teste [Wong and Mathur 1995]. O mesmo se aplica para Java sem perda de generalidade. Uma vez que, em geral, existe um grande nmero de requisitos de teste para serem cobertos, uma caracterstica interessante da ferramenta JaBUTi a utilizao de cores diferentes dando indicaes ao testador para facilitar a gerao de casos de teste que satisfaam um maior nmero de requisitos em menor tempo. As diferentes cores representam diferentes pesos que so associados aos requisitos de teste de cada critrio. Informalmente, os pesos correspondem ao nmero de requisitos de teste que so cobertos quando um requisito de teste particular satisfeito. Assim, cobrir os requisitos de teste de maior peso leva a um aumento na cobertura de forma mais rpida. Para ilustrar os aspectos operacionais da JaBUTi , utilizada a mesma aplicao de simulao de telefonia apresentada ao longo deste texto. Em particular utilizada a classe Call, cujo cdigo foi parcialmente listado na Seo 1.3.2. Considerando a utilizao da ferramenta JaBUTi via interface grca, o primeiro passo para conduzir uma atividade de teste a criao de um projeto de teste, o qual contm informaes sobre as classes a serem testadas. Para a criao do projeto de teste o testador deve, primeiramente, fornecer o nome de uma classe base a partir da qual as demais classes relacionadas sero identicadas. Fornecido o nome da classe base, a ferramenta exibe a janela do Gerenciador de Projeto (Project Manager), como ilustrado na Figura 1.12. Do lado esquerdo dessa janela encontra-se o conjunto completo das classes que foram identicadas a partir da classe base e que podem ser selecionadas para serem testadas. No exemplo, foi escolhida a classe Call. Pressionando o boto Ok, JaBUTi cria um novo projeto (Telecom.jbt no exemplo), constri o grafo DU para cada mtodo de cada classe a ser testada, deriva os requisitos de teste de cada critrio, calcula o peso desses requisitos, e apresenta na tela o bytecode de uma das classes sendo testadas, como ilustrado na Figura 1.13(a). Alm da visualizao do bytecode, a ferramenta oferece ainda a visualizao do cdigo fonte (quando se encontra disponvel) e
48

Teste de Software OO e OA: Teoria e Prtica

do grafo DU de cada mtodo (Figura 1.13(b)).

(a) Bytecode inicial

(b) Cdigo-fonte e DU iniciais

Figura 1.13. Telas iniciais da JaBUTi

49

Masiero, Lemos, Ferrari e Maldonado

Uma vez que o conjunto de requisitos de teste de cada critrio foi determinado, tais requisitos 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. O testador pode, por exemplo, decidir criar um conjunto de teste com base em critrios de teste funcionais ou mesmo gerar um conjunto de teste ad-hoc e avaliar a cobertura desse conjunto de teste em relao a cada um dos critrios de teste estruturais da JaBUTi . Por outro lado, o testador pode visualizar o conjunto de requisitos de teste de cada critrio gerado para cada um dos mtodos das classes sendo testadas, vericar quais deles ainda no foram cobertos por algum caso de teste e ento desenvolver um novo caso de teste que satisfaa tais requisitos. As Figuras 1.14(a) e 1.14(b) ilustram parte dos requisitos de teste do construtor da classe Call gerados pelos critrios Todos-Nsei e Todos-Usosei , respectivamente.

(a) Todos-Nsei

(b) Todos-Usosei

Figura 1.14. Requisitos de teste para dois dos critrios livres de exceo, para o construtor da classe Call

Ainda, como pode ser observado na Figura 1.14, a ferramenta permite ao testador ativar/desativar diferentes combinaes de requisitos de teste, bem como marcar um determinado requisito de teste como no-executvel quando no existir um caso de teste capaz de cobri-lo. A ttulo de ilustrao de um processo de teste para a aplicao de telefonia, pode-se utilizar os critrios para testar cada uma das classes envolvidas no sistema. Por exemplo, para a classe Call, cria-se conjuntos de teste para cobrir cada um dos requisitos de teste indicados pela ferramenta, focando-se em cada um dos mtodos. Na Figura 1.15(a) mostrado o cdigo do mtodo includes da classe Call utilizado para vericar se um dado cliente est ou no includo em uma
50

Teste de Software OO e OA: Teoria e Prtica

(a) Cdigo-fonte

(b) DU

Figura 1.15. Cdigo-fonte e DU do mtodo includes da classe Call, com os requisitos de teste referentes ao critrio Todas-Arestasei destacados
chamada. O atributo connections da classe Call utilizado para manter uma lista dos clientes includos na chamada. Para saber se um dado cliente faz parte da chamada, basta fazer um lao sobre os elementos do vetor, vericando se o cliente corresponde a algum desses elementos. Na Figura 1.15(a) a ferramenta destaca no cdigo do mtodo os requisitos referentes ao critrio Todas-Arestasei , depois de todos os ns do mtodo terem sido cobertos a partir de dois casos de teste (os dois primeiros mostrados na Figura 1.16). Os casos de teste so construdos com o apoio do framework JUnit [Beck and Gamma 2006], podendo ser importados para execuo na JaBUTi . Para cobrir o requisito ainda no satisfeito com relao ao critrio TodasArestasei , o testador pode vericar qual aresta ainda no foi coberta e, para isso, pode utilizar o grafo DU (mostrado na Figura 1.15(b)). Observando esse grafo e analisando o cdigo-fonte e o bytecode, percebe-se que a aresta que falta a 1337, referente condio em que a varivel booleana result tem valor verdadeiro (ou seja, o cliente est includo na chamada), e o vetor ainda no foi percorrido inteiramente. Assim, para cobri-la basta criar um caso de teste no qual o cliente faz parte da chamada e est localizado no incio do vetor, com outros tambm conectados no restante do vetor. O terceiro caso de teste apresentado na Figura 1.16 cobre esta condio. Ao executar esse caso de teste para exercitar tal aresta, percebe-se que o programa no retorna, entrando em lao innito. Com isso detectado um defeito no programa: como atribudo o valor verdadeiro para a varivel result nas primeiras iteraes do lao, nas prximas iteraes, o teste lgico ou avaliado como verdadeiro e, por causa da avaliao curto-circuito utilizada em Java (ou seja, quando a primeira parte da expresso avaliada como verdadeira, a segunda no chega a ser avaliada), o mtodo nextElement que itera sobre os elementos de connections no mais chamado. Assim, a condio de sada do mtodo, que o trmino da iterao sobre o vetor, nunca satisfeita, fazendo com que o lao nunca termine. Esse exemplo evidencia a utilidade da ferramenta de teste no auxlio para
51

Masiero, Lemos, Ferrari e Maldonado

public void testIncludesTrue() { Customer jim = new Customer("Jim", 650, "1111"); Customer mik = new Customer("Mik", 500, "1112"); Call c = new Call(jim, mik, false); c.pickup(); assertTrue(c.includes(mik)); } public void testIncludesFalse() { Customer jim = new Customer("Jim", 650, "1111"); Customer mik = new Customer("Mik", 500, "1112"); Customer luk = new Customer("Luke", 400, "1113"); Call c = new Call(jim, mik, false); c.pickup(); assertFalse(c.includes(luk)); } //Cobrindo Todas-Arestas-ei public void testIncludesEdge() { Customer jim = new Customer("Jim", 650, "1111"); Customer mik = new Customer("Mik", 500, "1112"); Customer john = new Customer("John", 500, "1113"); Customer cris = new Customer("Cris", 500, "1114"); Customer carl = new Customer("Carl", 500, "1115"); Customer luke = new Customer("Luke", 500, "1116"); Call c1 = new Call(jim, mik, false); c1.pickup(); Call c2 = new Call(john, cris, false); c2.pickup(); Call c3 = new Call(carl, luke, false); c3.pickup(); c1.merge(c2); c1.merge(c3); assertTrue(c1.includes(jim)); }

Figura 1.16. Casos de teste para cobrir Todos-Nsei e Todas-Arestasei do mtodo includes
encontrar defeitos no software. Outro ponto importante a ser notado que a aplicao dos critrios seria muito complicada sem a utilizao de uma ferramenta como a JaBUTi , pois realizar tal atividade manualmente sem incorrer em erros , em geral, impraticvel. Os critrios baseados em uxo de dados no foram explorados nesta seo devido a limitaes de espao. Entretanto, em geral, tais critrios mostram-se bastante ecientes para auxiliar o entendimento e o teste dos sistemas, pois so mais difceis de serem satisfeitos do que os critrios baseados em uxo de controle.

1.4.2. A Ferramenta JaBUTi/AJ


A ferramenta JaBUTi foi estendida para possibilitar a utilizao dos critrios estruturais orientados a aspectos, ou seja, os critrios Todos-Ns-Transversais, Todas-Arestas-Transversais e Todos-Usos-Transversais [Lemos 2005]. Nessa verso a ferramenta foi denominada JaBUTi/AJ . Para o teste estrutural da aplicao de telefonia em sua verso OA, primeiramente deve-se criar um projeto na ferramenta JaBUTi/AJ , escolhendo os arquivos relevantes, inclusive os aspectos, similarmente ao que foi feito com a ferramenta JaBUTi . Nesta seo nfase dada aos critrios OA aborda52

Teste de Software OO e OA: Teoria e Prtica

dos e, desse modo, os mdulos interessantes a serem testados so: a classe Call e o aspecto Timing, pois eles so afetados por adendos. Na Figura 1.17 so mostrados os elementos requeridos para o critrio Todos-Ns-Transversais (All-Nodes-c ), para todas as unidades sendo testadas. A classe Call aparece com cinco requisitos de teste, o que quer dizer que ela afetada por adendos em cinco pontos; e o aspecto Timing aparece com dois elementos requeridos. interessante notar que a informao dada por esse critrio valiosa no s para o testador mas tambm para o desenvolvedor pois, com os elementos requeridos, o testador/desenvolvedor sabe exatamente em quais classes/aspectos os adendos esto denindo comportamento e tambm a quantidade de adendos afetando as classes/aspectos.

Figura 1.17. Elementos requeridos para o critrio todosns-transversais para a aplicao de telefonia

Para testar a classe Call a partir do critrio Todos-Ns-Transversais, necessrio saber quais mtodos esto sendo afetados pelos adendos. Para isso, o testador pode visualizar tanto o bytecode quanto o cdigo-fonte e observar em quais pontos a ferramenta indica os requisitos de teste. Na Figura 1.18 mostrada a tela da ferramenta JaBUTi/AJ com o cdigo-fonte da classe Call, com os requisitos de teste referentes ao critrio Todos-Ns-Transversais destacados no cdigo. Na verdade, percebe-se que, nesse caso, os elementos requeridos acabam sendo os pontos de juno afetados pelos adendos. Por exemplo, no caso do construtor de Call, deve haver algum adendo que dene comportamento nas chamadas aos construtores de LongDistance e Local. Isso pode ser conferido visualizando o grafo AODU do construtor de Call (Figura 1.19(a)). Os ns transversais 33 e 78 indicam que um adendo posterior
53

Masiero, Lemos, Ferrari e Maldonado

do aspecto Billing afeta aqueles pontos (esse o adendo responsvel por atribuir a fatura pela ligao ao cliente chamador). A partir da o critrio TodosNs-Transversais requer que esses ns sejam cobertos, ou seja, que existam casos de teste que os exercitem.

Figura 1.18. Cdigo-fonte da classe Call com os elementos requeridos do critrio Todos-Ns-Transversais em destaque

Analisando a lgica do construtor de Call, para cobrir os ns transversais so necessrios dois casos de teste: um que realize uma chamada local e outro que realize uma chamada de longa distncia. Como o adendo que atribui a fatura ao cliente chamador deve distinguir entre chamadas locais e de longa distncia, aqui o testador j pode ter evidncias de que o aspecto est implementado corretamente, j que o adendo afeta o ponto onde uma chamada local criada e tambm o ponto onde uma chamada de longa distncia criada. A partir dos dois casos de teste, que testaro se o mtodo realmente cria uma chamada local quando os clientes possuem o mesmo cdigo de rea e uma chamada de longa distncia quando os clientes possuem cdigos de rea diferentes, possvel cobrir os dois ns transversais referentes s execues do adendo para os dois casos. Quanto ao mtodo hangup, tambm afetado por adendos, a lgica a se54

Teste de Software OO e OA: Teoria e Prtica

guinte: quando a ligao concluda, necessrio terminar todas as conexes relacionadas a ela, o que feito por meio de um lao. Como o adendo do aspecto de cronometragem deve parar o cronmetro toda vez que uma conexo terminada, ele executado depois de cada chamada ao mtodo drop. J no caso do aspecto de faturamento, este tambm deve afetar o mesmo ponto, pois cada vez que uma conexo terminada, tambm necessrio calcular o custo da chamada e atribu-lo fatura do cliente chamador. Na Figura 1.19(b) mostrado o grafo AODU do mtodo hangup, com as execues dos adendos representadas pelos ns transversais 11 e 32. Para cobrir esses ns transversais necessrio um caso de teste que inicie uma chamada e a termine. Quando o mtodo hangup chamado, os adendos so executados no trmino de cada conexo relacionada com a chamada. Aqui o testador tambm pode ter evidncias de que os aspectos esto implementados corretamente e, alm disso, que esto interagindo de forma correta j que o aspecto de faturamento acionado aps o aspecto de cronometragem. Nota-se que a precedncia dos aspectos importante nesse caso, pois se o aspecto de faturamento agisse antes do aspecto de cronometragem, o sistema falharia. Esse tipo de defeito faz parte do modelo de defeitos denido por Alexander [Alexander et al. 2004]. Tambm importante notar que esse caso evidencia a ecincia da abordagem, no que diz respeito a auxiliar na descoberta desse tipo de defeito.

(a) Grafo AODU do construtor da classe Call

(b) Grafo AODU do mtodo hangup

Figura 1.19. Grafos AODU do construtor e do mtodo da classe Call

55

Masiero, Lemos, Ferrari e Maldonado

O mtodo pickup utilizado para simular o momento em que o cliente tira o telefone do gancho e completa a ligao, sendo afetado pelo adendo posterior do aspecto Timing. Isso feito para que o aspecto inicie o cronmetro toda vez que uma conexo completada, por meio do adendo. Para cobrir o n transversal necessrio apenas um caso de teste que execute o mtodo, completando uma ligao e vericando que o cronmetro iniciado. No caso do aspecto Timing, os dois adendos posteriores que iniciam e terminam o cronmetro so afetados pelos adendos posteriores do aspecto TimerLog, para que seja registrado o tempo de incio e parada do cronmetro. Nesses pontos podem ser observadas interaes aspecto-aspecto, pois o aspecto TimerLog afeta o aspecto Timing. Para cobrir os ns transversais necessrio um caso de teste que inicie e termine uma ligao, sensibilizando os adendos do aspecto Timing que iniciam e terminam o cronmetro e, por conseqncia, sensibilizando tambm o aspecto TimerLog. A ttulo de ilustrao, trs defeitos referentes POA foram inseridos na aplicao de telefonia: um relacionado com a precedncia dos aspectos (foi trocada a precedncia dos aspectos, ou seja Billing Timing, por Timing Billing) e dois relacionados com as denies dos conjuntos de juno (mais especicamente aos conjuntos de juno denidos em Timing, que identicam o incio e o m das ligaes). Para detectar esses defeitos, o testador pode utilizar o grafo AODU do mtodo hangup: com a alterao do conjunto de juno que dene quando as chamadas terminam, o adendo de Billing que calcula a fatura deveria afetar esse ponto, mas percebe-se a falta do n transversal. Com um caso de teste que faa asseres com relao fatura, o defeito revelado. O segundo defeito tambm relacionado com denies de conjuntos de juno, porm agora sobre o conjunto de juno que dene o incio da chamada. Tal defeito pode ser detectado no teste do mtodo pickup, no qual deveria ser iniciado o cronmetro da chamada. Com um caso de teste que verica o tempo da conexo o defeito revelado. O terceiro defeito refere-se precedncia dos aspectos e tambm pode ser detectado no teste do mtodo hangup, pois ainda assim a fatura no ser calculada corretamente, j que o cronmetro s parado depois da execuo do adendo de fatura (e o clculo da fatura depende da execuo do adendo de cronometragem). Esse defeito tambm pode ser evidenciado a partir da observao do AODU, pois o n transversal referente ao adendo do clculo da fatura precede o referente ao adendo da cronometragem (inverso ao que aparece na Figura 1.19(b), com o programa correto). Dois casos de teste escritos no JUnit, que cobrem os ns transversais e que auxiliam o testador a encontrar os trs defeitos, esto apresentados na Figura 1.20.
56

Teste de Software OO e OA: Teoria e Prtica

public void testCoverCCNodesPickup() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Call c1 = new Call(jim, mik, false); c1.pickup(); assertTrue(Timing.aspectOf().getTotalConnectTime(jim) == 0); } public void testCoverCCNodesCallHangup() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Call c1 = new Call(jim, mik, false); c1.pickup(); wait(2.0); c1.hangup(); assertTrue((Timing.aspectOf().getTotalConnectTime(jim) > 150) && (Timing.aspectOf().getTotalConnectTime(jim) < 250)); assertTrue((Billing.aspectOf().getTotalCharge(jim) > 450) && (Billing.aspectOf().getTotalCharge(jim) < 750)); }

Figura 1.20. Casos de teste que revelam os defeitos relacionados com a POA

1.4.3. Aplicao do Teste de Mutao de Unidade em Programas OO


Nesta seo apresentado um exemplo de aplicao do teste de mutao de unidade de programas OO escritos em Java. Nesse exemplo utilizada a mesma aplicao de simulao de telefonia apresentada ao longo deste texto. Em particular, so consideradas somente as funcionalidades da verso OO da aplicao, cujo teste estrutural foi apresentado na seo 1.4.1 e no qual se identicou um defeito no mtodo includes da classe Call. Esse defeito foi corrigido na aplicao utilizada nesta seo. Na Figura 1.21 destaca-se parte do cdigo da classe Call. Em particular, apresenta-se a implementao do mtodo construtor da classe e do mtodo merge. Para efeito ilustrativo, alguns dos operadores de mutao de unidade apresentados na Seo 1.3.4 so selecionados para serem aplicados nesses mtodos. A descrio desses operadores apresentada na Tabela 1.3.

Tabela 1.3. Operadores selecionados para o exemplo


Operador
SSDL STRI

Descrio
Remove cada um dos comandos da unidade. Troca o comando if(e) pelos comandos if(trap_on_true(e)) e if(trap_on_false(e)), interrompendo a execuo do programa quando a expresso testada for verdadeira ou falsa, respectivamente. Insere operador de negao em predicados (comandos condicionais). Introduz, no incio de cada lao, uma chamada funo false_after_nth_loop_iteration(N), que fora o encerramento do lao aps N iteraes.

OCNG SMTC

Nas Figuras 1.22, 1.23, 1.24 e 1.25 so apresentados os mutantes gerados


57

Masiero, Lemos, Ferrari e Maldonado

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class Call { private Customer caller, receiver; private Vector connections = new Vector(); public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; if (receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); } ... 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); } } }

Figura 1.21. Parte do cdigo da classe Call da aplicao de telefonia


por cada um dos operadores da Tabela 1.3. Algumas observaes sobre a aplicao dos operadores nesse exemplo: O smbolo > no incio de uma linha indica a linha ou trecho de cdigo que foi modicado; A aplicao de alguns operadores pode gerar mutantes que no compilam. Por exemplo, o operador SSDL, quando aplicado ao mtodo construtor, gerou trs desses mutantes7 . Dependendo da estratgia adotada pelo testador (e possivelmente implementada em uma ferramenta de apoio), esses mutantes podem ou no serem gerados durante a aplicao do respectivo operador de mutao ou, ainda, serem automaticamente marcados como mortos durante a execuo do teste. Os mutantes marcados com eq foram identicados como equivalentes. Os mtodos utilitrios (por exemplo, trap_on_true(e)) foram implementadas como mtodos estticos de uma classe auxiliar (Util), conforme se pode observar nos mutantes gerados. Para o operador SMTC, a funo false_after_nth_loop_iteration(N) pode ser congurada com qualquer valor para N. Nesse exemplo, a funo foi congurada com N=2. Os casos de teste apresentados na Figura 1.26 foram implementados com o objetivo de matar os mutantes dos mtodos construtor e merge gerados com
7

Os mutantes no compilveis no foram listados por questes de espao.

58

Teste de Software OO e OA: Teoria e Prtica

public Call(Customer caller, Customer receiver, boolean iM) { > this.receiver = receiver; Connection c; if (receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); } >

public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; Connection c; if (receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); }

M1eq
public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; if (receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } > } > }

M2eq
public void merge(Call other){

M3
public void merge(Call other){ for(Enumeration e = other.connections.elements(); e.hasMoreElements();){ Connection conn = (Connection)e.nextElement(); other.connections.removeElement(conn); > } } } }

M4
public void merge(Call other){ for(Enumeration e = other.connections.elements(); e.hasMoreElements();){ Connection conn = (Connection)e.nextElement(); > connections.addElement(conn);

M5

M6

Figura 1.22. Mutantes gerados pelo operador SSDL

public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; > if (Util.trap_on_true( receiver.localTo(caller))) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); }

public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; > if (Util.trap_on_false( receiver.localTo(caller))) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); }

M7

M8

Figura 1.23. Mutantes gerados pelo operador STRI


a aplicao dos operadores apresentados na Tabela 1.3. Na Tabela 1.4 apresentada a relao dos mutantes mortos por cada caso de teste. Como se pode

59

Masiero, Lemos, Ferrari e Maldonado

public Call(Customer caller, Customer receiver, boolean iM) { this.caller = caller; this.receiver = receiver; Connection c; > if (!receiver.localTo(caller)) { c = new Local(caller, receiver, iM); } else { c = new LongDistance(caller, receiver, iM); } connections.addElement(c); }

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); } }

M9

M10

Figura 1.24. Mutantes gerados pelo operador OCNG

public void merge(Call other){ for(Enumeration e = other.connections.elements(); e.hasMoreElements();){ > if (Util.false_after_nth_loop_iteration(2)) { Connection conn = (Connection)e.nextElement(); other.connections.removeElement(conn); connections.addElement(conn); } } }

M11

Figura 1.25. Mutante gerado pelo operador SMTC


observar, um nico caso de teste pode matar mais de um mutante.

Tabela 1.4. Casos de teste X mutantes OO mortos


Caso de Teste testCallConstruction testCallMerging01 testCallMerging02 testCallInstantiation01 testCallInstantiation02 testCallType() Mutante M3 / M7 M3 / M7 / M4 / M5 / M10 M7 / M4 / M6 / M10 M7 M8 M7 / M8 / M9

Aps construo e a execuo dos seis casos de teste apresentados, o nico mutante vivo foi o gerado pelo operador SMTC (mutante M11). Durante a construo de um caso de teste para matar esse mutante, observou-se uma falha na sua execuo. Ao tentar realizar duas combinaes (merging ) consecutivas de chamadas, observou-se no segundo merging que as conexes da chamada recebida como parmetro (duas, nesse caso de teste) no eram todas transferidas para a chamada corrente. O caso de teste projetado apresentado na Figura 1.27. O cdigo do mtodo merge original apresentado novamente na Figura 1.28(a). Durante a depurao do cdigo, detectou-se o defeito na linha 5. Du60

Teste de Software OO e OA: Teoria e Prtica

public void testCallConstruction() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Call c1 = new Call(jim, mik, false); assertTrue(c1.includes(jim)); } public void testCallInstantiation01() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); try { Call c1 = new Call(jim, mik, false); assertTrue(true); } catch (RuntimeException e) { assertTrue(false); } } public void testCallInstantiation02() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer kat = new Customer("Kat", 750, "3361-1112"); try { Call c1 = new Call(jim, kat, false); assertTrue(true); } catch (RuntimeException e) { assertTrue(false); } }

public void testCallMerging01() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Customer joe = new Customer("Joe", 650, "3361-1113"); Call c1 = new Call(jim, mik, false); Call c2 = new Call(mik, joe, false); c1.merge(c2); assertTrue(c1.includes(joe)); } public void testCallMerging02() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Customer joe = new Customer("Joe", 650, "3361-1113"); Call c1 = new Call(jim, mik, false); Call c2 = new Call(mik, joe, false); c1.merge(c2); assertFalse(c2.includes(mik)); } public void testCallType() { Customer jim = new Customer("Jim", 650, "3361-1112"); Customer mik = new Customer("Mik", 650, "3361-1113"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos, true); System.setOut(ps); Call c1 = jim.call(mik, false); String s1 = baos.toString().trim(); assertEquals(s1, "[new local connection " + "from Jim(650) to Mik(650)]"); }

Figura 1.26. Casos de teste criados para matar mutantes OO

1 2 3 4 5 6 7 8 9 10 11

public void testCallMerging03() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); Customer joe = new Customer("Joe", 650, "3361-1113"); Call c1 = new Call(joe, mik, false); Call c2 = new Call(mik, jim, false); Call c3 = new Call(mik, joe, false); c1.merge(c2); c3.merge(c1); assertTrue(c3.includes(jim)); }

Figura 1.27. Caso de teste projetado para matar o mutante M11


rante a primeira iterao do lao, a conexo corrente removida do vetor de conexes enumeradas. Com essa remoo, a conexo seguinte passa a constar na posio 0 do vetor. Entretanto, durante a segunda iterao, o ndice criado para percorrer esse vetor j estava apontando para a segunda posio do vetor, o que no estado corrente apontava para um elemento nulo. Portanto, nesse caso, uma conexo no era transferida para a chamada resultante da combinao (objeto corrente). Na Figura 1.28(b) apresentado uma possibili-

61

Masiero, Lemos, Ferrari e Maldonado

dade do mtodo merge corrigido.

1 2 3 4 5 6 7 8 9 10

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); } }

1 2 3 4 5 6 7 8 9 10

public void merge(Call other){ for(Enumeration e = other.connections.elements(); e.hasMoreElements(); ){ Connection conn = (Connection)e.nextElement(); connections.addElement(conn); } other.connections.clear(); }

(a)

(b)

Figura 1.28. Mtodo merge original e modicado


importante destacar que durante a aplicao do teste estrutural de unidade na classe Call, todos os requisitos gerados para o mtodo merge foram cobertos com um caso de teste. Entretanto, a aplicao do teste estrutural no foi suciente para revelar o defeito encontrado nesse mtodo. Destaca-se com isso a importncia de se aplicar diferentes tcnicas e critrios de teste de forma complementar, objetivando-se a melhoria de qualidade do produto nal. Ressalta-se tambm que aps a deteco e correo de um defeito no cdigo do artefato original, como ocorrido nesse exemplo, o processo do teste de mutao deve passar novamente pelos quatro passos apresentados na seo 1.2. Nesse caso, a gerao dos mutantes pode gerar um conjunto de mutantes signicativamente diferente do conjunto originalmente gerado, podendo requerer novos casos de teste para mat-los. Alm disso, novos defeitos podero ser inseridos no cdigo durante a correo dos defeitos j encontrados, requerendo dessa forma que tanto o programa corrigido quanto os mutantes gerados a partir dele sejam exercitados com o conjunto de teste. Conforme discutido na Seo 1.2, indispensvel o uso de ferramentas de apoio para que a atividade de teste alcance a qualidade desejada. Em particular, tm sido propostas e implementadas algumas ferramentas de apoio ao teste de mutao de programas OO escritos em Java que utilizam os operadores de mutao discutidos nesta seo.

1.4.4. Aplicao do Teste de Mutao de Unidade em Programas OA


Em continuidade ao exemplo de aplicao do teste de mutao, nesta seo apresentado um exemplo de sua aplicao no teste de unidade de programas OA escritos em AspectJ, agora considerando a verso OA da aplicao de simulao de telefonia. Ressalta-se que dois defeitos foram encontrados nessa aplicao durante o desenvolvimento dos exemplos de teste estrutural e de mutao de unidade de programas OO apresentados nas sees 1.4.1 e 1.4.3. Esses defeitos foram corrigidos na aplicao utilizada nesta seo. Em relao aos operadores de mutao, os mesmos operadores utilizados
62

Teste de Software OO e OA: Teoria e Prtica

na seo anterior sero aplicados, sendo eles SSDL, STRI, OCNG e SMTC. Na Figura 1.29 destaca-se parte do cdigo do aspecto Billing. Em particular, destaca-se o adendo sobre o qual so aplicados os operadores de mutao selecionados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public aspect Billing { ... public static final long MOBILE_LD_RECEIVER_RATE = 5; ... after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); } ... }

public aspect Timing { ... pointcut endTiming(Connection c): target(c) && call(void Connection.drop()); ... }

Figura 1.29. Parte do cdigo dos aspectos Billing e Timing da aplicao de telefonia

Os casos de teste apresentados na Figura 1.30 foram implementados para matar os mutantes dos mtodos construtor e merge gerados nesse exemplo, que so mostrados nas Figuras 1.31, 1.32 e 1.33. Observa-se que o operador SMTC no produz mutantes para o adendo selecionado. Na Tabela 1.5 apresentada a relao dos mutantes mortos por cada caso de teste. Da mesma forma que no exemplo apresentado na seo anterior, pode-se observar que um nico caso de teste pode matar mais de um mutante. Pode-se observar tambm que nesse exemplo no foram gerados mutantes que no compilam ou equivalentes.

Tabela 1.5. Casos de teste X mutantes OA mortos


Caso de Teste testMobileReceiverCharging testMobileCallCharging testNotMobileCallCharging testLocalMobileCallCharging Mutante Ma1 / Ma2 / Ma3 / Ma4 / Ma5 / Ma8 / Ma9 Ma4 / Ma5 Ma6 Ma4 / Ma7

63

Masiero, Lemos, Ferrari e Maldonado

public void testMobileReceiverCharging() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 750, "3361-1112"); Call c1 = new Call(jim, mik, true); c1.pickup(); wait(2.0); c1.hangup(); double result = Billing.aspectOf().getTotalCharge(mik); assertTrue(result > 0); } public void testMobileCallCharging() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 750, "3361-1112"); try { Call c1 = new Call(jim, mik, true); c1.pickup(); c1.hangup(); assertTrue(true); } catch (RuntimeException e) { assertTrue(false); } }

public void testNotMobileCallCharging() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); try { Call c1 = new Call(jim, mik, false); c1.pickup(); c1.hangup(); assertTrue(true); } catch (RuntimeException e) { assertTrue(false); } } public void testLocalMobileCallCharging() { Customer jim = new Customer("Jim", 650, "3361-1111"); Customer mik = new Customer("Mik", 650, "3361-1112"); try { Call c1 = new Call(jim, mik, true); c1.pickup(); c1.hangup(); assertTrue(true); } catch (RuntimeException e) { assertTrue(false); } }

Figura 1.30. Casos de teste criados para matar mutantes OA

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; > } } getPayer(conn).addCharge(cost); }

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { > } getPayer(conn).addCharge(cost); }

Ma1
after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; > getPayer(conn).addCharge(cost); }

Ma2

Ma3

Figura 1.31. Mutantes gerados pelo operador SSDL


Considerando a necessidade de apoio automatizado atividade de teste, a ferramenta de apoio ao teste de mutao de unidades programas OO que

64

Teste de Software OO e OA: Teoria e Prtica

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; > if (Util.trap_on_true(conn.isMobile())) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { > if (Util.trap_on_true( conn instanceof LongDistance)){ long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

Ma4
after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; > if (Util.trap_on_false(conn.isMobile())) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

Ma5
after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { > if (Util.trap_on_false( conn instanceof LongDistance)){ long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

Ma6

Ma7

Figura 1.32. Mutantes gerados pelo operador STRI

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; > if (!conn.isMobile()) { if (conn instanceof LongDistance) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

after(Connection conn) returning () : Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; if (conn.isMobile()) { > if (!(conn instanceof LongDistance)) { long receiverCost = MOBILE_LD_RECEIVER_RATE * time; conn.getReceiver().addCharge(receiverCost); } } getPayer(conn).addCharge(cost); }

Ma8

Ma9

Figura 1.33. Mutantes gerados pelo operador OCNG


est em desenvolvimento no grupo ser estendida para apoiar tambm o teste de mutao de unidades de programas OA escritos em AspectJ.

65

Masiero, Lemos, Ferrari e Maldonado

1.5. Concluses e Perspectivas


Neste texto foi apresentada uma viso geral da atividade de teste de software, destacando sua importncia dentro do processo de desenvolvimento e a necessidade de apoio automatizado. Foram enfatizadas as tcnicas estrutural e baseada em defeitos; em particular, foram apresentados a aplicao de critrios baseados em uxo de controle, uxo de dados e mutao no contexto de teste de unidade de programas OO e OA. Questes relacionadas ao teste de mutao na fase de integrao tambm foram brevemente exploradas.Com as devidas adaptaes, observou-se que esses critrios, embora originalmente propostos para o teste de programas procedimentais, podem ser utilizados para o teste de software nos contextos de OO e OA tanto na fase de unidade quanto na fase de integrao. Durante a aplicao dos critrios de teste nos exemplos apresentados, foram identicadas falhas na execuo dos testes, que ocorreram devido a dois defeitos presentes nos programas originais. Nesses casos, podem-se observar evidncias sobre a eccia dos critrios em revelar defeitos presentes no software e, ainda, sobre o carter complementar das tcnicas de teste, visto que um dos defeitos foi identicado durante a utilizao da tcnica estrutural e o outro somente durante a aplicao do teste de mutao. No que se refere a direes futuras, observa-se a necessidade de mais investigaes no contexto de teste de software OO e OA. O paradigma OO, embora j consolidado no que diz respeito s demais atividades do ciclo de desenvolvimento de software, ainda possui questes em aberto que impactam a atividade de teste. Poucos ainda so os trabalhos que propem abordagens de teste direcionadas s caractersticas especcas introduzidas pelo paradigma como, por exemplo, encapsulamento e polimorsmo. Alm disso, ainda no existe um consenso sobre uma estratgia ideal de teste de integrao e ferramentas de apoio ao teste de software OO em geral. A POA, por sua vez, uma abordagem relativamente recente, e o foco principal das pesquisas realizadas e em andamento est no estabelecimento de conceitos e em como implement-los nas tecnologias de apoio. Nota-se que as mesmas necessidades encontradas para o teste de software OO esto presentes no contexto de software OA, e algumas outras especcas como, por exemplo, o teste adequado de conjuntos de juno e o teste de integrao entre aspectos e classes. Cabe destacar que a investigao dos problemas indicados, incluindo evolues e desenvolvimento de ferramentas de teste (como, por exemplo, evolues na ferramenta JaBUTi); e problemas relacionados com o teste de integrao de software OO e OA, so objetivos de trabalhos em andamento de alguns grupos de pesquisa, incluindo o grupo de Engenharia de Software do ICMC/USP. Para nalizar, ressalta-se que o conhecimento e as contribuies na rea de teste divididos basicamente em conhecimento terico, experimental e de ferramentas de apoio devem ser constantemente atualizados, assim como
66

Teste de Software OO e OA: Teoria e Prtica

nas demais reas. Nessa perspectiva, a organizao de uma base histrica sobre o custo e a eccia das tcnicas e critrios de teste, em diferentes domnios de aplicao, em relao a diferentes classes de defeitos, certamente facilitaria o planejamento de futuros desenvolvimentos de software. Facilitaria, ainda, o estabelecimento de estratgias de teste que explorem os aspectos complementares das tcnicas e critrios, viabilizando a deteco do maior nmero de defeitos possvel e com o menor custo, o que contribuiria para a liberao de produtos de software de maior qualidade a um menor custo [Rocha et al. 2001].

1.6. Agradecimentos
Os autores agradecem Fundao de Amparo Pesquisa do Estado de So Paulo (FAPESP) pelo apoio nanceiro e aos pesquisadores Ellen Francine Barbosa, Auri Marcelo Rizzo Vincenzi e Mrcio Eduardo Delamaro pelas diversas contribuies neste trabalho.

Referncias
[Acree 1980] Acree, A. T. (1980). On Mutation. PhD thesis, Scholl of Information and Computer Science, Georgia Institute of Technology, Atlanta/GA USA. [Acree et al. 1979] Acree, A. T., Budd, T. A., DeMillo, R. A., Lipton, R. J., and Sayward, F. G. (1979). Mutation analysis. Technical Report GIT-ICS-79/08, School of Information and Computer Science, Georgia Institute of Technology, Atlanta/GA - USA. [Adrion et al. 1982] Adrion, W. R., Branstad, M. A., and Cherniavsky, J. C. (1982). Validation, Verication, and Testing of Computer Software. ACM Computing Surveys, 14(2):159192. [Agrawal et al. 1989] Agrawal, H., DeMillo, R. A., Hathaway, R., Hsu, W., Hsu, W., Krauser, E. W., Martin, R. J., Mathur, A. P., and Spafford, E. H. (1989). Design of mutant operators for the C programming language. Technical Report SERC-TR41-P, Software Engineering Research Center, Purdue University, West Lafayette/IN - USA. [Alexander 2003] Alexander, R. (2003). Aspect-oriented programming: the real costs? IEEE Software, 20(6):9093. [Alexander et al. 2004] Alexander, R. T., Bieman, J. M., and Andrews, A. A. (2004). Towards the systematic testing of aspect-oriented programs. Technical report, Department of Computer Science, Colorado State University. [Alexander and Offutt 2000] Alexander, R. T. and Offutt, A. J. (2000). Criteria for testing polymorphic relationships. In Proceedings of the 11th International Symposium on Software Reliability Engineering (ISSRE2000), pages 1523, Sao Jose/CA - USA. IEEE Computer Society. [AspectJ Team 2003] AspectJ Team (2003). The AspectJ programming guide. Online. Disponvel em http://www.eclipse.org/aspectj/doc/ released/progguide/index.html - ltimo acesso em 20/01/2006.
67

Masiero, Lemos, Ferrari e Maldonado

[Beck and Gamma 2006] Beck, K. and Gamma, E. (2006). JUnit, testing resources for extreme programming. Online. Disponvel em http://www. junit.org/index.htm - ltimo acesso em 02/04/2006. [Binder 1999] Binder, R. V. (1999). Testing Object-Oriented Systems Models, Patterns, and Tools. Addison Wesley, 1st. edition. [Booch 1994] Booch, G. (1994). Object-Oriented Analysis and Design with Applications. Addison Wesley, 2nd. edition. [Budd 1981] Budd, T. A. (1981). Mutation analysis: Ideas, example, problems and prospects. Computer Program Testing North-Holand Publishing Company. [Chevalley and Thvenod-Fosse 2003] Chevalley, P. and Thvenod-Fosse, P. (2003). A mutation analysis tool for java programs. International Journal on Software Tools for Technology Transfer (STTT), 5(1):90103. [Cox and Novobilski 1991] Cox, B. J. and Novobilski, A. J. (1991). ObjectOriented Programming. Addison-Wesley, 2nd. edition. [Delamaro 1997] Delamaro, M. E. (1997). Mutao de Interface: Um critrio de adequao interprocedimental para o teste de integrao. PhD thesis, IFSC/USP. [Delamaro et al. 2001] Delamaro, M. E., Maldonado, J. C., and Mathur, A. P. (2001). Interface Mutation: An approach for integration testing. IEEE Transactions os Software Engineering, 27(3):228247. [DeMillo 1978] DeMillo, R. A. (1978). Hints on test data selection: Help for the practicing programmer. IEEE Computer, 11(4):3443. [Domingues 2002] Domingues, A. L. S. (2002). Avaliao de critrios e ferramentas de teste para programas OO. Masters thesis, ICMC/USP, So Carlos/SP - Brasil. [Elrad et al. 2001] Elrad, T., Filman, R. E., and Bader, A. (2001). Aspectoriented programming: Introduction. Communications of the ACM, 44(10):2932. [Filman and Friedman 2000] Filman, R. and Friedman, D. (2000). Aspectoriented programming is quantication and obliviousness. In Workshop on Advanced Separation of Concerns, OOPSLA 2000, pages 2135, Minneapolis - USA. [Frankl and Weyuker 2000] Frankl, P. G. and Weyuker, E. J. (2000). Testing software to detect and reduce risk. Journal of Systems and Software, 53(3):275286. [Friedman 1975] Friedman, A. D. (1975). Logical Design of Digital Systems. Computer Science Press. [Harrold 2000] Harrold, M. J. (2000). Testing: A roadmap. In 22th International Conference on Software Engineering - Future of SE Track, pages 6172.

68

Teste de Software OO e OA: Teoria e Prtica

ACM Press. [Harrold and Rothermel 1994] Harrold, M. J. and Rothermel, G. (1994). Performing data ow testing on classes. In 2nd ACM SIGSOFT Symposium on Foundations of Software Engineering, pages 154163, New York, NY. ACM Press. [Howden 1986] Howden, W. E. (1986). Functional Program Testing and Analysis. McGraw-Hill, Inc., New York/NY - USA. [IEEE 1990] IEEE (1990). IEEE standard glossary of software engineering terminology. Standard 610.12, Institute of Electric and Electronic Engineers. [Kiczales et al. 2001] Kiczales, G., Hilsdale, E., Hugunin, J., Kersten, M., Palm, J., and Griswold, W. G. (2001). An overview of AspectJ. In ECOOP 01: Proceedings of the 15th European Conference on Object-Oriented Programming, pages 327353. [Kiczales et al. 1997] Kiczales, G., Irwin, J., Lamping, J., Loingtier, J.-M., Lopes, C., Maeda, C., and Menhdhekar, A. (1997). Aspect-oriented programming. In Ak sit, M. and Matsuoka, S., editors, Proceedings of the European Conference on Object-Oriented Programming, volume 1241, pages 220 242, Berlin, Heidelberg, and New York. Springer-Verlag. [Kiczales and Mezini 2005] Kiczales, G. and Mezini, M. (2005). Aspectoriented programming and modular reasoning. In Proceedings of the 27th International Conference on Software Engineering (ICSE2005), pages 49 58. ACM Press. [Kim et al. 2000] Kim, S., Clark, J., and McDermid, J. (2000). Class mutation: Mutation testing for object-oriented programs. In Proceedings of the FMES2000. [Laddad 2003] Laddad, R. (2003). AspectJ In Action. Manning Publications. [Lemos 2005] Lemos, O. A. L. (2005). Teste de programas orientados a aspectos: Uma abordagem estrutural para AspectJ. Masters thesis, ICMC/USP, So Carlos/SP - Brasil. [Lemos et al. 2004] Lemos, O. A. L., Maldonado, J. C., and Masiero, P. C. (2004). Data ow integration testing criteria for aspect-oriented programs. In Anais do 1o Workshop Brasileiro de Desenvolvimento de Software Orientado a Aspectos (WASP2004), Braslia/DF - Brasil. [Lemos et al. 2005] Lemos, O. A. L., Maldonado, J. C., and Masiero, P. C. (2005). Structural unit testing of AspectJ programs. In Proceedings of the 1st Workshop on Testing Aspect Oriented Programs in conjunction with AOSD2005, Chicago/IL, USA. [Linnenkugel and Mllerburg 1990] Linnenkugel, U. and Mllerburg, M. (1990). Test data selection criteria for (software) integration testing. In First International Conference on Systems Integration, pages 709717, Morristown/NJ -

69

Masiero, Lemos, Ferrari e Maldonado

USA. [Ma et al. 2002] Ma, Y. S., Kwon, Y. R., and Offutt, J. (2002). Inter-class mutation operators for java. In Proceedings of the 13th International Symposium on Software Reliability Engineering (ISSRE02), pages 352366, Annapolis, MD. IEEE Computer Society Press. [Maldonado 1991] Maldonado, J. C. (1991). Critrios Potenciais Usos: Uma Contribuio ao Teste Estrutural de Software. PhD thesis, DCA/FEE/UNICAMP, Campinas, SP - Brasil. [Mathur and Wong 1993] Mathur, A. P. and Wong, W. E. (1993). Evaluation of the cost of alternative mutation strategies. In Anais do 7o Simpsio Brasileiro de Engenharia de Software (SBES1993), pages 320335, Joo Pessoa/PB - Brasil. [McDaniel and McGregor 1994] McDaniel, R. and McGregor, J. D. (1994). Testing polymorphic interactions between classes. Technical Report TR-94-103, Clemson University. [Mortensen and Alexander 2004] Mortensen, M. and Alexander, R. T. (2004). Adequate testing of aspect-oriented programs. Technical Report CS 01-110, Department of Computer Science, Colorado State University. [Myers et al. 2004] Myers, G. J., Sandler, C., Badgett, T., and Thomas, T. M. (2004). The Art of Software Testing. John Wiley & Sons, 2nd edition. [Offutt et al. 1993] Offutt, A. J., Rothermel, G., and Zapf, C. (1993). An experimental evaluation of selective mutation. In Proceedings of the 15th International Conference on Software Engineering (ICSE1993), pages 100107, Baltimore/MD - USA. IEEE Computer Society Press. [Offutt et al. 2001] Offutt, J., Alexander, R., Wu, Y., Xiao, Q., and Hutchinson, C. (2001). A fault model for subtype inheritance and polymorphism. In Proceedings of the 12th International Symposium on Software Reliability Engineering, pages 8493, Hong Kong - China. IEEE Computer Society Press. [Pressman 2000] Pressman, R. S. (2000). Software engineering - A Practitioners Approach. McGraw-Hill, 5th edition. [Rapps and J.Weyuker 1982] Rapps, S. and J.Weyuker, E. (1982). Data ow analysis techniques for program test data selection. In 6th International Conference on Software Engineering, pages 272278, Tokio, Japan. [Rapps and J.Weyuker 1985] Rapps, S. and J.Weyuker, E. (1985). Selecting software test data using data ow information. IEEE Transactions on Software Engineering, 11(4):367375. [Rocha et al. 2001] Rocha, A. R. C., Maldonado, J. C., and Weber, K. C. (2001). Qualidade de Software. Prentice Hall. [Simo and Maldonado 2000] Simo, A. S. and Maldonado, J. C. (2000). Mutation based test sequence generation for Petri Nets. In Proceedings of the

70

Teste de Software OO e OA: Teoria e Prtica

3rd Workshop of Formal Methods, pages 6879, Joo Pessoa/PB - Brasil. [Sommerville 2001] Sommerville, I. (2001). Software Engineering. AddisonWesley, 6th edition. [Stroustrup 1986] Stroustrup, B. (1986). The C++ Programming Language. Addison-Wesley. [Sun Microsystems 2006] Sun Microsystems (2006). Java Technology. Online. Disponvel em http://java.sun.com/ - ltimo acesso em 02/04/2006. [Vilela et al. 1999] Vilela, P. R. S., Maldonado, J. C., and Jino, M. (1999). Data ow based integration testing. In Anais do 13o Simpsio Brasileiro de Engenharia de Software (SBES1999), Florianpolis/SC - Brasil. [Vincenzi 2004] Vincenzi, A. M. R. (2004). Orientao a Objeto: Denio, Implementao e Anlise de Recursos de Teste e Validao. PhD thesis, ICMC/USP, So Carlos, SP - Brasil. [Vincenzi et al. 2005] Vincenzi, A. M. R., Maldonado, J. C., Wong, W. E., and Delamaro, M. E. (2005). Coverage testing of java programs and components. Science of Computer Programming, 56(1-2):211230. [Vincenzi et al. 2002] Vincenzi, A. M. R., Nakagawa, E. Y., Maldonado, J. C., Delamaro, M. E., and Romero, R. A. R. (2002). Bayesian-learning based guidelines to determine equivalent mutants. International Journal of Software Engineering and Knowledge Engineering - IJSEKE, 12(6):675689. [Weyuker 1982] Weyuker, E. J. (1982). Computer Journal, 25(4):465470. On testing non-testable programs.

[Weyuker 1996] Weyuker, E. J. (1996). Using failure cost information for testing and reliability assessment. ACM Transactions on Software Engineering and Methodology, 5(2):8798. [Wirfs-Brock et al. 1990] Wirfs-Brock, R., Wilkerson, B., and Wiener, L. (1990). Designing Object-Oriented Software. Prentice-Hall. [Wong and Mathur 1995] Wong, W. E. and Mathur, A. P. (1995). Fault detection effectiveness of mutation and data ow testing. Software Quality Journal, 4(1):6983. [Zhu et al. 1997] Zhu, H., Hall, P., and May, J. (1997). Software unit test coverage and adequacy. ACM Computing Surveys, 29(4):366427.

71