• Nenhum resultado encontrado

2.4 Abordagens de teste de componentes

2.4.1 Teste embutido

Um dos trabalhos relacionados ao aumento da testabilidade dos componentes é o mecanismo de teste embutido (built-in test), que é derivado de técnicas de auto-teste e detecção de defeitos de componentes eletrônicos. Os primeiros pesquisadores a apresentar essa idéia foram Wang et al. (1997, 1999). A ideia geral é incorporar funcionalidades aos componentes para facilitar o seu teste. As funcionalidades de teste compreendem tanto a implementação de interfaces que contenham operações para controlar ou verificar o estado interno dos componentes quanto a implementação de casos de teste para o componente (Wang e King, 2002; Hornstein e Edler, 2002; Gross, 2005).

Com base nos conceitos de teste embutido, um projeto europeu chamado de Component+ estabeleceu uma arquitetura para o desenvolvimento e uso de componentes testáveis. A arquitetura é composta basicamente de três tipos de componentes (Wang e King, 2002; Hornstein e Edler, 2002; Gross, 2005):

• Componente testável: é o componente que será testado e por isso possui mecanismos de teste embutido.

• Componente testador: é o componente que possui casos de teste implementados para testar componentes testáveis por meio da invocação de suas operações regulares ou de teste. É o testador que verifica se o componente está executando corretamente.

• Tratador de exceção (Handler): é utilizado para sinalizar os erros encontrados no compo- nente testado. Esse tipo de componente é importante para a criação de sistemas tolerantes a falhas.

O componente testável possui interfaces para oferecer serviços relacionados à sua funciona- lidade e uma interface de teste. A interface de teste deve conter as seguintes operações (Gross, 2005):

• IBITQuery: determina se o componente fornece uma interface de teste nos padrões da ar- quitetura de teste embutido.

• IBITSetToStateX: operação que altera o componente para um dos estados pré-definidos. • IBITIsInStateX: verifica se o componente está em um estado pré-definido.

CAPÍTULO 2. TESTE DE COMPONENTES DE SOFTWARE 13 • IBITInvokeTester: operação que invoca um componente testador. Entidades externas, como um ambiente de implantação de componentes apresentado por Brenner et al. (2006), por exemplo, podem iniciar a execução dos testes.

Um exemplo genérico de um componente testável pode ser visto na Figura 2.1.

Componente Testavel

IFuncional ITeste

Componente Testavel<<component>> propriedade1 propriedade2 <<IFuncional>> operacao1() operacao2() <<ITeste>> IBITQuery() IBITSetTester(Component) IBITInvokeTester() IBITSetToStateX(State) IBITIsInState() Figura 2.1: Um componente testável.

Um exemplo de arquitetura genérica de um sistema com componentes testáveis e testadores pode ser visto na Figura 2.2. O componente Testavel1 tem uma interface com operações funci- onais (IFuncional) e uma interface de teste (ITeste) que são usadas pelo componente Testador1. O componente Testador1 possui casos de teste para exercitar as funcionalidades do componente Testavel1 e validar os contratos das operações por meio da interface de teste. Os componentes testáveis podem requerer operações de outros componentes testáveis, assim como o componente Testavel1requer operações da interface funcional do componente Testavel2. O componente Testa- vel2é testado pelo componente Testador2.

Componente

Testador1 ITeste ComponenteTestavel1

IFuncional

Componente

Testador2 IFuncionalITeste ComponenteTestavel2

Figura 2.2: Uma arquitetura genérica de componentes testáveis e testadores.

Entre os trabalhos que utilizam componentes com teste embutido, destaca-se o trabalho de Brenner et al. (2006), que desenvolveram um ambiente em que os componentes precisam possuir

CAPÍTULO 2. TESTE DE COMPONENTES DE SOFTWARE 14 funcionalidades de teste para serem testados em tempo de execução. No ambiente desenvolvido, pode-se configurar tarefas que ativarão componentes testadores em diversas ocasiões:

• Lookup-time: quando um componente fizer referência a outro componente, uma rotina de teste será disparada para testar o componente referenciado.

• Call-time: quando um componente invocar uma operação da interface de outro componente, uma rotina de teste será disparada para testar o componente invocado.

• Topology-change: uma rotina de teste será disparada para testar o sistema inteiro quando um componente for trocado por outro ou um novo componente entrar na arquitetura.

• Idle-time: uma rotina de teste para testar o sistema será invocada todas as vezes que um recurso (processador, por exemplo) estiver ocioso. Neste caso é possível determinar o quão ocioso o recurso precisa estar para que a rotina seja ativada.

• Periodic: pode-se configurar a ativação das rotinas de teste em períodos de tempo determi- nados (horas, dias, etc.).

Durante a realização dos testes em tempo de execução, o ambiente pode ter reações automáticas de acordo com os resultados do teste. Uma reação é o shut down, em que o sistema é interrom- pido, e a outra é o try next, em que o componente que não apresenta comportamento esperado é substituído por outro do repositório que tenha a mesma função até que o componente apresente o comportamento esperado de acordo com os testes realizados.

Existem situações em que componentes adquiridos de terceiros não possuem capacidade de teste embutido e não é possível integrá-lo em uma arquitetura específica como a de Brenner et al. (2006). Para resolver o problema, Barbier et al. (2003) criaram a biblioteca BIT/J e um conjunto de ferramentas capazes de introduzir funcionalidades de teste no componente reusado. Com a ferramenta são gerados automaticamente o componente testável e o componente testador. O código gerado do componente testável deverá ser alterado manualmente para adicionar os estados e suas transições. O componente testador deverá ser alterado para incluir os casos de teste.

Uma evolução da biblioteca BIT/J foi proposta por Bruel et al. (2003). A evolução consistiu no uso de aspectos para introduzir as funcionalidades de teste nos componentes. Nesta mesma linha, Moreira et al. (2005) propuseram que, a partir de um modelo de estados de um componente, sejam criados aspectos para introduzir atributos que controlam o estado por meio de declarações inter- tipos e controlam as transições de estados por meio de adendos. Essas abordagens, no entanto, só podem ser aplicadas quando é possível manipular o código objeto do componente por meio de bibliotecas específicas ou linguagens como as orientadas as aspectos, por exemplo.

CAPÍTULO 2. TESTE DE COMPONENTES DE SOFTWARE 15