Testes de Software Aplicados à Orientação a Objetos
1. Introdução
Este artigo apresenta um tema muito importante na Engenharia de Software que são os Testes de Software aplicados à Orientação a Objetos.
Um dos problemas, constantemente citados quando se discute teste de software, é o alto custo. Segundo Pressman, a atividade de teste é um elemento crítico da garantia de qualidade de software e pode assumir até 40% do esforço despendido no desenvolvimento de software [3]. Por este motivo, o teste de software tornou-se, pouco a pouco, um tema de grande importância, com a necessidade de adaptação de métodos práticos que assegurem a qualidade dos produtos finais, a fim de torná-los confiáveis e de fácil manutenção.
As técnicas e métodos baseados em Orientação a Objetos surgiram trazendo um enfoque diferente dos métodos tradicionais. Uma grande vantagem da abordagem Orientada a Objetos é que ela adota formas mais próximas dos mecanismos humanos para gerenciar a complexidade de um software, tal como a abstração, classificando elementos (objetos) em grupos (classes), através de uma estrutura hierárquica. Neste paradigma, o mundo real é visto como sendo constituído de objetos autônomos, concorrentes, que interagem entre si, e cada objeto tem seu próprio estado e comportamento, semelhante ao seu correspondente no mundo real. O teste orientado a objetos é essencialmente diferente do teste convencional, pois testa o estado dos objetos e produz um menor número de casos de testes devido a reutilização de alguns casos de testes derivados da presença do conceito de herança, entretanto, não é questionável que a realização de testes de códigos Orientados a Objetos seja mais complexa, principalmente em função da hierarquia de classes, do polimorfismo e da herança.
2. Teste de software orientado a objetos
Embora a abordagem Orientada a Objetos apresente várias vantagens em relação aos paradigmas procedimentais antigos, a realização de testes é um dos maiores problemas encontrados no desenvolvimento de códigos Orientados a Objetos, pois, ao mesmo tempo em que algumas características próprias das linguagens orientadas a objetos tentam reduzir alguns tipos de erros, podem favorecer a adição de novos erros. Este paradigma nos proporciona algumas facilidades, como por exemplo: redução no tamanho dos métodos através da utilização de heranças; algoritmos menos complexos, ou seja, algoritmos com poucas linhas de código, o que aumenta a legibilidade e o entendimento do mesmo; facilidade na localização e correção de defeitos; encapsulamento, que previne muitos problemas causados pelo escopo de dados sem controle.
Uma diferença importante do teste de programas procedimentais em relação aos modelos orientados a objetos encontra-se no fato que as aplicações Orientadas a Objetos não são executadas seqüencialmente. Como nenhuma ordem de invocação de métodos é especificada, a análise de fluxo de controle do fluxo de dados para o teste de classe é dificultada, fazendo com que técnicas de teste estrutural não sejam diretamente aplicáveis. Os problemas adicionais para o teste e a manutenção de programas introduzidos por este paradigma podem ser resumidos no entendimento dos conceitos de aplicações Orientadas a Objetos, devido às características de encapsulamento e polimorfismo causados pelos relacionamentos complexos que existem em programas Orientados a Objetos, tais como: herança, agregação, associação, criação dinâmica de objetos, polimorfismo, etc. Outro problema que podemos considerar neste paradigma é o teste de comportamento dependente de estado, onde os objetos possuem comportamentos dependentes de seu estado, isto é, o efeito de uma operação em um objeto depende de seu estado e pode alterar este estado, sendo que este problema também está altamente ligado à carência no desenvolvimento de ferramentas de suporte ao teste Orientado a Objetos ou total inexistência das mesmas no mercado e o uso de técnicas convencionais não atenderem plenamente aos problemas específicos de teste Orientado a Objetos.
A menor unidade de teste para sistemas Orientados a Objetos é a classe, pois é onde encontramos uma coleção de declarações de variáveis e funções. Vários autores recomendam que seja realizado, inicialmente, o teste das classes que não possuem dependência e o teste das classe/objetos que usam seus serviços, juntando desta forma os testes de unidade/interação. A estratégia de teste proposta por estes autores é denominada Teste Baseado em Estados. Binder propõe que o comportamento de uma classe é definido pelo conjunto de valores encapsulados que a classe possui em determinado momento, e que seu comportamento é controlado por valores encapsulados, seqüências de mensagens ou ainda por ambos; o autor também descreve que um estado é um subconjunto do conjunto de todas as combinações possíveis dos valores de atributos da classe e através do monitoramento das mudanças que ocorrem em valores dos atributos dos objetos. Neste tipo de teste, é possível verificar as diversas interações que ocorrem entre estes [1]. A ênfase do teste baseado em estados está nos valores armazenados na representação dos objetos, consistindo no teste das interações em uma classe pela monitoração das alterações dos valores dos atributos em um objeto, que conseqüentemente alteram seu estado.
Entre as propostas encontradas na área de testes Orientados a Objetos, podemos também citar a proposta de Fiedler, onde foram aplicadas técnicas de teste tradicionais para softwares escritos em C++ e Java. As classes são validadas na ordem determinada por um grafo de chamada, sendo que as classes base do grafo são validadas primeiro [2]. Existem também frameworks para desenvolvimento de casos de teste para softwares Orientados a Objetos, com uma técnica baseada em estados, dirigindo-se a vários escopos de teste, tais como: teste de classes baseado na implementação e baseado na representação, teste de clusters, de subsistemas e de sistemas.
3. Reflexão Computacional
Reflexão Computacional é definida como uma técnica de programação que permite ao programador obter informações a respeito do próprio programa, com o objetivo de monitorá-lo, adicionar novas funcionalidades e mesmo fazer alterações adaptativas em tempo de execução [4]. Sua utilização no processo de teste possibilita realizar uma análise da aplicação de forma dinâmica, sem a necessidade de instrumentar o código-fonte da aplicação. Através da reflexão é possível monitorar classes e objetos específicos, realizando uma intervenção na computação da aplicação em teste [4].
A Reflexão Computacional é a atividade executada por um sistema computacional quando faz processamentos sobre suas próprias ações e define uma arquitetura em níveis, denominada arquitetura reflexiva [2]. Em uma arquitetura reflexiva, um sistema computacional é visto como parte de dois componentes: um representando o objeto e o outro a parte reflexiva. Os processamentos de um objeto, localizado no nível base, têm por objetivo resolver problemas e retornam informações sobre as computações do objetivo, podendo adicionar funcionalidades extra a este objeto. A figura 1 permite observar uma arquitetura reflexiva, composta por um metanível, que contém a reflexão do sistema objeto, e um nível-base, onde ficam os objetos do domínio da aplicação. Os dados do nível base são usados no metanível para a realização de computações reflexivas, que podem interferir nas computações de nível-base.
A computação reflexiva pode atuar tanto sobre a estrutura do sistema objeto, ocorrendo em nível de classes, quanto sobre o comportamento, quando ocorre em nível de objetos. Quando a computação reflexiva ocorre em nível de classes, o metanível é composto por metaclasses que contêm informações sobre os aspectos estruturais de nível-base. Se a computação reflexiva ocorre em nível de objetos, o metanível é composto de metaobjetos, os quais contêm informações sobre o comportamento dos objetos (instância de classes) do nível-base.
A principal diferença entre a reflexão estrutural e a comportamental é que a primeira realiza a associação de classes a classes de objetos (metaclasses), enquanto a segunda realiza a associação de objetos a objetos do metanível (metaobjetos).
O conceito de metaobjetos foi introduzido como um mecanismo para suportar comportamento reflexivo em linguagens orientadas a objetos. O comportamento computacional de cada objeto que faz parte de uma aplicação é determinado por um metaobjeto que controla e define a operação deste objeto.
Novos comportamentos podem ser adicionados, ou na forma como os métodos são executados podem ser modificados. A reflexão computacional permite o controle do comportamento do objeto ao receber a mensagem, possibilitando a verificação de informações sobre o processo de execução, com o objetivo de monitorá-lo, podendo modificar o curso do mesmo, se necessário. A conexão é realizada ligando-se um objeto a um metaobjeto. Sempre que o objeto receber uma mensagem, o metaobjeto associado intercepta e passa a realizar o tratamento da mensagem, dependendo de informações obtidas durante a computação. O metaobjeto realiza a transformação de atributos do programa em dados disponíveis ao próprio programa e realiza as computações no metanível. Os objetos notificados constituem as metainformações sobre as quais são realizadas computações reflexivas [4].
4. Ferramentas de Apoio
Para utilizar os conceitos comentados acima e, ao mesmo tempo, automatizar o processo de teste de software, podemos realizar a construção de uma ferramenta que nos ajude a executar essas tarefas. Ao realizar testes baseados em estados podemos avaliar as várias mudanças de estados pelas quais passam os objetos de determinada classe, baseando-se no modelo dinâmico da classe (diagrama/máquina de estados); desta forma é possível verificar a integridade dos estados de um objeto durante a execução do programa com a interceptação de mensagens entre objetos, e também é possível, com a reflexão destes, a verificação através de consultas aos valores dos atributos dos objetos.
Uma ferramenta que contempla estes conceitos é o compilador Guaraná [4] onde a interface para interação do usuário com a ferramenta possibilita escolher a aplicação para teste. Logo após é apresentada a hierarquia de classes da aplicação, sendo relacionados, também, os métodos e atributos de cada classe. Em seguida, o usuário especifica quais classes e/ou métodos serão selecionados para serem monitorados. Para cada classe escolhida poderá ser especificada uma variante associada a esta classe e, para cada método, poderá ser especificada a pré e a pós-condição para avaliação, não sendo, entretanto, obrigatória a especificação de asserções.
A próxima etapa desta ferramenta é a de reconfiguração do metanível, com o intuito de se instanciar metaobjetos e associá-los às classes/métodos escolhidos para teste. Executando-se a aplicação, esta seria interrompida quando mensagens fossem encaminhadas a estas classes, sendo transferido o controle da aplicação para o metanível, onde os métodos dos metaobjetos associados fariam os processamentos necessários para a verificação das asserções e, conseqüentemente, a validação dos estados esperados.
Existe também uma outra ferramenta chamada ATeste que auxilia o teste de programas Orientados a Objetos, desenvolvido para uso em aplicações Smalltalk, com a finalidade de prover facilidades ao desenvolvedor de software nas atividades relacionadas à análise de códigos Orientados a Objetos. ATeste implementa uma estratégia de teste, denominada teste dinâmico de caminho, a qual aplica conceitos de teste baseado em estados, aliados às vantagens da Reflexão Computacional [2].
5. Conclusão
Neste artigo foram apresentadas questões relativas às estratégias de testes Orientados a Objetos. Foi mostrado também que estas técnicas e estratégias ainda são imaturas e a atividade de teste ainda se encontra em fase de muitas pesquisas em desenvolvimento por todos os cantos do mundo.
Também foram apresentados, como principal contribuição, esclarecimentos sobre a atividade de teste de software aplicado à Orientação a Objetos, que fornece uma infra-estrutura para capturar informações, realizar verificações e monitorar a execução das aplicações de forma dinâmica sem a necessidade de alteração do código fonte. Isto é possível graças à composição de um conjunto de metaobjetos, coordenados por um gerenciador, que monitora a execução da aplicação, dinamicamente, realizando a análise e a visualização dos resultados sem interferir no seu código, como foi discutido no tópico de reflexão computacional.
O mecanismo de Reflexão Computacional possibilita que interceptações de métodos sejam estabelecidas e supridas de forma dinâmica, sem afetar o funcionamento próprio das classes interceptadas, eliminando, assim, a possibilidade de deixar erros nas aplicações durante o processo de teste. A implementação da estratégia de teste dinâmico de caminho, aplicando conceitos de teste baseado em estado, fornece ao testador uma visão do estado da classe sob teste, antes e após a execução dos métodos requeridos para monitoração, bem como uma visão textual de todo o caminho executado pela aplicação, mostrando todos os métodos executados e os respectivos estados, de maneira ordenada.
Concluímos com este artigo que é necessário estimular, não somente o meio acadêmico praticante, mas todas as pessoas interessadas no tema Testes de Software aplicados à Orientação a Objetos, a proposição de debates em listas de discussão e novos artigos a fim de obter um maior entendimento do assunto em ambas as partes.
6. Referências
1. BINDER, R. V. "The FREE approach to testing object - oriented software: an overview. Chicago: RBSC Corporation., 1994. Disponível em: <http://www.rbsc.com/pages//onlineix.html>. Acesso em: 07 out. 2003.
2. PINTO, I. M.; PRICE, A. A. Um sistema de apoio ao teste de programas orientados a objetos . In: SBES - XII SIMPÓSIO BRASILEIRO DE ENGENHARIA DE SOFTWARE, 1998, Maringá. Anais. Maringá, XII SIMPÓSIO BRASILEIRO DE ENGENHARIA DE SOFTWARE, 1998. v.1, p.87-102.
3. PRESSMAN, R. S. Engenharia de software . São Paulo: Makron Books do Brasil, 1995. p.786.
4. SILVEIRA, F. F. Ferramenta de apoio ao teste de aplicações java baseada em reflexão computacional . Disponível em: <http://www.inf.ufrgs.br/pos/SemanaAcademica/
Semana2000/FabioSilveira>. Acesso em: 20 set. 2003.
5. WERLANG, D. Ferramenta de teste de software implementada em ambiente multiparadigma . Disponível em: <http://www.inf.ufrgs.br/pos/
SemanaAcademica/
Semana99/denisemw/denisemw.html>. Acesso em: 13 set. 2003.