Um Método Automático de Teste Funcional para a Verificação...

144
Um Método Automático de Teste Funcional para a Verificação de Componentes Daniel Lima Barbosa Dissertação submetida à Coordenação do Curso de Pós-Graduação em Informática da Universidade Federal de Campina Grande – Campus I como parte dos requisitos necessários para obtenção do grau de Mestre em Informática. Área de Concentração: Ciência da Computação Linha de Pesquisa: Engenharia de Software Patricia Duarte de Lima Machado (Orientadora) Jorge César Abrantes de Figueiredo (Co-Orientador) Campina Grande, Paraíba, Brasil c Daniel Lima Barbosa, Junho de 2005

Transcript of Um Método Automático de Teste Funcional para a Verificação...

Um Método Automático de Teste Funcional para a

Verificação de Componentes

Daniel Lima Barbosa

Dissertação submetida à Coordenação do Curso de Pós-Graduação em

Informática da Universidade Federal de Campina Grande – Campus I

como parte dos requisitos necessários para obtenção do graude Mestre

em Informática.

Área de Concentração: Ciência da Computação

Linha de Pesquisa: Engenharia de Software

Patricia Duarte de Lima Machado

(Orientadora)

Jorge César Abrantes de Figueiredo

(Co-Orientador)

Campina Grande, Paraíba, Brasil

c©Daniel Lima Barbosa, Junho de 2005

Ficha Catalográfica Elaborada pela Biblioteca Central da UFCG

B238m Barbosa, Daniel Lima

Um método automático de teste funcional para a verificação de

componentes / Daniel Lima Barbosa. – Campina Grande: UFCG, 2005.

132 f. : il. color.

Referências.

Dissertação (Mestrado em Informática) – Centro de Ciênciase

Tecnologia, Universidade Federal de Campina Grande – UFCG.

Orientadores: Profa. Dra. Patrícia Duarte de Lima Machado.

Prof. Dr. Jorge César Abrantes de Figueiredo.

1 - Engenharia de Software 2 - Componentes de Software

3 - Teste Funcional 4 - Título

CDU 004.41(043)

Resumo

O desenvolvimento baseado em componentes vem sendo cada vezmais considerado para

a construção desoftwarede alta qualidade com baixo custo e tempo de desenvolvimento.

Entretanto, o reuso efetivo de componentes está fortementerelacionado à confiabilidade dos

mesmos. A maioria dos processos de desenvolvimento de componentes realiza as atividades

de teste de formaad hoce apenas no final do desenvolvimento, o que aumenta os custos

inerentes à correção dos defeitos detectados. Por outro lado, quando métodos de teste são

empregados, estes fazem uso de especificações complexas e/ou construídas especificamente

para as atividades de teste, além de nem sempre disporem de ferramentas de suporte para

automatizar estas atividades. Neste trabalho, propomos ummétodo que realiza, de forma

sistemática, a geração, execução e análise de resultados detestes funcionais para compo-

nentes de software a partir de especificações UML e restrições OCL. O método utiliza um

conjunto mínimo de artefatos UML, que constitui o requisitoprincipal para sua aplicação em

conjunto com uma metodologia de desenvolvimento. Uma ferramenta de suporte foi desen-

volvida para automatizar suas atividades e um estudo de casofoi realizado para demonstrar

a aplicação do método, assim como o funcionamento da ferramenta.

ii

Abstract

Component-based development has been even more used to build high quality software in

a cost-effective and reduced-time way. Nevertheless, effective reuse of components is closely

related to their reliability. The majority of component development processes execute test

activities in an ad hoc way and just at the end of development.This may lead to increased

correction costs of detected defects. On the other hand, when test methods are employed,

they use complex and/or test-only specifications. Futhermore, few of those methods have

support tools to automatize their activities. In this work,we propose a method to generate,

execute and analize the results of functional component tests in a systematic way from UML

specifications and OCL constraints. The method uses a minimum set of UML artifacts that

constitute the main requirements to its application in a development methodology. A support

tool has been developed to automatize the activities of the method and a case study was

performed to demonstrate its application as well as tool functionality.

iii

Agradecimentos

Agradeço primeiramente a Deus, por mais esta graça alcançada em minha vida. A meus

pais e irmãs, pelo apoio inestimável durante toda a minha formação. A minha amada esposa

Márcia, pelo carinho, compreensão e apoio constante, principalmente nos momentos mais

difíceis. A meus orientadores, Patrícia Duarte de Lima Machado e Jorge César Abrantes

de Figueiredo, por todo o empenho, dedicação, incentivo e confiança, sem os quais este

trabalho não seria possível. A Carina Machado, pela elaboração do trabalho que serviu

de base para este. Aos companheiros de pesquisa, Wilkerson Andrade, Helton Souza e

Cidinha Gouveia, pelo esforço empregado na busca por resultados. Aos demais amigos

integrantes do Grupo de Métodos Formais: Ana Emíla, André, Afrânio, Amâncio, Cássio,

Daniel Aguiar, Elthon, Emerson, Fabrício, Flávio, Jaírson, Paulo, Rogério e Taciano, pelos

dois anos incríveis compartilhados no LabPetri. Aos membros da Banca Examinadora, pela

atenção e ricas sugestões. Ao CNPq e à CAPES, pelo apoio financeiro. Por fim, gostaria

de agradecer a todos que, de uma forma ou de outra, através de palavras, gestos ou orações,

contribuíram para o sucesso deste trabalho.

iv

Conteúdo

1 Introdução 1

1.1 Objetivos do Trabalho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.2 Metodologia de Trabalho . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.3 Resultados e Relevância . . . . . . . . . . . . . . . . . . . . . . . . . . .4

1.4 Estrutura da Dissertação . . . . . . . . . . . . . . . . . . . . . . . . . .. 6

2 Fundamentação Teórica 8

2.1 Teste de Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.1.1 Tipos de Teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.1.2 Terminologia de Teste . . . . . . . . . . . . . . . . . . . . . . . . 10

2.2 Componentes de Software . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.2.1 Definição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.2.2 Vantagens e Desvantagens do Desenvolvimento Baseadoem Com-

ponentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.3 Teste de Componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.4 O Método FCT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.4.1 Planejamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.4.2 Especificação dos Testes . . . . . . . . . . . . . . . . . . . . . . . 17

2.4.3 Construção, Execução e Análise de Resultados . . . . . . .. . . . 22

2.4.4 Empacotamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2.5 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23

3 O Método AFCT 24

3.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

v

CONTEÚDO vi

3.2 Especificação de Teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3.2.1 Seleção dos Casos de Teste . . . . . . . . . . . . . . . . . . . . . . 28

3.2.2 Geração de Oráculos . . . . . . . . . . . . . . . . . . . . . . . . . 34

3.2.3 Seleção de Dados de Teste . . . . . . . . . . . . . . . . . . . . . . 36

3.3 Construção e Empacotamento dos Artefatos de Teste . . . . .. . . . . . . 39

3.4 Execução dos Testes e Análise de Resultados . . . . . . . . . . .. . . . . 41

3.5 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42

4 A Ferramenta SPACES 43

4.1 Características da Ferramenta . . . . . . . . . . . . . . . . . . . . .. . . . 43

4.2 Visão Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

4.3 Projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

4.3.1 Gerenciador de Persistência . . . . . . . . . . . . . . . . . . . . .50

4.3.2 Parser XMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

4.3.3 Seletor de Casos de Teste . . . . . . . . . . . . . . . . . . . . . . . 53

4.3.4 Gerador de Oráculos . . . . . . . . . . . . . . . . . . . . . . . . . 57

4.3.5 Seletor de Dados de Teste . . . . . . . . . . . . . . . . . . . . . . 63

4.3.6 Gerador de Artefatos de Teste . . . . . . . . . . . . . . . . . . . . 67

4.3.7 Empacotador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

4.4 Testador de Componentes . . . . . . . . . . . . . . . . . . . . . . . . . . .73

4.5 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76

5 Estudo de Caso 77

5.1 Descrição da Aplicação e Escolha do Componente . . . . . . . .. . . . . 77

5.2 Especificando o Componente . . . . . . . . . . . . . . . . . . . . . . . . .78

5.3 Aplicando o Método AFCT . . . . . . . . . . . . . . . . . . . . . . . . . . 86

5.3.1 Planejando o Teste . . . . . . . . . . . . . . . . . . . . . . . . . . 86

5.3.2 Especificando os Testes . . . . . . . . . . . . . . . . . . . . . . . . 87

5.3.3 Construindo e Empacotando os Artefatos de Teste . . . . .. . . . 91

5.3.4 Executando o Teste . . . . . . . . . . . . . . . . . . . . . . . . . . 93

5.4 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . .96

CONTEÚDO vii

6 Conclusão 99

6.1 Contribuições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

6.2 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

A Mapeamento OCL – Java 109

A.1 Declaração de Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

A.2 Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

A.3 Operadores (Numéricos, Relacionais e Lógicos) . . . . . . .. . . . . . . . 110

A.4 Operações sobre Strings . . . . . . . . . . . . . . . . . . . . . . . . . . .111

A.5 Construções Básicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

A.6 Construções Collection . . . . . . . . . . . . . . . . . . . . . . . . . . .. 112

B Subconjunto XMI Utilizado 117

C API UML 127

Lista de Figuras

2.1 Interfaces de Componentes . . . . . . . . . . . . . . . . . . . . . . . . .. 13

2.2 Teste de Componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.3 Exemplo de Modelo de Uso de Método FCT . . . . . . . . . . . . . . . . .19

3.1 Integração entre Metodologias de Desenvolvimento de SBC e o Método de

Teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3.2 Algoritmo para a Criação de Modelos de Uso . . . . . . . . . . . . .. . . 29

3.3 Formato de Modelo de Uso do Método AFCT . . . . . . . . . . . . . . . .30

3.4 Algoritmo para a Criação de Diagramas de Seqüência a partir de Modelos

de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

3.5 Algoritmo para a Seleção de Caminhos em Modelos de Uso . . .. . . . . 32

3.6 Exemplo de Conversão de OCL para Pseudo-Código . . . . . . . .. . . . 35

4.1 Arquitetura da Ferramenta SPACES . . . . . . . . . . . . . . . . . . .. . 45

4.2 SPACES: Tela Principal . . . . . . . . . . . . . . . . . . . . . . . . . . . .47

4.3 SPACES: Menus Arquivo e Projeto . . . . . . . . . . . . . . . . . . . . .. 47

4.4 SPACES: Abrindo Arquivos XMI . . . . . . . . . . . . . . . . . . . . . . 48

4.5 Visão Geral do Projeto da Ferramenta SPACES . . . . . . . . . . .. . . . 49

4.6 SPACES: Tela Salvar Projeto . . . . . . . . . . . . . . . . . . . . . . . .. 50

4.7 SPACES: Gerenciador de Persistência . . . . . . . . . . . . . . . .. . . . 51

4.8 SPACES: Tela de Seleção de Casos de Teste . . . . . . . . . . . . . .. . . 53

4.9 SPACES: Modelo de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

4.10 SPACES: Tela de Edição de Probabilidades . . . . . . . . . . . .. . . . . 55

4.11 SPACES: Probabilidades Alteradas no Modelo de Uso . . . .. . . . . . . 55

4.12 SPACES: Seletor de Casos de Teste . . . . . . . . . . . . . . . . . . .. . 56

viii

LISTA DE FIGURAS ix

4.13 Dresden OCL: Integração entre os Módulos . . . . . . . . . . . .. . . . . 58

4.14 Dresden OCL: Exemplo de Árvore Sintática Abstrata . . . .. . . . . . . . 58

4.15 Integração Dresden OCL - SPACES: Acesso ao Modelo UML . .. . . . . 59

4.16 Dresden OCL: Gerador de Código . . . . . . . . . . . . . . . . . . . . .. 60

4.17 SPACES: Gerador de Oráculos . . . . . . . . . . . . . . . . . . . . . . .. 62

4.18 SPACES: Seletor de Dados . . . . . . . . . . . . . . . . . . . . . . . . . .64

4.19 SPACES: Gerador de Artefatos de Teste . . . . . . . . . . . . . . .. . . . 68

4.20 SPACES: Hierarquia do Código de Teste . . . . . . . . . . . . . . .. . . . 68

4.21 SPACES: Formato de Métodos de Teste para Cenários . . . . .. . . . . . . 69

4.22 JTestCase: Formato de Arquivos XML . . . . . . . . . . . . . . . . .. . . 70

4.23 SPACES: Formato XML para Descrição do Código de Teste Gerado . . . . 71

4.24 SPACES: Empacotador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

4.25 Testador de Componentes: Tela Principal . . . . . . . . . . . .. . . . . . 74

4.26 Testador de Componentes: Tela de Alteração de Dados . . .. . . . . . . . 76

5.1 Diagrama de Componentes do Sistema de Reservas . . . . . . . .. . . . . 78

5.2 Diagrama de Funcionalidades para o ComponenteGerenciador de Hotéis. 79

5.3 Modelo Conceitual do ComponenteGerenciador de Hotéis. . . . . . . . . 80

5.4 Modelo de Informação do ComponenteGerenciador de Hotéis. . . . . . . 80

5.5 Diagrama que descreve o cenário de usoHotel Inexistenteda funcionalidade

Fazer Reserva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

5.6 Diagrama que descreve o cenário de usoTipo de Quarto Inexistenteda fun-

cionalidadeFazer Reserva . . . . . . . . . . . . . . . . . . . . . . . . . . 82

5.7 Diagrama que descreve o cenário de usoPeríodo Inválidoda funcionalidade

Fazer Reserva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

5.8 Diagrama que descreve o cenário de usoQuarto Não Disponívelda funcio-

nalidadeFazer Reserva. . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

5.9 Diagrama que descreve o cenário de usoSucessoda funcionalidadeFazer

Reserva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

5.10 Selecionando os casos de teste com SPACES . . . . . . . . . . . .. . . . 87

5.11 Modelo de Uso para a Funcionalidade Fazer Reserva . . . . .. . . . . . . 88

LISTA DE FIGURAS x

5.12 Modelo de Uso para a Funcionalidade Cancelar Reserva . .. . . . . . . . 89

5.13 Oráculo correspondente à pré-condição do cenárioPeríodo Inválidoda Fun-

cionalidadeFazer Reserva . . . . . . . . . . . . . . . . . . . . . . . . . . 90

5.14 Oráculo correspondente à pós-condição do cenárioPeríodo Inválidoda Fun-

cionalidadeFazer Reserva . . . . . . . . . . . . . . . . . . . . . . . . . . 91

5.15 Dados selecionados para o cenárioTipo de Quarto Inexistenteda funciona-

lidadeFazer Reserva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

5.16 Hierarquia de Classes de Teste geradas para o Componente Gerenciador de

Hoteis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

5.17 Execução dos Testes: Detecção de Falhas . . . . . . . . . . . . .. . . . . 94

5.18 Execução dos Testes: Revisando os Dados de Teste . . . . . .. . . . . . . 95

5.19 Execução dos Testes: Testes Ok . . . . . . . . . . . . . . . . . . . . .. . 96

C.1 API UML: Visão Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

C.2 API UML: Hierarquia de Tipo . . . . . . . . . . . . . . . . . . . . . . . . 129

C.3 API UML: Associação . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

C.4 API UML: Generalização e Abstração . . . . . . . . . . . . . . . . . .. . 130

C.5 API UML: Colaboração e Diagrama de Seqüência . . . . . . . . . .. . . . 131

C.6 API UML: Hierarquia de Ações . . . . . . . . . . . . . . . . . . . . . . . 132

C.7 API UML: Ator e Caso de Uso . . . . . . . . . . . . . . . . . . . . . . . . 132

Lista de Tabelas

2.1 Exemplo de Tabela de Decisão do Método FCT . . . . . . . . . . . . .. . 21

3.1 Esquema de Conversão de Restrições OCL para Código Fonte. . . . . . . 35

3.2 Padrões Lógicos para Identificação de Partições de Equivalência . . . . . . 37

3.3 Frameworks de TestexUnit . . . . . . . . . . . . . . . . . . . . . . . . . . 40

5.1 Cenários de Uso das Funcionalidades do ComponenteGerenciador de Hotéis 81

5.2 Determinando o número de casos de teste a ser realizados para cada funcio-

nalidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

A.1 Mapeamento OCL – Java: Declaração de Context . . . . . . . . . .. . . . 109

A.2 Mapeamento OCL – Java: Tipos . . . . . . . . . . . . . . . . . . . . . . . 110

A.3 Mapeamento OCL – Java: Operadores . . . . . . . . . . . . . . . . . . .. 111

A.4 Mapeamento OCL – Java: Operações sobre Strings . . . . . . . .. . . . . 111

A.5 Mapeamento OCL – Java: Construções Básicas . . . . . . . . . . .. . . . 111

A.6 Mapeamento OCL – Java: Construções Collection . . . . . . . .. . . . . 116

xi

Capítulo 1

Introdução

O desenvolvimento de produtos de qualidade é hoje o maior desafio da indústria desoft-

ware. Para tanto, processos de teste bem planejados e executadossão necessários de forma a

garantir que o produto final do desenvolvimento seja o mais livre de defeitos possível. Entre-

tanto, como observado por McGregor[MS01], a concepção tradicional do processo de teste

como sendo uma fase final e independente do processo de desenvolvimento propriamente

dito, tem se mostrado muito ineficiente no que diz respeito aocusto necessário tanto para

reparar os erros encontrados quanto com a própria manutenção dosoftware.

Neste contexto, o teste funcional surge como uma técnica capaz de amenizar os custos

inerentes ao processo de teste, uma vez que, a partir da especificação dosoftware, casos de

teste podem ser obtidos paralelamente ao seu desenvolvimento. Em função disto, muitas

pesquisas vêm sendo desenvolvidas com o objetivo de produzir técnicas efetivas para a deri-

vação de casos de teste a partir da especificação dos sistemas. Entretanto, isso só é possível

se essa especificação for precisamente definida, caracterizando com exatidão o comporta-

mento desejado para o sistema[Bei95]. Uma forma de se alcançar essa precisão é a partir da

utilização de especificações formais.

Por outro lado, o desenvolvimento de especificações complexas e específicas para propó-

sitos de teste só é justificado para projetos de alta complexidade e risco, como, por exemplo,

sistemas de tempo real, em função dooverhead(número de atividades extras) necessário

para se gerar esses artefatos. Dessa forma, a utilização de especificações UML (Unified

Modeling Language) [OMG03b], tradicionalmente empregadas nas fases de análise e pro-

jeto dos processos de desenvolvimento desoftware, como fonte de informações relevantes

1

2

para teste, aparece como uma alternativa altamente atraente uma vez que não se faz mais

necessário a criação de artefatos específicos para propósitos de teste[BL01; OA99; FL00;

VDR00; RPG01; CCD+02]. Entretanto, é preciso, de alguma forma, compensar a falta de

formalidade das especificações UML para que estas possam serutilizadas na geração auto-

mática de casos de teste. Uma forma interessante de resolveresse problema é enriquecendo

os artefatos UML com restrições OCL (Object Constraint Language) [OMG03a], provendo,

desta forma, informações relacionadas a invariantes, pré epós-condições e outros compor-

tamentos mais específicos. Embora o OCL esteja cada vez mais presente nas especificações

UML, ainda não existem muitas pesquisas acerca de sua utilização como fator formalizador

de especificações UML para propósitos de teste.

Um ponto de convergência entre os pesquisadores e especialistas da área é a necessidade

de automação destas técnicas de forma que todo o processo de teste - geração, execução e

análise de resultados - possa ser executado (e re-executado) com a menor interferência hu-

mana possível. Entretanto, poucos são os trabalhos que apresentam ferramentas realmente

em produção[TB02; FJJV97]. A grande maioria destas ferramentas é composta por pro-

tótipos cujos projetos nunca vêm a ser finalizados. Além disso, as poucas ferramentas em

produção não abrangem todo o processo de teste - muitas realizam apenas a geração dos ca-

sos de teste - e a maioria delas faz uso de especificações mais complexas e menos utilizadas

do que o UML, a exemplo da ferramenta apresentada por Tretmans [TB02].

Aliada a esta busca por processos de teste mais eficientes, a composição de sistemas a

partir de componentes, em função de sua alta capacidade de reuso, também desponta como

um fator de determinação desoftwarede alta qualidade, rápida construção e baixo custo.

Para tanto os componentes precisam ser testados de forma a garantir a sua confiabilidade e,

conseqüentemente, a confiabilidade do sistema como um todo.

Do ponto de vista de um componente, os testes a serem realizados precisam satisfazer

duas expectativas distintas. Do lado do fornecedor, é preciso garantir que o componente se

comportará adequadamente sob os mais diferentes contextosem que venha a ser utilizado.

Do lado do cliente, é necessário assegurar que o componente apresente a funcionalidade

desejada quando integrado aos demais componentes para compor uma aplicação. Para que

isso seja possível, é necessário que os componentes apresentem interfaces bem especificadas.

Em função disto, métodos para a verificação funcional de componentes e da integração

1.1 Objetivos do Trabalho 3

destes na composição de sistemas vem sendo propostos[dF03; MTY01; GMF04; CWO03].

Em particular, em[dF03], Farias propõe o método FCT (Functional Component Testing),

um método de teste funcional aplicável a componentes desoftware. A principal contribuição

desse trabalho está em possibilitar a verificação de propriedades individuais dos compo-

nentes e empacotar artefatos e resultados de teste de forma adequada, facilitando o reuso e

a composição dos mesmos pelos clientes. Esta abordagem é complementada por Gouveia

[GMF04] que apresenta um método de teste de integração funcional para sistemas basea-

dos em componentes com a finalidade de testar o componente dentro do contexto onde ele

será inserido, isto é, testar a forma como ele irá interagir com a aplicação e com os demais

componentes que irão compor o sistema.

Entretanto, embora cubram todo o processo de teste, estes métodos não possuem fer-

ramentas de suporte e, em função de definições incompletas e/ou informais, apresentam

atividades bastante subjetivas e de difícil automação, o que constitui um obstáculo para a

construção destas ferramentas. Dessa forma, todas as suas etapas precisam ser manualmente

executadas. Com isso, até mesmo aplicações simples demandam um grande esforço por

parte das equipes de desenvolvimento em atividades de teste, tornando-se, assim, um pro-

cesso bastante oneroso. A redefinição destas atividades e o desenvolvimento de ferramentas

de suporte é, portanto, fundamental para garantir a viabilidade destes métodos em aplicações

práticas.

1.1 Objetivos do Trabalho

O objetivo maior deste trabalho é a definição de um novo métodode teste funcional para

componentes desoftwarecujas atividades sejam precisamente definidas de forma a possi-

bilitar o desenvolvimento de um ferramental de suporte que realize, de forma automática,

a geração, execução e análise de resultados de casos de testes. Esse método, que é forte-

mente baseado no método FCT[dF03], cobrirá inicialmente apenas a perspectiva de teste do

fornecedor e não estará limitado a uma única metodologia de desenvolvimento.

O ferramental de suporte a ser desenvolvido deverá apresentar, dentre outras, as seguintes

características:

• Suporte a múltiplas ferramentas de modelagem UML;

1.2 Metodologia de Trabalho 4

• Utilização de artefatos produzidos com a especificação UML durante as fases de aná-

lise e projeto dos processos de desenvolvimento desoftware;

• Geração, implementação, execução e análise de resultado decasos de teste para os

componentes;

• Empacotamento dos casos de teste e dados experimentais gerados para o componente

de forma a permitir a sua distribuição e a reexecução dos testes pelos usuários do

mesmo.

1.2 Metodologia de Trabalho

Com o intuito de se atingir o objetivo proposto para este trabalho, inicialmente foi feita

uma análise crítica do estado da arte em automação de teste funcional parasoftwareorientado

a objetos e/ou baseado em componentes. Em seguida fundamentamos o trabalho no estudo

das linguagens UML e OCL, das principais técnicas empregadas no teste de componentes

de software, e das atividades e técnicas empregadas no método FCT[dF03]. A partir das

conclusões tomadas e das metas traçadas, realizamos a definição de cada uma das atividades

e etapas do novo método de teste assim como o desenvolvimentodo ferramental de suporte.

Por fim, com o objetivo de guiar e fornecerfeedbackspara estas definições, construímos a

especificação e implementação de um componente a ser usado como estudo de caso.

1.3 Resultados e Relevância

O principal resultado deste trabalho é a disponibilização de um método para a geração,

execução e análise de resultados de casos de teste para componentes desoftwarede forma

automática e a partir de especificações UML e restrições OCL das fases de análise e projeto

dos processos de desenvolvimento.

No ciclo de desenvolvimento desoftware, o teste é, sem sombra de dúvidas, a ativi-

dade em que podemos observar a maior distância entre a teoria(métodos e técnicas de teste

propostas) e a prática (real aplicação destas técnicas). Uma das principais causas disso é

o impacto causado pela adoção destas propostas, seja em função dooverheadnecessário à

1.3 Resultados e Relevância 5

sua utilização, ou devido ao alto grau de complexidade e/ou formalismo das especificações

necessárias.

Ao contrário do que ocorre com outras propostas, o método utiliza uma especificação

usual, padronizada e de grande utilização pela indústria, eos artefatos necessários já são

comumente desenvolvidos pelas principais metodologias dedesenvolvimento, sendo des-

necessária a construção de artefatos específicos para propósitos de teste, fatores esses que

facilitam a sua adoção.

Estudos mostram que cerca de 50% do custo do desenvolvimentode umsoftwareé desti-

nado à atividade de teste[Har00; Bei90]. Com o uso do método, estes custos podem vir a ser

drasticamente reduzidos, uma vez que, faltas e/ou inconsistências na especificação podem ser

detectadas nas primeiras etapas dos processos de desenvolvimento, tornando mais simples e

barata a sua correção e evitando a sua propagação para as etapas futuras do desenvolvimento.

Outra contribuição do trabalho é o desenvolvimento e disponibilização do ferramental

de suporte necessário à automação das atividades do método de teste. Apesar de algumas

ferramentas desenvolvidas para teste funcional de sistemas orientados a objetos poderem ser

aplicadas também ao teste de componentes, muitas se baseiamem artefatos produzidos ex-

clusivamente para propósitos de teste e/ou em especificações pouco usuais[PBB97; Gra94;

TB02]. Além disso, nenhuma dessas ferramentas dá enfoque às características específicas

dos componentes desoftware, a exemplo da verificação de propriedades e da forma como es-

tes interagem entre si. Com tais ferramentas de suporte, o esforço empregado no processo de

teste dos componentes será minimizado, o que viabiliza a suautilização em sistemas comple-

xos e/ou de grande porte. Além disso, a diminuição do número de atividades manuais reduz a

chance de inserção de defeitos durante a execução do processo de teste, maximizando assim

a confiabilidade dos resultados obtidos e, conseqüentemente, a qualidade do produto final.

Por fim, o trabalho contribuirá com um estudo de caso a fim de demonstrar a aplicação

do método de teste e da ferramenta de suporte.

1.4 Estrutura da Dissertação 6

1.4 Estrutura da Dissertação

O restante deste documento foi organizado da seguinte forma:

Capítulo 2: Fundamentação Teórica Na fundamentação teórica, apresentaremos os prin-

cipais conceitos e terminologias relativos a teste e componentes desoftware, que foram

abordados neste trabalho. Por fim, será apresentado o métodoFCT, no qual este trabalho

está baseado, a partir da descrição de suas fases e atividades.

Capítulo 3: O Método de Teste AFCT Neste capítulo, será apresentado o método de

teste proposto. Serão enumerados os requisitos para a sua aplicação e serão descritas as suas

etapas e atividades assim como os algoritmos e técnicas utilizadas na sua definição.

Capítulo 4: A Ferramenta SPACES A ferramenta SPACES será apresentada neste capí-

tulo. A ferramenta foi construída para dar suporte às atividades do método AFCT. Será feita

uma descrição da sua arquitetura e, para cada módulo, serão apresentadas as funcionalidades

e os principais algoritmos.

Capítulo 5: Estudo de Caso Neste capítulo será demonstrada uma aplicação prática do

método AFCT e da ferramenta SPACES. Um componente será selecionado para ser especi-

ficado, implementado e testado segundo as diretrizes do método de teste proposto e com o

auxílio da ferramenta. Ao final, os resultados obtidos serãoanalisados.

Capítulo 6: Conclusões O trabalho será concluído neste capítulo a partir de uma avaliação

dos resultados obtidos assim como uma descrição das perspectivas de trabalhos futuros.

Apêndice A No Apêndice A, é apresentado o mapeamento proposto da linguagem OCL

para um subconjunto da linguagem de programação Java. Este mapeamento foi utilizado

para a geração dos oráculos de teste sob a forma de código-fonte Java a partir das restrições

OCL.

1.4 Estrutura da Dissertação 7

Apêndice B No Apêndice B, apresentamos um DTD que descreve o subconjunto dos ele-

mentos XMI considerados para a obtenção de informações do modelo UML a partir da fer-

ramenta SPACES.

Apêndice C No Apêndice C, são apresentados os diagramas que descrevem as classes

componentes da API UML, que é instanciada a partir do arquivoXMI para disponibilizar as

informações do modelo UML da especificação dos componentes para a ferramenta SPACES.

Capítulo 2

Fundamentação Teórica

Este capítulo tem como objetivo principal fornecer um suporte técnico para os leitores

com relação aos conceitos empregados neste trabalho. Serãoapresentados os principais

conceitos relacionados a teste, componentes e teste de componentes. O método FCT, no

qual este trabalho está baseado, também será apresentado neste capítulo a partir da descrição

de suas fases e atividades.

2.1 Teste de Software

À medida que cresce a utilização de sistemas desoftwarena execução de tarefas críticas,

cresce também a exigência de produtos cada vez mais qualificados. Teste é uma das técni-

cas de verificação desoftwaremais utilizadas na prática. Se usado de forma efetiva, pode

fornecer indicadores importantes sobre a qualidade e confiabilidade de um produto.

O teste desoftwareé uma atividade que consiste na descoberta de defeitos que possam

ter sido introduzidos em qualquer fase do desenvolvimento ou manutenção de sistemas de

softwaree que, em geral, são decorrentes de omissões, inconsistências ou mau entendimento

das especificações ou dos requisitos dos sistemas[MS01]. Trata-se de um processo de difícil

execução para o qual, raramente, os recursos necessários estão disponíveis.

O teste é importante porque contribui substancialmente para certificarmos que uma apli-

cação faz tudo o que era esperado que fizesse, embora existam também alguns esforços que

vão além disso, e tentam assegurar que as aplicações não fazem nada além do especificado.

De qualquer forma, eles fornecem meios de avaliar a existência de defeitos que poderiam

8

2.1 Teste de Software 9

resultar em perda de tempo, clientes ou até mesmo, no caso de sistemas críticos, de vidas.

Embora o principal objetivo do teste seja a detecção de defeitos, existem modalidades

de teste mais específicas que visam a certificação de alguns requisitos não funcionais das

aplicações, a exemplo dos testes de segurança, destresse de performance.

Por muito tempo o teste desoftwarefoi definido dentro dos processos de desenvolvi-

mento como uma atividade isolada e que só era realizada, de maneira informal, após a im-

plementação dos sistemas. Entretanto, o alto custo atribuído a esta etapa – cerca de 50% do

custo total de desenvolvimento, segundo alguns estudos[Har00; Bei90] – demonstrou que

essa não era uma prática adequada. Isso contribuiu para a definição de métodos e técnicas

sistemáticas de teste que constituíssem um processo a parteque pudesse ser aplicado, para-

lelamente, ao longo do processo de desenvolvimento[MS01]. Dessa forma, as atividades de

teste poderiam ser aplicadas cada vez mais cedo, contribuindo para a verificação não ape-

nas do código desenvolvido, mas também para a descoberta de faltas nos próprios artefatos

produzidos pelas etapas do processo de desenvolvimento, evitando, assim, a sua propagação

para as etapas subseqüentes e reduzindo drasticamente o esforço e o custo decorrente de sua

correção.

2.1.1 Tipos de Teste

Segundo Beizer[Bei95], três estratégias podem ser utilizadas para se realizar teste de

softwarecom o objetivo de detectar defeitos:

• Teste Funcional: Observa se um programa está de acordo com a sua especificação,

independentemente do código fonte. Teste funcional é também conhecido como teste

black-boxou teste comportamental.

• Teste Estrutural: Baseia-se na estrutura do objeto testado. Requer, portanto, acesso

completo ao código fonte do programa para ser realizado. É também conhecido por

testeglass-boxou testewhite-box.

• Teste Híbrido: Combina as duas estratégias de teste anteriores.

Os testes funcional e estrutural são técnicas complementares. Alguns defeitos são tão re-

lacionados à forma como uma determinada funcionalidade foiimplementada que só podem

2.1 Teste de Software 10

ser revelados a partir do desenvolvimento de testes baseadona implementação desta fun-

cionalidade, e portanto, estruturais. Por outro lado, a verificação da conformidade de uma

funcionalidade para com sua especificação através das técnicas funcionais, nos permite um

controle maior sobre o que osoftwarefaz, possibilitando a detecção de defeitos mais con-

ceituais e menos dependentes da implementação em si, a exemplo de inconsistência entre

modelos.

Segundo J. Chang et. al.[CR99], teste funcional é freqüentemente mais fácil de compre-

ender e realizar do que as técnicas de teste tradicionais baseadas em código, porque o teste

funcional baseia-se em uma descrição “black-box” da funcionalidade dosoftware, enquanto

que as técnicas de teste estrutural exigem um conhecimento mais detalhado da implementa-

ção. Neste trabalho, nos concentraremos apenas nas técnicas de teste funcional, estando as

técnicas estruturais fora de seu escopo.

Uma vez que são baseadas na especificação dosoftware, as técnicas de teste funcional

podem ser aplicadas antes mesmo da disponibilização da implementação, contribuindo para

o aperfeiçoamento dos próprios artefatos de desenvolvimento. Além disso, quando as espe-

cificações são construídas a partir de linguagens com semântica formal ou semi-formal, o

teste funcional pode ser realizado de forma parcial ou completamente automática, minimi-

zando o esforço necessário à sua aplicação assim como o riscode inserção de erros durante

a execução manual de suas atividades.

Em geral, os testes funcionais compreendem três componentes básicos:Casos de Teste,

Oráculose Dados de Teste. Estes e outros conceitos relacionados à atividade de testesão

apresentados na seção 2.1.2.

2.1.2 Terminologia de Teste

Teste desoftwareapresenta um vocabulário bem estabelecido. A seguir, são listados

alguns dos termos mais comumente usados[Bei95; Bin99]:

• Falha: É a manifestação dosoftwareem não executar de forma correta uma determi-

nada funcionalidade. Em função disso, o sistema em execuçãoapresenta algum tipo de

comportamento não esperado, ou seja, a saída produzida não coincide com o resultado

previsto.

2.2 Componentes de Software 11

• Defeito ou Falta: Termos equivalentes que são usados para indicar a ausênciade có-

digo ou a presença de código incorreto no sistema, o que pode ocasionar uma falha no

mesmo.

• Erro: É a ação humana que dá origem a uma falta.

• Caso de Teste: Aspecto ou funcionalidade a ser testado em um sistema, cujocompor-

tamento esperado é expresso por critérios de entrada e aceitação.

• Dados de Teste: Valores de entrada necessários para a execução de um caso deteste.

• Oráculo: Especificação de procedimentos responsáveis por verificarse uma dada exe-

cução do sistema (estimulada pelos dados de entrada) está deacordo com o comporta-

mento definido pelos casos de teste.

• Validação: Processo que avalia se a especificação de um objeto está de acordo com o

domínio do problema. Este processo assegura que osoftwareproduzido é osoftware

correto.

• Verificação: Processo que avalia se a implementação de um objeto satisfaz os requi-

sitos declarados em sua especificação. Assume-se neste processo que a especificação

está correta. Este processo assegura que osoftwareestá sendo produzido corretamente.

• Testabilidade: Propriedade fundamental que engloba todos os aspectos quefacilitam

a elaboração e execução dos testes dosoftware.

2.2 Componentes de Software

Na indústria desoftware, requisitos relativos a menores custos de produção e ma-

nutenção, maior rapidez na entrega de sistemas e aumento da qualidade estão cada vez

mais presentes, e a sua satisfação é determinante para o sucesso dos produtos em de-

senvolvimento assim como para a sobrevivência das própriasorganizações em um mer-

cado cada vez mais competitivo. Entre os pesquisadores, é consenso que tais requisi-

tos só podem ser atendidos pelo reuso generalizado e sistemático de software [Som03;

2.2 Componentes de Software 12

Szy98]. O desenvolvimento desoftwarebaseado em componentes é uma das técnicas de

reuso que vem apresentando melhores resultados na prática[Som03].

A engenharia desoftwarebaseada em componentes propõe a concepção de sistemas

a partir da combinação de unidades pré-desenvolvidas com o objetivo de reduzir o tempo

de desenvolvimento, facilitar o acoplamento de novas funcionalidades, ou a mudança de

funcionalidades já existentes e ainda promover o reuso de partes dosoftware. Contudo,

embora tenha havido interesse no reuso de componentes desdeo início da década de 80,

foi somente nos últimos anos que ele se tornou aceito como umaabordagem prática para o

desenvolvimento de sistemas desoftware[Som03].

2.2.1 Definição

Na engenharia desoftware, o termo Componente possui diversas definições. Uma das

definições mais usadas e a que consideramos neste trabalho é ade C. Szyperski[Szy98]:

"Um componente de software é uma unidade de composição com interfaces

especificadas através de contratos e dependências de contexto explícitas, que

pode ser distribuída independentemente e está sujeita a composição com outras

partes."

Desta definição podemos concluir que o componente é uma peça de software que pode

ser desenvolvida de forma isolada e que é projetada para utilização em diferentes contextos

de um determinado domínio de aplicação. A definição também indica que um componente

encapsula suas características, estando portanto bem separado de seu ambiente e de outros

componentes. Sua interação com o mundo exterior se dá através de especificações bem

definidas acerca do que o componente requer para o seu corretofuncionamento e de quais os

serviços por ele fornecidos.

Estas especificações são as interfaces que constituem o contrato de utilização do mesmo

(Figura 2.1). A interfacerequires(requer) especifica quais os serviços que devem ser for-

necidos pelo sistema ou, eventualmente, por outros componentes, para possibilitar o correto

funcionamento do componente. A interfaceprovides(fornece), por sua vez, define os servi-

ços fornecidos pelo componente.

2.2 Componentes de Software 13

Figura 2.1: Interfaces de Componentes

2.2.2 Vantagens e Desvantagens do Desenvolvimento Baseadoem Com-

ponentes

O desenvolvimento de sistemas baseado em componentes apresenta uma série de vanta-

gens em relação às técnicas de desenvolvimento tradicionais [Som03]:

• Maior confiabilidade. Os componentes reutilizados que são empregados nos siste-

mas em operação são, em geral, mais confiáveis do que os componentes novos. Eles já

foram experimentados e testados em diferentes ambientes. Os defeitos de projeto e im-

plementação são descobertos e eliminados no uso inicial doscomponentes, reduzindo,

assim, o número de falhas quando o componente é reutilizado.

• Redução dos riscos de processo. Se recorrermos a um componente já existente, serão

menores as incertezas sobre os custos relacionados ao reusodesse componente do que

sobre custos de desenvolvimento. Esse é um fator importantepara o gerenciamento de

projetos, pois reduz as incertezas nas estimativas de custos de projeto.

• Uso efetivo de especialistas. Em vez de os especialistas em aplicações fazerem o

mesmo trabalho em diferentes projetos, eles podem desenvolver componentes reutili-

záveis, que englobam seu conhecimento.

• Desenvolvimento acelerado. De modo geral, é mais importante fornecer um sis-

tema para o mercado o mais rápido possível do que se prender aos custos gerais de

desenvolvimento. O reuso de componentes acelera a produção, porque o tempo de

desenvolvimento e o de validação devem ser reduzidos.

Apesar das vantagens apresentadas, algumas desvantagens e/ou problemas também pre-

cisam ser considerados durante o projeto de sistemas baseados em componentes. Algumas

2.3 Teste de Componentes 14

destas desvantagens, que estão relacionadas aos requisitos para implantação do desenvolvi-

mento baseado em componentes em si e à mudança de culturas organizacionais, são listadas

a seguir[Som03].

• Aumento nos custos de manutenção. Se o código-fonte do componente não estiver

disponível, então os custos de manutenção poderão aumentar, uma vez que os ele-

mentos reutilizados no sistema podem se tornar crescentemente incompatíveis com as

mudanças do sistema.

• Síndrome do ‘não-foi-inventado-aqui’. Alguns engenheiros desoftwareàs vezes

preferem reescrever componentes porque acreditam que podem fazer melhor que o

componente reutilizável. Isso tem a ver em parte com a confiança e em parte com o fato

de que escrever umsoftwareoriginal é visto como mais desafiador do que reutilizar o

softwarede outras pessoas.

• Manutenção de uma biblioteca de componentes. Implementar uma biblioteca de

componentes e assegurar que os desenvolvedores desoftwareutilizem essa biblioteca

pode ser dispendioso, uma vez que as técnicas atuais de classificação, catalogação e

recuperação de componentes desoftwareainda são imaturas.

• Encontrar e adaptar componentes reutilizáveis. Os componentes desoftwarede-

vem ser encontrados em uma biblioteca, compreendidos e, algumas vezes, adaptados,

a fim de trabalharem em um novo ambiente. Os engenheiros precisam ter uma razoá-

vel certeza de poder encontrar um componente em uma biblioteca, antes de incluírem

a rotina de busca de componentes como parte de seu processo normal de desenvolvi-

mento.

2.3 Teste de Componentes

A qualidade dos sistemas construídos a partir da composiçãode componentes assim

como o reuso efetivo destes estão intimamente relacionadosa sua confiabilidade. O teste

é, na prática, uma das técnicas mais empregadas para se avaliar a qualidade e a confiabili-

dade dos componentes.

2.3 Teste de Componentes 15

De acordo com Harrold[Har00], o teste de componentes pode ser visto sob duas perspec-

tivas: a perspectiva do fornecedor do componente e a perspectiva do cliente do componente.

O fornecedor vê o componente independentemente do contextoem que ele será utilizado e

deve portanto, testar todas as configurações do componente de uma forma livre de contexto.

Já para o cliente, o componente estará inserido no contexto de uma aplicação, interagindo

com outros componentes para compor uma funcionalidade maior. Dessa forma, é mais in-

teressante testar os aspectos do componente que são relevantes no contexto específico da

aplicação, assim como a correta interação entre os componentes que a compõem.

Na Figura 2.2, vemos como se dá o teste de um sistema baseado emcomponentes atra-

vés dessas perspectivas. O fornecedor dos componentes ‘A’,‘B’ e ‘C’ precisa assegurar a

sua funcionalidade como unidade funcional, verificando se omesmo se comporta adequa-

damente sob as características comuns ao domínio de aplicações para o qual foi projetado.

O cliente, que está usando os componentes para compor o sistema ‘ABC’, precisa averi-

guar o correto funcionamento dos componentes neste contexto específico, o que implica na

verificação das interações estabelecidas entre eles.

<< component >>

<< component >>

Sistema ABC << component >>

B

C

A

Figura 2.2: Teste de Componentes

O teste dos componentes sob duas perspectivas distintas é fundamental para que esta

atividade seja realizada com sucesso. Embora estas perspectivas tenham como objetivo a

descoberta de diferentes classes de defeitos, uma falta nãodescoberta na funcionalidade de

um componente durante o teste na perspectiva do fornecedor pode resultar em falhas na

integração deste com outros componentes dentro de uma aplicação, contribuindo para o fun-

cionamento incorreto dos demais componentes, assim como daaplicação como um todo. Por

outro lado, a(s) funcionalidade(s) obtida(s) a partir da integração de componentes correta-

2.4 O Método FCT 16

mente testados de forma individual pode(m) apresentar falhas decorrentes desta integração,

e que, portanto, só podem ser identificadas a partir do teste na perspectiva do cliente.

2.4 O Método FCT

Este trabalho é baseado no método FCT (Functional Component Testing), proposto por

Farias em[dF03]. Este método tem como objetivo principal possibilitar o teste funcional

das propriedades individuais de componentes desoftware, sob a perspectiva do fornecedor, a

partir de especificações UML e restrições OCL. Para tanto, o método fornece um conjunto de

diretrizes e técnicas que auxiliam a execução e o acompanhamento de cada uma das etapas do

processo de teste: planejamento, especificação, construção, execução e análise de resultados,

além de uma etapa extra que compreende o empacotamento dos artefatos de teste gerados

para o componente para possibilitar a distribuição destes efavorecer o desenvolvimento de

novos testes na perspectiva do cliente.

O método, cujas atividades estão integradas à metodologiaUML Components[CD01]

de desenvolvimento de sistemas baseados em componentes, faz uso dos seguintes artefatos

UML:

• Diagrama de Classe, onde são descritos, de forma estrutural, o modelo conceitual, as

classes do sistema assim como as associações entre elas;

• Restrições OCLque especificam os contratos (pré e pós-condições) das operações das

interfaces do componente;

• Diagramas de Casos de Usoque representam as funcionalidades do componente;

• Diagramas de Seqüênciaque especificam os cenários de uso das funcionalidades do

componente.

A seguir, vemos uma descrição sucinta das fases e atividadesque constituem o método

FCT.

2.4 O Método FCT 17

2.4.1 Planejamento

A etapa de planejamento de teste do método FCT é iniciada logoapós a análise de requi-

sitos do componente e consiste na tomada de decisões relativas à atividade de teste a partir

dos artefatos produzidos pela análise de requisitos e dos recursos disponíveis. Estas decisões

se concentram em que partes do componente serão testadas e noquanto cada parte deverá ser

testada. Como o teste exaustivo nem sempre é passível de ser realizado frente aos recursos

disponíveis, é necessário aplicar uma técnica sistemáticaque ajude a selecionar um subcon-

junto das funcionalidades. Para tanto, é proposta a utilização da técnica de análise de riscos

discutida em[MS01].

O objetivo principal da análise de risco é identificar o riscoque cada caso de uso oferece

à conclusão do projeto. A análise de risco envolve três tarefas: identificar os riscos relativos

a cada caso de uso, quantificar estes riscos e produzir uma lista dos casos de uso, ordenada

pelo grau de risco.

A quantificação dos riscos pode variar de um projeto para outro. Ela deve ter níveis

suficientes para separar os casos de uso em grupos de tamanho razoável. De uma maneira

geral, podem ser considerados 3 graus de risco: baixo, médioe alto. A idéia é que os

casos de uso que se encaixam no grau mais alto de risco recebamuma atenção especial e,

conseqüentemente, um maior número de casos de teste.

Ao final desta fase, o plano de teste do componente deverá estar concluído, indicando

quantos casos de teste deverão ser desenvolvidos para cada caso de uso.

2.4.2 Especificação dos Testes

A especificação dos testes é a fase do método FCT em que são gerados os modelos de

teste a partir dos quais são derivados os casos de teste, oráculos e dados de teste. Os modelos

de teste são gerados a partir da especificação do componente,que contém informações im-

portantes sobre as funcionalidades a serem fornecidas pelomesmo. Esta fase é composta por

três etapas:Seleção de Casos de Teste, Geração de OráculoseSeleção de Dados de Teste.

2.4 O Método FCT 18

Seleção de Casos de Teste

Nesta etapa são selecionados os casos de teste a ser desenvolvidos para o componente.

Como dito anteriormente, o teste exaustivo é quase sempre impossível de ser realizado frente

aos recursos disponíveis. Isto leva à necessidade de selecionar um subconjunto dos casos de

teste possíveis que irão ser efetivamente desenvolvidos e testados. A análise de riscos rea-

lizada durante o planejamento diz quantos cenários de cada funcionalidade do componente

serão testados, mas não indica quais são esses cenários. Para preceder esta seleção, é usada

uma combinação de duas técnicas de teste existentes, TOTEM (Testing Object-orienTed sys-

tEms with the unified Modeling language) [BL01] e Cleanroom[PTLP99].

A técnica TOTEM propõe que os diagramas de seqüência que descrevem os cenários de

uso de uma funcionalidade, sejam expressos como expressõesregulares, uma forma mais

compacta e analisável dos mesmos. O alfabeto dessas expressões são os métodos públicos

dos objetos presentes nos diagramas. As expressões são então constituídas de termos que

apresentam o formatoOperacaoClasse, denotando qual é a operação que está sendo executada

e a que classe esta operação pertence. O método FCT faz uso da técnica TOTEM para derivar

expressões regulares a partir de cada cenário de uso das funcionalidades do componente e,

a partir da disjunção das expressões dos cenários, obtém umaúnica expressão regular para

cada funcionalidade. Assim, a expressão regular para as funcionalidades segue o formato:

<Exp_Cenario_1> ∨ <Exp_Cenario_2> ∨ ... ∨ <Exp_Cenario_n>.

De posse das expressões regulares das funcionalidades do componente, é feita a apli-

cação da técnica Cleanroom. Esta técnica de teste propõe a construção de um modelo de

uso do sistema que represente todos os possíveis usos do mesmo e suas probabilidades de

ocorrência. Este modelo é expresso normalmente por meio de um grafo direcionado, onde

um conjunto de estados são conectados através de arcos de transição. Cada arco representa

um estímulo para o sistema, que o faz mudar de estado, e possuium valor de probabilidade

associado. Os casos de teste são gerados percorrendo-se o modelo, partindo-se do seu estado

inicial até o estado final e a seqüência de estímulos que leva osistema do seu estado inicial

ao estado final, através de um determinado caminho no modelo,é definida baseando-se nas

probabilidades das transições.

O método FCT determina que seja construído um modelo de uso para cada expressão

regular e, conseqüentemente, para cada funcionalidade do componente, de forma que cada

2.4 O Método FCT 19

estímulo seja correspondente a um termo e cada caminho corresponda exatamente a uma

cláusula da expressão regular da funcionalidade. As probabilidades do modelo de uso são

atribuídas a partir da análise de risco realizada na etapa deplanejamento. Na Figura 2.3,

é apresentado um exemplo de modelo de uso gerado para uma funcionalidade denominada

Fazer Reserva. Como pode ser visto, o modelo de uso possui cinco caminhos distintos, o

que indica que a funcionalidade em questão possui cinco cenários de uso.

2

0

1

3

(Gerenciador de

Hoteis.getHotel, 1)

(Gerenciador de

Hoteis.fazerReserva, 1)

4

5

6 7

8

9

(Hotel inexistente, 0.1)(Hotel.fazerReserva, 0.9)

(Hotel.getTipoQuarto, 1)

(Tipo quarto inexistente, 0.2)

(Hotel.isPeriodoReservaOk, 0.8)

(Quarto indisponivel, 0.5)

(Periodo Invalido, 0.1)

(Hotel.verificarDisponibilidade, 0.9)

(Hotel.gerarCodReserva, 0.5)

(Reserva realizada com sucesso, 1)

(Reserva.Reserva, 1)

Figura 2.3: Exemplo de Modelo de Uso de Método FCT

Uma vez construído o modelo de uso da funcionalidade, a seleção dos casos de teste

é feita, segundo as diretrizes da técnica Cleanroom, até o número de casos de teste a ser

desenvolvidos para a funcionalidade em questão, de acordo com as decisões tomadas na fase

de planejamento do método.

O processo de seleção do método FCT é bastante sistemático. No entanto, estudando

a técnica TOTEM, observamos que ela realiza apenas a geraçãode casos de teste e que o

próprio algoritmo apresentado pela técnica para a conversão dos diagramas de seqüência

em expressões regulares utiliza um grafo para tanto. Dessa forma, para efeito de seleção

de casos de teste, o uso de expressões regulares é absolutamente dispensável uma vez que

os modelos de uso da técnica Cleanroom, que são grafos, podemser gerados diretamente a

2.4 O Método FCT 20

partir dos diagramas de seqüência. Além disso, a aplicação da técnica Cleanroom apresenta

o problema da seleção de casos de teste repetidos, uma vez queos casos de teste selecionados

não são desconsiderados a cada nova seleção, o que pode resultar em um conjunto de casos

de teste ineficiente.

Geração de Oráculos

Para realizar a geração dos oráculos, o método FCT novamentefaz uso da técnica

TOTEM. A técnica propõe que seja construída uma tabela de decisão para cada expressão

regular que representa os cenários de uso selecionados nas funcionalidades do componente.

Esta tabela deve conter as condições de realização do uso e asações que serão tomadas pelo

componente diante da sua ocorrência. A principal fonte de informação para construção desta

tabela de decisão são as pré e pós-condições definidas para asoperações das classes que im-

plementam as funcionalidades das interfaces do componente. Para cada cláusula da expres-

são regular é necessário identificar suas condições de execução e expressá-las em OCL. O

próximo passo é definir, também em OCL, que mudanças de estadoocorrem no componente

com a execução do caso de uso. Além disso, deve-se identificartambém quais mensagens

são retornadas para o ator do caso de uso.

Na Tabela 2.1, é apresentado um exemplo de tabela de decisão gerada para uma funcio-

nalidade com cinco cenários de uso selecionados. Cada cenário na tabela representa uma

cláusula da expressão regular correspondente. Para cada umdesses cenários são associadas

as condições de execução (A-E), as mensagens retornadas para o usuário após sua execução

(I-V) e a indicação se a sua execução causa ou não uma mudança de estado no componente.

As informações coletadas durante esta etapa do processo servirão de base para a imple-

mentação dos oráculos e poderão auxiliar na seleção dos dados de teste.

Este processo, embora sistemático, é de difícil implementação, uma vez que o resultado

dos testes só pode ser determinado após uma consulta à tabelapara verificar se a execução

de um determinado cenário produziu a mensagem de saída e a mudança de estado esperadas,

o que compromete a automação da posterior atividade de construção dos casos de teste.

Além disso, na construção das tabelas de decisão são consideradas apenas as pré e pós-

condições OCL dos cenários. Contudo, tanto para as condições de execução dos cenários

quanto para a verificação de mudança de estado, as invariantes da classe que especifica a

2.4 O Método FCT 21

Condições Ações

Mensagem Mudança

de Estado

Cenários A B C D E I II III IV V

1 X X Não

2 X X Não

3 X X Não

4 X X Não

5 X X Sim

Tabela 2.1: Exemplo de Tabela de Decisão do Método FCT

funcionalidade também devem ser consideradas uma vez que estas são pré e pós-condições

implícitas[MS01].

Seleção de Dados de Teste

A seleção de dados de teste significativos, i.e. que sejam capazes de revelar comporta-

mentos anômalos em uma dada funcionalidade, é ainda um dos maiores problemas enfren-

tados pelos testadores desoftware. Além disso, esta é uma atividade bastante subjetiva e

poucas são as técnicas sistemáticas existentes. Em função disso, o método FCT se limita a

fornecer orientações sobre como selecionar os dados necessários para a execução dos casos

de teste.

Para realizar a seleção dos dados de teste, o método propõe a utilização da técnica de

particionamento por equivalência[Som03; Bei90]. Essa técnica parte do princípio que os

dados de entrada de um programa podem ser agrupados em classes que apresentam caracte-

rísticas comuns e que o programa se comporta da mesma forma para todos os membros de

uma mesma classe. Usando essa técnica, o trabalho de selecionar os dados de teste consiste

em identificar as partições e escolher dados particulares dentro de cada partição. A iden-

tificação das partições deve ser baseada na especificação e documentação dosoftware. No

caso do método FCT, são usadas as condições de execução definidas durante a geração dos

oráculos para identificar partições adequadas ao teste. A escolha dos dados tanto pode ser

feita de forma aleatória, como de forma mais direcionada, a fim de obter dados mais prová-

2.4 O Método FCT 22

veis de revelar erros. Na escolha direcionada consideramosos dados encontrados nos limites

da partição, por representarem normalmente valores atípicos, e dados considerados típicos,

encontrados no meio da partição.

Embora a seleção dos dados seja realizada através da técnicade particionamento por

equivalência, reconhecidamente uma das mais eficientes, a falta de um mecanismo sistemá-

tico para a identificação das partições a partir das condições de execução dos cenários torna

esta atividade bastante subjetiva uma vez que a escolha de umconjunto de dados eficiente é

intimamente dependente da experiência de quem está realizando a atividade e do seu conhe-

cimento sobre o domínio de aplicação do componente.

2.4.3 Construção, Execução e Análise de Resultados

Nesta etapa, as informações geradas até o momento são utilizadas para implementar os

casos de teste e oráculos. As tabelas de decisão, que foram construídas durante a geração dos

oráculos, são especialmente úteis. Para facilitar a construção das classes de teste, o método

FCT propõe o uso da ferramenta JUnit1 para implementar os casos de teste e oráculos. A

ferramenta é utilizada para executar os testes e analisar osresultados obtidos.

Cada funcionalidade dá origem a uma classe de teste que contém um método para testar

cada cenário definido na tabela de decisão da mesma. Assim, para cada cenário, são im-

plementadas, no método correspondente, as condições de execução definidas na tabela. Por

fim, a mensagem retornada e a ocorrência ou não de mudança de estado são verificadas, de

acordo com as definições da tabela, para indicarmos se o testeobteve sucesso ou não.

2.4.4 Empacotamento

O método FCT define, como última atividade, o empacotamento ea distribuição dos tes-

tes produzidos juntamente com o componente desenvolvido demaneira a contribuir para a

sua re-avaliação por parte do cliente, i.e. dentro de um contexto de uso e integração especí-

fico. Entretanto, o formato de empacotamento a ser utilizadoe a forma de disponibilização

deste pacote não são definidos pelo método.

1http://www.junit.org

2.5 Considerações Finais 23

2.5 Considerações Finais

Neste capítulo fornecemos a fundamentação teórica necessária para a compreensão do

trabalho aqui proposto. Foram apresentados os principais conceitos e termos relacionados a

teste desoftware, assim como as diferentes estratégias de teste empregadas para a detecção

de defeitos em sistemas desoftware. Com relação a componentes desoftware, foi apresen-

tada a definição e as características dos componentes abordados neste trabalho, as vantagens

e desvantagens do desenvolvimento baseado em componentes,além das duas perspectivas

sob as quais os componentes precisam ser testados. Por fim, apresentamos o método FCT,

no qual este trabalho se baseia. Foram descritas as suas fases e atividades assim como as téc-

nicas empregadas na sua definição. Também foi feita uma análise dos principais problemas

do método que dificultam a sua aplicação de maneira automática.

No próximo capítulo, é feita a apresentação do método AFCT, um método de teste auto-

mático que foi concebido a partir das idéias apresentadas nométodo FCT.

Capítulo 3

O Método AFCT

Embora concebido para potencializar a automação, o método FCT [dF03] ainda apre-

sentava muitas atividades subjetivas e/ou pouco sistemáticas que, apesar de não representar

nenhum problema para sua aplicação manual, consistia em um grande empecilho do ponto

de vista de automação. Em função disso, definimos o método de teste AFCT, uma nova pro-

posta fortemente baseada em[dF03], cujas atividades foram projetadas segundo algoritmos

e técnicas pré-definidas com o objetivo de possibilitar a automação a partir do desenvolvi-

mento de uma ferramenta de suporte. Neste capítulo será apresentado o método de teste

proposto abordando desde os artefatos da especificação dos componentes necessários a sua

aplicação, até a descrição de suas atividades e algoritmos.

3.1 Introdução

O método AFCT (Automatic Functional Component Testing) procede a verificação de

componentes de software na perspectiva do fornecedor, fazendo uso da especificação des-

tes componentes, sob a forma de artefatos UML e restrições OCL produzidos nas fases de

análise e projeto dos processos de desenvolvimento, para derivar casos de teste, oráculos e

dados de teste, e, a partir destas informações, proceder a geração e a execução de código

de teste assim como a interpretação dos resultados obtidos.O método determina ainda, que

os artefatos de teste gerados sejam disponibilizados juntamente com os componentes forne-

cidos para favorecer a atividade de teste na perspectiva do cliente, ou seja, no momento da

montagem da aplicação[Mac00].

24

3.1 Introdução 25

As atividades do método AFCT, como apresentado em[BAMF04b], estão agrupadas em

5 fases:

• Planejamento de Teste, onde são tomadas decisões relativas ao processo de teste;

• Especificação de Teste, onde são derivados os casos de teste, oráculos e dados que

constituem a especificação dos testes;

• Construção, que é responsável pela geração do código de teste;

• Empacotamento, onde o código de teste é compilado e empacotado para distribuição;

• Execução e Análise de Resultados, que realiza a execução e análise de resultados dos

códigos de teste gerados sobre as implementações dos componentes.

A fase de Planejamento de Teste do método AFCT é realizada de forma idêntica à apre-

sentada na definição do método FCT, no Capítulo 2, o que torna desnecessária a sua re-

apresentação. As atividades que constituem as demais fasessão apresentadas ao longo deste

capítulo.

O método AFCT foi concebido para ser utilizado paralelamente às metodologias de de-

senvolvimento de sistemas baseados em componentes (SBC), sem, no entanto, estar asso-

ciado a nenhuma em especial. Para prover esta independência, procuramos definir a sua

integração ao longo do processo de desenvolvimento, a partir de atividades genéricas nor-

malmente realizadas e de artefatos comumente desenvolvidos por estas metodologias. Dessa

forma, constituímos uma metodologia abstrata que descreveas atividades e a especificação

necessárias à aplicação do método de teste. Estas atividades e os artefatos que compõem a es-

pecificação dos componentes são apresentados na Figura 3.1 que descreve como é realizada

a integração das atividades do método de teste ao longo do processo de desenvolvimento de

SBC.

Inicialmente, para cada componente a ser utilizado na aplicação, é feita a análise dos

requisitos. A partir desta análise, são produzidos oDiagrama de Funcionalidades, um dia-

grama de casos de uso que especifica as funcionalidades dos componentes e os relaciona-

mentos entre elas, e oModelo Conceitual, um diagrama de classe onde são representados os

conceitos envolvidos no domínio de problema para o qual o componente está sendo desen-

volvido. De posse destes artefatos, a atividade de planejamento do método de teste já pode

3.1 Introdução 26

Planejamento de Teste

Especificação deTeste e Construção

Empacotamento, Execuçãoe Análise de Resultados

Montagem

Diagrama de Funcionalidades

Requisitos

Modelo Conceitual

Implementação

Materialização

Modelo de Informação

Diagramas de Cenário de Uso

Contratos

Modelagem

Figura 3.1: Integração entre Metodologias de Desenvolvimento de SBC e o Método de Teste

ser realizada. Após o levantamento dos requisitos do componente, é feita a sua modelagem,

que engloba as fases de análise e projeto do processo de desenvolvimento. Nesta etapa são

produzidos oModelo de Informação, um diagrama de classe que especifica o conjunto de

elementos (classes e associações) que precisam ser explicitados para possibilitar a utilização

do componente, osDiagramas de Cenário de Uso, diagramas de seqüência que especificam

os cenários de uso das funcionalidades do componente, e osContratos, restrições OCL que

descrevem os contratos de uso (pré e pós-condições) das operações da(s) interface(s), assim

como das invariantes de classe do componente. Estes artefatos são utilizados para a realiza-

ção das fases de especificação de teste e construção do métodoAFCT. Uma vez concluída a

modelagem do componente, é realizada a sua materialização,que pode se dar tanto a partir

da implementação de um componente novo como através da aquisição de um componente

previamente desenvolvido que esteja de acordo com a modelagem realizada. De posse da

implementação do componente, o código de teste gerado pela fase de construção do método

é compilado, e são realizadas as fases de empacotamento e execução e análise de resultados,

concluindo assim o teste do componente na perspectiva do fornecedor. A última etapa do

processo de desenvolvimento é a montagem da aplicação a partir dos componentes desen-

volvidos e testados individualmente.

Conforme descrito, os artefatos necessários a aplicação dométodo AFCT, que são pro-

duzidos ao longo do processo de desenvolvimento, nada mais são do que adaptações de arte-

3.1 Introdução 27

fatos comumente desenvolvidos pelas metodologias de desenvolvimento baseadas na lingua-

gem UML. No entanto, para possibilitar a definição de um método passível de automação,

foi preciso estabelecer a forma como estes artefatos devem ser construídos, principalmente

no que diz respeito à associação entre eles, uma vez que, do ponto de vista de automação, é

impossível detectar a real associação entre os artefatos sem que essa informação seja expli-

citamente fornecida. Para tanto, a abordagem de especificação proposta, que assemelha-se a

apresentada por Larman[Lar98], consiste na construção dos artefatos da seguinte maneira:

• No diagrama de funcionalidades, cada funcionalidade da(s)interface(s) do com-

ponente deve ser especificada como um caso de uso que terá comoator, o sis-

tema/componente que faz uso da funcionalidade correspondente.

• No modelo de informação do componente, construído a partir do modelo conceitual,

deverão ser especificadas todas as informações necessáriasà utilização do componente

além das restrições OCL correspondentes às invariantes de classe.

• Para explicitar a relação existente com os casos de uso (funcionalidades) que descre-

vem, os diagramas de cenário de uso deverão ser nomeados de acordo com o formato:

<nome_do_caso_de_uso>‘->’<nome_do_cenario> .

• Nestes diagramas de seqüência, o primeiro objeto deverá seruma instância do sis-

tema/componente que é ator do caso de uso correspondente a funcionalidade, e o se-

gundo uma instância da classe do componente que fornece a funcionalidade. Além

disso, todos os objetos devem ter o tipo definido e presente nomodelo de informação

do componente.

• As mensagens dos diagramas de seqüência devem ser numeradasde acordo com sua

ordem de execução e, ou corresponder diretamente à chamada da operação no objeto

receptor ou especificar o retorno de uma chamada. A última mensagem do diagrama

deverá ser de retorno e corresponderá a mensagem de saída do cenário. Nos cenários

que terminem com exceção, esta deve ser especificada na mensagem de saída. Para os

demais cenários, a mensagem de saída deverá ser vazia ou conter aString ‘return’

seguida do valor de retorno da funcionalidade (caso haja um).

3.2 Especificação de Teste 28

• As restrições OCL que descrevem os contratos de uso deverão ser inseridas, sob a

forma de comentários, nos diagramas de cenário de uso, especificando suas condições

de execução e as mudanças de estado esperadas. Embora esta não seja a maneira mais

comum para a especificação de restrições OCL, é a única forma de associá-las aos

cenários de uso e, ao mesmo tempo, possibilitar que as especificações sejam cons-

truídas em ferramentas de modelagem UML simples que disponham do mecanismo

de notas de comentário. Estas restrições, respectivamente, pré e pós-condições, terão

como contexto a operação correspondente à funcionalidade em questão na interface

do componente. Para diferenciar de comentários comuns, cada um desses comentá-

rios deverá ser iniciado com aString ‘ <<constraint >>’ seguido da restrição OCL

(‘context...’ ).

Nas seções seguintes, são descritas as atividades que constituem cada uma das fases do

método AFCT assim como os algoritmos e técnicas utilizados em suas definições.

3.2 Especificação de Teste

A fase de especificação de testes do método AFCT consiste na derivação de casos de

teste, oráculos e dados de teste a partir da especificação do componente. É nesta fase que

reside a maior contribuição do método AFCT para a automação do processo de teste. Cada

uma das suas etapas foram sistematicamente definidas e algoritmos foram propostos para

possibilitar a sua automação. Nas subseções seguintes, vemos a definição das atividades

realizadas nesta fase assim como dos algoritmos e técnicas propostos.

3.2.1 Seleção dos Casos de Teste

Ao estudarmos a atividade de seleção de casos de teste do método FCT e, conseqüente-

mente, as técnicas TOTEM[BL01], responsável pela geração dos casos de teste, e Clean-

room[PTLP99], que realiza a seleção propriamente dita, observamos que haviam dois pontos

falhos na aplicação conjunta das técnicas. A técnica TOTEM define um procedimento para

a conversão dos diagramas de seqüência em expressões regulares que são utilizadas pelo

método para a construção dos modelos de uso (grafos) necessários à aplicação da técnica

3.2 Especificação de Teste 29

Cleanroom. No entanto, segundo o algoritmo de conversão apresentado na técnica TOTEM,

os diagramas são convertidos em grafos a partir dos quais sãogeradas as expressões regula-

res, o que demonstra a possibilidade de conversão dos diagramas diretamente em modelos

de uso. Além disso, a técnica TOTEM só realiza esta conversãoporque os casos de teste

precisam ser representados sob a forma de expressões regulares para serem utilizados pelas

demais atividades da técnica. Em função disso, procuramos simplificar este processo criando

um algoritmo para a conversão direta dos diagramas de seqüência para o modelo de uso. Este

algoritmo é apresentado na Figura 3.2.

UseModel createUseModel(Collection scenarioDiagrams){

UseModel um new UseModel();Collection stimuli;SequenceDiagram d;for each d in scenarioDiagrams{

stimuli sortStimuliByName(d.stimuli);

int numSt stimuli.size;for each s in stimuli{

if (numSt > 1){um.addAction(toUseModelAction(s));

}else{

um.addFinalAction(toUseModelAction(s));}

numSt numSt-1;}

}return um;

}

¬

¬

¬

¬

String toUseModelAction(Stimulus st){

Object sender st.sender;

}

¬

¬

¬

¬

¬

Object receiver st.receiver;

Action action st.dispatchAction;String actionSeparator;if (action is ReturnAction){

actionSeparator '|';}else{

actionSeparator '.';}return sender.instanceClassifier.name + '->' +

receiver.instanceClassifier.name +actionSeparator + action;

Figura 3.2: Algoritmo para a Criação de Modelos de Uso

A funçãocreateUseModelrecebe como parâmetro uma coleção de diagramas de seqüên-

cia, que especificam os cenários de uso da funcionalidade correspondente, e produz como

saída o modelo de uso da funcionalidade. Cada diagrama de seqüência possui, além do

nome, uma coleção de objetos, do tipoObject, e uma coleção de mensagens, do tipoStimu-

lus. Os objetos possuem um nome e um classificador (instanceClassifier), que determina o

tipo do objeto no modelo UML. Cada mensagem, por sua vez, é composta por um objeto

transmissor (sender), um objeto receptor (receiver) e uma ação (do tipoAction). O modelo

de uso (UseModel) nada mais é do que um grafo dirigido com dois vértices pré-definidos, o

inicial e o final, no qual cada vértice possui uma coleção de arcos de saída e cada arco de

saída possui uma ação, uma probabilidade de execução, que reflete as decisões tomadas na

fase de planejamento de testes, e uma referência para o vértice destino.

3.2 Especificação de Teste 30

O algoritmo consiste em, para cada mensagem de cada diagramade seqüência, inserir a

ação correspondente no modelo de uso. Essas ações vão sendo inseridas a partir do vértice

inicial e de tal forma que, para cada ação diferente, é criadoum novo vértice no modelo de

uso, que receberá a ação inserida e a partir do qual serão inseridas as novas ações até que a

ação correspondente a última mensagem de cada diagrama de seqüência seja inserida, tendo

como destino o vértice final. Para tanto, é usada a função auxiliar toUseModelActionque é

responsável por converter as mensagens dos diagramas de seqüência para os formatos que

definimos para a ação do modelo de uso:

<tipo_do_sender>‘->’<tipo_do_receiver>‘.’<acao_da_m ensagem_de_chamada>

<tipo_do_sender>‘->’<tipo_do_receiver>‘|’<acao_da_m ensagem_de_retorno>

Com o algoritmo, definimos um procedimento automático e maissimplificado para a

construção dos modelos de uso. Na Figura 3.3, vemos um exemplo de modelo de uso cons-

truído para uma funcionalidade denominadaFazer Reservaa partir da aplicação do algo-

ritmo. Nele podemos destacar a utilização de um formato bem definido para todas as ações

e a introdução de informações extras, a exemplo do tipo do objetosender, que não estavam

disponíveis a partir das expressões regulares produzidas pela técnica TOTEM.

03

7

8

1

9

6

2

4

5(Hotel−>Reserva.Reserva(), 1)

(SistemaReservas−>GerenciadorHoteis.fazerReserva(), 1)(GerenciadorHoteis−>GerenciadorHoteis.getHotel(), 1)

(GerenciadorHoteis−>SistemaReservas|Hotel inexistente, 0.1)

(Hotel−>SistemaReservas|Periodo invalido, 0.1)

(Hotel−>SistemaReservas|Quarto indisponivel, 0.5)

(Hotel−>Hotel.verificarDisponibilidade(), 0.9)

(Hotel−>Hotel.gerarCodReserva(), 0.5)

(Hotel−>SistemaReservas|Tipo de quarto inexistente, 0.2)

(GerenciadorHoteis−>Hotel.fazerReserva(), 0.9)

(Hotel−>Hotel.getTipoQuarto(), 1)

(Hotel−>Hotel.isPeriodoReservaOk(), 0.8)

(Reserva−>SistemaReservas|Reserva realizada com sucesso, 1)

Figura 3.3: Formato de Modelo de Uso do Método AFCT

3.2 Especificação de Teste 31

Observando a mesma figura, vemos que, tanto o conjunto de diagramas de seqüência

quando o modelo de uso, apresentam a mesma expressividade, ou seja, é possível obter os

diagramas de seqüência a partir do modelo de uso correspondente. Isto pode ser comprovado

com o algoritmo apresentado na Figura 3.4.

Collection toScenarioDiagrams(UseModel um){

Collection scenarios new Collection();

}

¬

¬

¬

¬

¬

Collection paths um.paths;Collection actions;SequenceDiagram diagram;Stimulus st;Path p;for each p in paths{

diagram new SequenceDiagram();

actions p.actions;for each a in actions{

st toStimulus(a);diagram.addObject(st.sender);diagram.addObject(st.receiver);diagram.addStimulus(st);

}scenarios.add(diagram);

}return scenarios;

Figura 3.4: Algoritmo para a Criação de Diagramas de Seqüência a partir de Modelos de

Uso

Neste algoritmo, o modelo de uso, recebido como parâmetro, dá origem a uma coleção de

diagramas de seqüência representando os cenários de uso da funcionalidade correspondente.

O algoritmo consiste na criação de um diagrama de seqüência para cada caminho (path) do

vértice inicial ao vértice final do modelo de uso. Para tanto,é feito uso do algoritmo auxiliar

toStimulus(), que não foi exibido porque apresenta a funcionalidade inversa dotoUseModel-

Action(), ou seja, recebe umString no formato da ação do modelo de uso e produz como

resultado o objetoStimuluscorrespondente.

O outro problema detectado ao analisarmos a atividade de seleção de casos de teste do

método FCT está relacionado ao mecanismo de seleção da técnica Cleanroom. Como os

caminhos selecionados no modelo de uso não são de alguma forma diferenciados dos cami-

nhos ainda não selecionados, o processo de seleção pode resultar em um conjunto de casos

de teste com repetições e, portanto, ineficiente. Dessa forma, desenvolvemos um algoritmo

de seleção baseado nas diretrizes da técnica Cleanroom, i.e. os caminhos são selecionados

3.2 Especificação de Teste 32

a partir da aplicação de uma função de distribuição de probabilidade ao caminhar no mo-

delo de uso do vértice inicial ao vértice final, mas com o diferencial de ignorar caminhos já

selecionados. Esse algoritmo é apresentado na Figura 3.5.

Collection selectPaths(UseModel um, Integer n){

}

selectPath(Path p, Node startNode){

}

Collection result new Collection();Path path;Integer i;for each i in 1..n{

path new Path();selectPath(path, um.firstNode);result.add(path);

}return result;

Collection links selectableLinks(startNode.links);if (not links.size = 0){

Real[links.size] probs;Link l;

Integer i 0;for each l in links{

probs[i] l.probability;

i i + 1;}

Integer position selectPosition(probs);

Link selectedLink links.get(position);P.addAction(selectedLink.label, probs[position]);

Node nodeTo selectedLink.nodeTo;selectPath(p, nodeTo);if (selectableLinks(nodeTo.links).size = 0){

l.visited <- true;}

}

¬

¬

¬

¬

¬

¬

¬

¬

¬

Collection selectableLinks(Collection links){

}

Integer selectPosition(Real[] probs){

}

fixProbs(Real[] probs){

}

Collection result new Collection();for each l in links{

if (not l.visited){result.add(l);

}}return result;

fixProbs(probs);Real[probs.length] storedProbs;

storedProbs[0] probs[0];Integer i;for each i in 1..probs.length{

storedProbs[i] storedProbs[i-1] + probs[i];}

Real randomNumber random();

Integer j 0;while (randomNumber > storedProbs[j]){

j j + 1;}return j;

Real total 0.0;Integer i;for each i in 0..probs.length{

total total + probs[i];}if (total <> 1){

for each i in 0..probs.length{

probs[i] probs[i] + ( (probs[i] / total) *(1 - total) );

}}

¬

¬

¬

¬

¬

¬

¬

¬

¬

Figura 3.5: Algoritmo para a Seleção de Caminhos em Modelos de Uso

A seleção dos casos de teste começa com a execução do algoritmo selectPathsque recebe

como parâmetros o modelo de uso (um) e o númeron de caminhos a serem selecionados no

modelo de uso. Este algoritmo consiste em caminharn vezes no modelo de uso e, a partir do

vértice inicial, selecionar um caminho por vez, o que é feitopelo algoritmoselectPath().

Para entender o funcionamento do algoritmoselectPath(), precisamos, primeiramente,

3.2 Especificação de Teste 33

definir o conceito de arco visitado. Um arco é dito visitado quando todos os caminhos a

partir dele já foram selecionados. Com a introdução desta característica no modelo de uso,

passamos a ter o conhecimento dos caminhos já selecionados apartir de um determinado

vértice, evitando, assim, a seleção de caminhos repetidos.Essa verificação é feita pela função

selectableLinksque recebe como parâmetro a coleção de arcos que saem de um determinado

vértice e retorna a coleção dos que ainda não foram visitadose que, portanto, estão aptos a

ser selecionados.

A partir das probabilidades dos arcos não visitados do vértice corrente (startNode), caso

existam, é criado um array que é utilizado pela funçãoselectPosition(). Esta função faz a

escolha de uma posição no array e, conseqüentemente, do arcocorrespondente, baseada na

distribuição das probabilidades. Para proceder tal escolha é preciso, primeiramente analisar

se a soma das probabilidades é 100% e, caso não seja, redistribuir de forma proporcional a

probabilidade complementar entre as probabilidades do array. Esse procedimento, realizado

pela funçãofixProbs(), é necessário pois, como os arcos visitados são desconsiderados pelo

processo de seleção, a soma das probabilidades dos arcos concorrentes a ele é inferior a

100%, existindo assim a possibilidade de nenhum dos arcos concorrentes ser selecionado.

Voltando para a funçãoselectPosition(), uma vez que o array de probabilidades é com-

pleto, para proceder a escolha de um índice baseado na distribuição das probabilidades,

criamos um segundo array de mesmo número de elementos no qualcolocamos as probabili-

dades acumuladas do array de probabilidades (probs), de tal forma que o primeiro elemento

do novo array (storedProbs) será a primeira probabilidade do array de probabilidades ere-

presentará a faixa de probabilidades de 0% até esse valor, e oúltimo elemento será 1, repre-

sentando a faixa de probabilidades do valor do penúltimo elemento até 100%. Em seguida,

geramos um número real aleatório entre 0 e 1 e verificamos a qual faixa de probabilidades

do array esse número pertence. O índice correspondente a essa faixa de probabilidade será

então a posição selecionada.

Uma vez selecionada a posição do array de probabilidades e, conseqüentemente, o arco

correspondente, na funçãoselectPath(), o rótulo contendo a ação deste é inserido no caminho

parcial (p) e a seleção do caminho continua a partir do vértice destino do arco selecionado.

Ao final da função, quando um caminho completo do vértice atual ao final tiver sido selecio-

nado, se todos os arcos do vértice destino do arco selecionado já tiverem sido visitados, então

3.2 Especificação de Teste 34

o arco selecionado também será marcado como visitado, o que,como vimos, impossibilita a

sua seleção em novos caminhos.

Com esses algoritmos estabelecemos a automação da atividade de seleção de casos de

teste a partir das informações do modelo UML. Uma última ressalva deve ser feita com

relação às probabilidades das ações do modelo de uso. Como sabemos, essa informação é

obtida a partir das decisões tomadas na fase de planejamento, o que torna a atribuição destas

probabilidades uma atividade manual, uma vez que depende dainteração humana. Assim,

para possibilitar que a atividade de seleção seja executadade forma totalmente automática,

propomos atribuir, por padrão, probabilidades iguais a arcos concorrentes do modelo de uso.

Dessa forma, caso não haja uma interação para alterar as probabilidades, a seleção se dará

de forma aleatória, segundo uma distribuição uniforme.

3.2.2 Geração de Oráculos

Como vimos no Capítulo 2, a atividade de geração de oráculos do método FCT também

é baseada na técnica TOTEM. A técnica propõe a construção de uma tabela de decisão para

cada expressão regular obtida na geração dos casos de teste.A idéia consiste em associar,

para cada termo da expressão (cenário de uso), sua condição de realização e as ações que

serão tomadas pelo componente diante da sua ocorrência, quepor sua vez são compostas

pela mensagem de saída esperada e pela verificação da ocorrência de mudança de estado do

componente. Este processo, embora sistemático, é de difícil implementação, o que compro-

mete a automação não só desta atividade mas também da fase posterior de construção do

código de teste. Além disso, a técnica TOTEM não considera asinvariantes definidas para a

funcionalidade do componente como parte da especificação das condições de realização dos

usos e da ocorrência de mudança de estado do componente.

Em função disso, definimos que a atividade de geração de oráculos do método AFCT

seria realizada a partir de um mapeamento sistemático das restrições OCL, que expressam

as pré e pós-condições dos cenários de uso (incluindo as invariantes), diretamente para frag-

mentos de código fonte. As mensagens de saída, por sua vez, são mapeadas para exceções

esperadas, ou seja, que deverão ser lançadas durante a execução da funcionalidade em decor-

rência da realização do cenário de uso correspondente. Os fragmentos de código resultantes,

que deverão ser incorporados ao código de teste a ser construído, são, basicamente, méto-

3.2 Especificação de Teste 35

dos responsáveis por determinar, em tempo de execução, se a condição expressa na restrição

OCL é verdadeira considerando-se o estado atual do componente. Para tanto, estes métodos

devem receber, como parâmetros, uma instância da classe contexto da restrição, os parâme-

tros da operação, no caso de restrições de pré e pós-condições, além do resultado da operação

para restrições de pós-condição. Na Tabela 3.1, é apresentado o esquema geral de conversão

proposto1, enquanto que, na Figura 3.6, vemos um exemplo de conversão.

OCL context <context> [ :: <operation> ( [<op_params>] ) [:<result_type>]]

inv | pre | post<const-name> ...

Código Fonte Boolean check_<const-name>(<context> self [, <op_params>]

[, <result_type> result]) { ... }

Tabela 3.1: Esquema de Conversão de Restrições OCL para Código Fonte

context Empresa inv idadeEmpregados:

empregados forAll (p : Pessoa | p.idade >= 18)

Boolean check_idadeEmpregados(Empresa self){

Boolean result true;

Collection empregados self.empregados;Pessoa p;for each p in empregados{

if (not (p.idade >= 18)){

result false;break;

}}return result;

®

¬

¬

¬

Figura 3.6: Exemplo de Conversão de OCL para Pseudo-Código

Uma vez que OCL é uma linguagem de restrições que atua sobre asclasses e operações

de um modelo UML, para simplificar o mapeamento, a linguagem de programação a ser

utilizada na implementação destes métodos deverá ser, necessariamente, orientada a objetos.

Além disso, por motivos óbvios, deverá ser a mesma linguagemutilizada na implementação

dos casos de teste, realizada na fase de construção. Neste trabalho, realizamos o mapeamento

1Construções dentro de [ ] são opcionais.

3.2 Especificação de Teste 36

das restrições OCL para código fonte na linguagem Java, entretanto, uma vez satisfeita as

condições descritas acima, qualquer outra linguagem poderia ser usada. O mapeamento pro-

posto das construções mais usuais de OCL para um subconjuntobem definido da linguagem

Java é apresentado no Anexo A.

De posse dos métodos que constituem o oráculo de um determinado cenário, a avaliação

do resultado do teste, em tempo de execução, é feita da seguinte forma: Inicialmente é

chamado o método correspondente à pré-condição do cenário para avaliar se o teste está

apto a ser realizado. Caso esse método retornefalse, indicando falha na avaliação da pré-

condição, o teste não pode ser realizado pois o contrato do cenário de uso não está sendo

respeitado. Neste caso, não é possível informar que o teste falhou nem que passou e o

resultado é considerado indefinido. Caso, por outro lado, o método retornetrue, então o teste

será realizado e o resultado obtido vai ser utilizado na chamada do método correspondente

à pós-condição do cenário, cujo retorno, juntamente com a mensagem de saída do cenário,

caso exista, indicarão se o teste obteve sucesso ou falhou.

O uso de código fonte na representação dos oráculos gerados elimina a necessidade de um

processo de conversão para que os mesmos sejam acessados a partir do código de teste para

avaliar os resultados obtidos. Além disso, essa abordagem contribui para a flexibilização do

método no que diz respeito às linguagens de especificação de contratos e de implementação

do código de teste a serem utilizadas, desde que seja elaborado um mapeamento entre as

duas linguagens, o que, apesar de não ser um trabalho trivial, é bastante válido em função da

possibilidade de geração automática de teste proporcionada pelo método.

3.2.3 Seleção de Dados de Teste

Vimos, no Capítulo 2, que a seleção de dados de teste significativos para uma dada fun-

cionalidade constitui uma das atividades mais complexas e subjetivas de todo o processo

de teste, o que resulta na existência de poucas técnicas sistemáticas para tanto. Uma das

técnicas mais eficientes para a seleção de dados de teste é a técnica de particionamento por

equivalência[Bei90; Som03], que é utilizada pelo método FCT. No entanto, como o pro-

cesso de identificação das partições de dados, necessário à aplicação da técnica, é bastante

subjetivo e dependente de conhecimentos de domínio da aplicação, é preciso prover meios

de sistematizar esse processo para promover a automação da atividade de seleção como um

3.2 Especificação de Teste 37

todo.

Em função disso, na definição da atividade de seleção de dadosde teste do método AFCT,

propomos a utilização da técnica apresentada por McGregor em [MS01] para promover a

identificação das partições de dados, necessárias à aplicação da técnica de particionamento

por equivalência, a partir das condições de execução dos cenários de uso. Esta técnica visa

identificar as partições de equivalência a partir da decomposição das restrições OCL em

termos lógicos e, segundo o critério de cobertura MC/DC[CM94], avaliar a correta con-

tribuição de cada termo determinante para o resultado da expressão lógica como um todo.

Na Tabela 3.2, vemos como as restrições OCL podem ser decompostas segundo a técnica

de McGregor e as contribuições que devem ser analisadas em cada caso para se alcançar a

cobertura definida pelo critério MC/DC. Nessa tabela,1, 2 e 3 são termos de uma restrição

OCL que podem representar desde expressões relacionais, mais simples, até subcondições,

mais complexas. A idéia é derivar recursivamente essas condições de acordo com os padrões

lógicos da tabela até as estruturas mais simples e, para cadacontribuição destacada, que

corresponderá as partições de equivalência, identificar o domínio de dados que a satisfaz.

Padrão Expressão Lógica OCL Contribuição a ser Analisada

Valor 1 1

Valor Complementar not 1 not 1

E 1 and2 1 and2

Ou 1 or 2 1

2

1 and2

Ou Exclusivo 1 xor 2 1 and not2

not 1 and2

Implica 1 implies2 not 1

1 and2

Se-Então-Senão if 1 then2 1 and2

else3 endif not 1 and3

Tabela 3.2: Padrões Lógicos para Identificação de Partiçõesde Equivalência

3.2 Especificação de Teste 38

Uma vez identificada as partições de equivalência estabelecidas pela restrição OCL, a

seleção de dados consiste na escolha de dados limites e representativos dentro dos domínios

correspondentes a cada partição.

Para exemplificar a aplicação da atividade de seleção de dados do método AFCT, va-

mos considerar uma funcionalidadecontratar()em um componente hipotético para geren-

ciamento de uma empresa. A operação que implementa esta funcionalidade recebe, como

parâmetros, o objeto que representa a pessoa a ser contratada, o cargo a ser exercido e o nú-

mero de horas semanais. A empresa tem como política a contratação de estagiários apenas

em tempo parcial. A restrição OCL que especifica a condição deexecução para o cenário de

sucesso da funcionalidade poderia ser dada por:

context Empresa::contratar(p: Pessoa, cargo: String, hor as: Integer)

pre: if cargo=‘Estagiario’ then horas=20 else (horas >= 20 a nd horas <= 44)

endif

Aplicando-se a técnica de McGregor, observamos que a restrição se enquadra no pa-

drãoSe-Então-Senãoda Tabela 3.2. Assim1 representa o termocargo=‘Estagiario’ , 2

representa o termohoras=20 e 3 representa o termo(horas >= 20 and horas <= 44) .

Segundo a tabela, devem ser analisadas as contribuições dassubexpressões1 and2 e not 1

and3, que representam as duas maneiras possíveis de exercitar o cenário de uso em questão.

Neste momento, a técnica é aplicada de forma recursiva para cada contribuição. O primeiro

caso (1 and2) casa com o padrãoE, no qual a contribuição a ser analisada é a própria restri-

ção, o que indica que devem ser gerados dados que satisfaçam,ao mesmo tempo, os termos

1 e 2. Como ambos os termos são igualdades e envolvem variáveis diferentes (cargo e

horas ), os valores capazes de satisfazê-lo são dados pela própriaigualdade (‘Estagiario’

e20). No segundo caso (not1 and3), a expressão também pode ser vista segundo o padrãoE

(1’ and2’) em que1’ representa o termonot 1 (not cargo=‘Estagiario’ ) e2’ representa o

termo3 ((horas >= 20 and horas <= 44) ). Como vimos, o padrãoE implica na seleção

de dados que satisfaçam ambos os termos. O termo1’ (not cargo=‘Estagiario’ ) é ava-

liado segundo o padrãoValor Complementar, o que estabelece uma partição de equivalência

composta por todos os valores do tipo da variávelcargo à exceção do utilizado na expressão.

Como o tipo da variável é String, valores interessantes a serselecionados são a cadeia vazia

(‘’), que corresponde a um valor limite da partição, e um valor qualquer que seja diferente

3.3 Construção e Empacotamento dos Artefatos de Teste 39

do utilizado na expressão, por exemplo‘Cargo X’ , que representa um valor intermediário

na partição. Na avaliação do termo2’ (horas >= 20 and horas <= 44 ) mais uma vez é

aplicado o padrãoE (1” and2” ), sendo1” a subexpressãohoras >= 20 e2” a subexpressão

horas <= 44 . Neste caso, porém, como ambos os termos são referentes a umamesma va-

riável (horas ), estabelece-se uma única partição de equivalência composta pelos dados que

satisfazem, ao mesmo tempo, os dois termos. A partição estabelecida compreende o inter-

valo de valores inteiros entre20 e 44. Além desses valores, considerados críticos, também

devem ser selecionados valores intermediários, a exemplo de 30 e 40.

Dessa forma, concluímos a atividade selecionando, para a primeira forma de execução do

cenário de sucesso, o valor‘Estagiario’ para a variávelcargo e o valor20 para a variável

horas . Para a segunda forma de execução, selecionamos os valores‘’ (cadeia vazia) e

‘Cargo X’ para a variávelcargo e os valores20, 30, 40 e 44 para a variávelhoras . Esses

valores devem então ser combinados, dentro de cada forma de execução do cenário, para se

alcançar a cobertura de teste promovida pelo método.

Embora o processo de seleção de dados tenha sido demonstradoapenas para variáveis

de tipos simples (primitivos), nada impede a sua utilizaçãopara a construção de valores

complexos (objetos) desde que a estrutura destes seja conhecida e que sejam compostos por

tipos primitivos. Neste caso, são selecionados valores para os tipos primitivos que compõe o

tipo complexo.

É importante salientar que a atividade de seleção de dados deteste do método AFCT só

realiza a geração de valores significativos para as variáveis envolvidas nas restrições OCL.

Por este motivo não foram gerados dados para instanciação davariávelp (tipo Pessoa) da

restrição. Assim, todas as variáveis para as quais o processo de seleção não é realizado,

deverão ter valores fornecidos no momento da execução do teste. Por desconhecermos o

impacto que o valor dessas variáveis acarreta na execução das funcionalidades, aconselhamos

que estes sejam definidos de forma aleatória.

3.3 Construção e Empacotamento dos Artefatos de Teste

Uma vez terminada a fase de especificação de teste, os casos deteste, oráculos e dados

são utilizadas para a construção e o empacotamento dos artefatos de teste. Os artefatos de

3.3 Construção e Empacotamento dos Artefatos de Teste 40

teste a ser gerados compreendem as classes de teste e os arquivos de dados, que são criados

para permitir a modificação dos dados de teste sem a necessidade de alteração do código

fonte dos testes, o que é necessário para possibilitar o teste do componente, por parte do

cliente, com dados específicos ao contexto no qual será usado.

O método AFCT propõe que as classes de teste sejam implementadas a partir de API’s

(Application Programming Interfaces) de frameworksde execução de teste da família xUnit

e de acordo com a linguagem de programação usada na implementação sob teste. A idéia é

aproveitar toda a infra-estrutura provida por estesframeworkspara concentrar o trabalho no

que deve ser testado e não em como esses testes serão executados. Na Tabela 3.3, podemos

ver os principaisframeworksde teste existentes para algumas linguagens de programação

utilizadas no desenvolvimento de componentes.

Linguagem de Programação Framework de Teste

Java JUnit

C++ CppUnit

Visual Basic VBUnit

Object Pascal (Delphi) DUnit

Linguagens do Framework .NET NUnit

Tabela 3.3: Frameworks de TestexUnit

O código de teste é construído da seguinte forma: É criada umaclasse de teste para

cada funcionalidade do componente a ser testada e, nestas classes, são criados métodos de

teste, um para cada cenário de uso selecionado na funcionalidade. Estes métodos fazem uso

dos dados selecionados para executar a funcionalidade nos cenários de uso correspondentes

e, a partir dos oráculos de teste, determinar a correção da funcionalidade naquele cenário

de uso. Uma vez que, na fase anterior, os oráculos de teste foram gerados sob a forma de

fragmentos de código, estes devem ser inseridos nas classesde teste correspondentes para

poderem ser acessados pelos métodos de teste. Além disso, caso o cenário de uso apresente

uma mensagem de retorno, esta deverá ser tratada como uma exceção esperada e o método

de teste deverá assegurar que esta exceção é lançada durantea execução da funcionalidade

no cenário de uso correspondente.

3.4 Execução dos Testes e Análise de Resultados 41

Os arquivos de dados, por sua vez, são gerados a partir dos valores selecionados durante

a fase de especificação de teste. É criado um arquivo para cadaclasse de teste, i.e. para cada

funcionalidade a ser testada. Nestes arquivos ficam armazenados os dados selecionados para

as variáveis necessárias à execução dos testes dos cenáriosde uso da funcionalidade. Embora

não seja definido o formato exato destes arquivos, os mesmos deverão ser construídos de

forma a facilitar o acesso aos dados, em tempo de execução, a partir das classes de teste.

Uma vez construídos, os artefatos de teste devem ser empacotados e disponibilizados

junto com o componente desenvolvido para o cliente. Propomos que estes artefatos sejam

empacotados em algum formato que possibilite a execução e análise de resultados dos teste

nele contidos, seja de forma auto-suficiente, seja através de um aplicativo a ser fornecido

para esta função. Este pacote será então distribuído juntamente com o componente de forma

a possibilitar aos clientes o teste do mesmo sob contextos deutilização específicos.

3.4 Execução dos Testes e Análise de Resultados

A última fase do método é a execução dos testes e análise de resultados. Nesta fase, o

pacote contendo os artefatos de teste produzidos é utilizado para testar uma implementação

da especificação do componente. Uma vez que é disponibilizado juntamente com o compo-

nente desenvolvido, o pacote de artefatos permite que o cliente possa re-executar os testes

gerados pelo fornecedor, optando, a cada execução, por:

1. Avaliar a conformidade de um determinado cenário de uma funcionalidade;

2. Avaliar a conformidade de uma funcionalidade a partir do teste de todos os seus cená-

rios;

3. Avaliar a conformidade de todas as funcionalidades do componente.

Além disso, uma vez que o processo utilizado para a seleção dos dados visa a obtenção

de dados genéricos e independentes do contexto em que o componente venha a ser utili-

zado, é necessário prover ao cliente uma forma de execução dos casos de teste com dados

específicos da aplicação na qual o componente será usado, ou seja, dados que comprovem

a conformidade das funcionalidades do componente quando integrado a outros componen-

tes para compor uma aplicação. Em função disso, a mesma infra-estrutura que fornece os

3.5 Considerações Finais 42

meios para execução e avaliação de resultados dos testes contidos no pacote de artefatos do

componente deve possibilitar o acesso dos clientes aos dados contidos nos arquivos de dados

para permitir, além da utilização dos dados selecionados pelo método, a informação de novos

dados de teste em tempo de execução.

3.5 Considerações Finais

Apresentamos, neste capítulo, o método de teste automáticoAFCT que foi proposto neste

trabalho com o objetivo de suprir as deficiências do método FCT [dF03] no que diz respeito

à automação das atividade inerentes ao processo de teste. Asfases e atividades do método

foram definidas a partir de algoritmos e técnicas bem definidas e exemplos de sua aplicação

foram demonstrados. As definições aqui apresentadas, serviram de base para o projeto e

desenvolvimento da ferramenta de suporte SPACES, que é apresentada no Capítulo 4.

Capítulo 4

A Ferramenta SPACES

Neste capítulo, apresentaremos a ferramenta SPACES que foidesenvolvida para dar su-

porte à execução automática das atividades do método AFCT apresentado no Capítulo 3.

Inicialmente serão destacadas as características gerais para as quais o projeto da ferramenta

foi dirigido. A seguir, será apresentada uma visão geral da ferramenta a partir de sua ar-

quitetura e serão apresentados os detalhes de projeto e implementação de cada módulo da

ferramenta. Por fim, será apresentada a aplicação Testador de Componentes, responsável

pela execução dos testes gerados pela ferramenta.

4.1 Características da Ferramenta

A ferramenta SPACES, acrônimo deSPecification bAsed Component tESter, foi desen-

volvida para automatizar as atividades do método AFCT e, assim sendo, tem como objetivo

a geração de artefatos de teste para componentes de softwarea partir do conjunto de artefa-

tos UML e restrições OCL que constituem a especificação destes componentes, auxiliando o

fornecedor a maximizar a qualidade dos componentes disponibilizados. Entre suas caracte-

rísticas gerais destacam-se:

• Integração com ferramentas de modelagem UML que exportem para o formato XMI

(XML Metadata Interchange), padrão da OMG1 para troca de informações entre ferra-

mentas;1Object Management Group. http://www.omg.org

43

4.1 Características da Ferramenta 44

• Arquitetura adaptável para utilização de diferentes linguagens de especificação de res-

trições (OCL, Object Z, etc.) assim como a geração de código de teste para diferentes

linguagens de programação e/ou plataformas;

• Persistência de sessões de trabalho, permitindo que os usuários possam interromper

e posteriormente dá continuidade ao trabalho em qualquer etapa do processamento,

e minimizando, dessa forma, o impacto causado pela alteração da especificação do

componente durante o uso da ferramenta;

• Disponibilização dos testes gerados juntamente com o componente de forma a pos-

sibilitar a reexecução destes a partir de novos dados fornecidos dinamicamente tanto

pelos fornecedores quanto pelos clientes.

Além disso, para maximizar a usabilidade da ferramenta SPACES, alguns requisitos não-

funcionais também foram considerados:

Portabilidade Uma vez que ferramentas de modelagem UML estão disponíveis para di-

ferentes plataformas operacionais, a exemplo deWindows, Linuxe Mac, procuramos desen-

volver uma ferramenta de teste que fosse independente de plataforma. Em função disso,

resolvemos implementar a ferramenta com a linguagem de programação Java.

Autonomia Em uma ferramenta de automação, o número de interações humanas neces-

sárias deve ser minimizado. SPACES foi desenvolvida de forma a permitir as interações

indispensáveis à realização das atividades de teste, ao mesmo tempo em que possibilita um

comportamento padrão para cada uma dessas interações com o objetivo de melhor automa-

tizar o processo.

Confiabilidade Por se tratar de um software para a verificação e determinaçãoda confor-

midade de outros, a confiabilidade da ferramenta era uma característica imprescindível para

avaliar quais funcionalidades dos componentes estavam ou não corretamente implementadas.

Durante o projeto da ferramenta SPACES, procuramos assegurar a sua correta implementa-

ção a partir da realização de testes de unidade e de testes de aceitação. Além disso, como

pode ser visto no Capítulo 5, procedemos a aplicação da ferramenta em um estudo de caso

previamente escolhido.

4.2 Visão Geral 45

Rapidez e Escalabilidade Para alcançar uma maior escalabilidade no que se refere a

complexidade – medida em número de funcionalidades e cenários – dos componentes a

serem verificados, procuramos desenvolver a ferramenta de forma a minimizar a utilização

de recursos de sistema (i.e memória, processador, etc.), sem, no entanto, comprometer a

velocidade de execução do processo como um todo.

4.2 Visão Geral

Para alcançar as características desejadas, procuramos desenvolver a ferramenta SPACES

de forma modularizada. O projeto arquitetural foi elaborado levando-se em consideração as

funcionalidades que a mesma precisava prover para automatizar ao máximo as atividades do

método AFCT. As funcionalidades foram agrupadas em móduloscorrespondentes às fases

do processo de teste e a arquitetura inicial da ferramenta foi definida. Esta arquitetura foi

então aperfeiçoada dando origem a versão final, apresentadana Figura 4.1.

Testador deComponentes

Pacotede Teste

Ferramenta deModelagem

Diagrama deSequência

OCL

Diagrama deCasos de Uso

Diagramade Classe

XMI

API Java(Modelo UML)

ParserXMI

Gerador deOráculo

Gerador deArtefatos de Teste

Seletor de Casosde Teste

Seletor de Dadosde Teste Empacotador

SPACES

Casos de TestexUnit

Gerenciador dePersistência

Figura 4.1: Arquitetura da Ferramenta SPACES

Como pode ser visto na arquitetura, a ferramenta recebe comoentrada a especificação do

componente no formato XMI. Esse formato foi adotado para permitir a utilização de especi-

ficações construídas nas principais ferramentas CASE. Trata-se de um padrão desenvolvido

pela OMG para possibilitar a representação textual baseadaem XML (Extended Markup

Language) de instâncias do MOF (Meta-Object Facility), um metamodelo para metamodelos

orientados a objetos, a exemplo do metamodelo UML. Apesar dapadronização da OMG, os

arquivos exportados neste formato costumam apresentar pequenas diferenças e/ou elementos

4.3 Projeto 46

proprietários, ou seja, dependentes da ferramenta de modelagem. No entanto, em que pese o

fato de, tanto nos testes realizados durante o desenvolvimento da ferramenta SPACES quanto

na aplicação do estudo de caso apresentado no Capítulo 5, termos feito uso da ferramenta

Gentleware Poseidon for UML2 para produzir as especificações UML e exportá-las sob o

formato XMI, já existem ferramentas no mercado capazes de “traduzir” os dialetos XMI ex-

portados pelas principais ferramentas de modelagem UML, incluindo aIBM Rational Rose3

além da própriaPoseidon for UML.

De acordo com a arquitetura, podemos identificar claramenteo fluxo de execução da

ferramenta. Inicialmente, a especificação no formato XMI é processada pelo móduloParser

XMI com o objetivo de disponibilizar, a partir da instanciação das classes daAPI UML, as

informações do modelo UML para os demais módulos da ferramenta. No móduloSeletor

de Casos de Testesão identificadas as funcionalidades do componente e selecionados os

cenários a ser testados. A especificação de cada um desses cenários constitui a entrada

para os módulosGerador de Oráculose Seletor de Dados de Testeresponsáveis por gerar,

respectivamente, os procedimentos responsáveis pela avaliação dos resultados dos testes e

os dados necessários à sua execução. O móduloGerador de Artefatos de Teste, por sua vez,

recebe como entrada as informações processadas pelos módulosSeletor de Casos de Teste,

Gerador de Oráculose Seletor de Dados de Testepara promover a construção do código de

teste. Esse código de teste é empacotado pelo móduloEmpacotadordando origem aoPacote

de Testedo componente, que constitui o artefato de saída da ferramenta. Por fim, uma vez

gerado oPacote de Teste, fazemos uso da aplicaçãoTestador de Componentespara executar

e analisar os resultados dos testes de uma implementação da especificação do componente.

A seguir vemos os detalhes do projeto da ferramenta SPACES, cuja versão preliminar foi

apresentada em[BAMF04a].

4.3 Projeto

Desde o princípio, o foco do projeto da ferramenta SPACES foia funcionalidade. Sendo

assim, não foi dada muita importância ao desenvolvimento dausabilidade da interface grá-

2http://www.gentleware.com3http://www.rational.com

4.3 Projeto 47

fica, embora reconheçamos que esta é uma característica determinante para o uso efetivo da

ferramenta e que, portanto, merecerá uma atenção especial em trabalhos futuros. A inter-

face gráfica desenvolvida buscou centralizar o acesso às diversas funcionalidades fornecidas

pelos módulos da ferramenta na tela principal apresentada na Figura 4.2. Nesta tela, que se-

gue o padrão MDI (Multiple Document Interface), ou seja, que funciona como umcontainer

para as demais telas da ferramenta, as funcionalidades implementadas pelos módulos estão

agrupadas em dois menus: o menuArquivoe o menuProjeto, cujos itens são apresentados

na Figura 4.3.

Figura 4.2: SPACES: Tela Principal

Figura 4.3: SPACES: Menus Arquivo e Projeto

O menu Arquivo reúne as funcionalidades gerais da ferramenta acessadas a partir dos

submenusAbrir e Salvar Projetoe Ler Arquivo XMI. Na Figura 4.4, vemos a tela para

abertura de arquivos XMI acionada pela funcionalidadeLer Arquivo XMI.

4.3 Projeto 48

Figura 4.4: SPACES: Abrindo Arquivos XMI

A partir do menu Projeto são acessadas as funcionalidades correspondentes às etapas do

método AFCT. Essas funcionalidades foram implementadas emmódulos distintos da ferra-

menta e serão apresentadas durante a descrição destes.

A ferramenta foi projetada para possibilitar a sua expansão, o que pode ser visto, mais

claramente, no diagrama da Figura 4.5, onde é apresentada uma visão geral do projeto da

ferramenta SPACES.

Tanto no projeto de cada módulo como no projeto da ferramentaem si, fizemos uso do

padrão de projetoFacade[GHJV95]. A classeSpacesé a classefacadeda ferramenta, ou

seja, a classe que dá acesso à lógica da aplicação. Essa classe possui uma coleção de objetos

do tipoFunctionality, que representam as funcionalidades especificadas para o componente,

além de métodos-chave para prover acesso às funcionalidades dos diversos módulos da ferra-

menta. Cada objetoFunctionality, por sua vez, possui referência para todos os cenários (tipo

Scenario) especificados para a funcionalidade em questão. No diagrama, podemos observar,

que a implementação dos módulosGerador de Oráculos, Seletor de Dados de Teste, Ge-

rador de Artefatos de Teste, Gerenciador de Persistênciae Empacotador, respectivamente,

nos pacotesoraclegenerator, dataselector, testartifactsgenerator, persistencee packager, é

baseada em composição, ou seja, a lógica destes módulos é definida por uma interface cuja

4.3 Projeto 49

testartifactsgenerator

(from spaces)

Scenario

(from spaces)

- name :String

- exceptionThrow :String

- preCheckCode :String

- postCheckCode :String

- testData :TestData

+ getName ():String

+ setName (name :String ):void

+ getExceptionThrow ():String

+ setExceptionThrow (exceptionThrow :String ):void

+ getPreCheckCode ():String

+ setPreCheckCode (code :String ):void

+ getPostCheckCode ():String

+ setPostCheckCode (code :String ):void

+ getTestData ():TestData

+ setTestData (testData :TestData ):void

+ toString ():String

Spaces

(from spaces)

+ openProject (prjFile :File):Spaces

+ saveProject (spacesObj :Spaces,prjFile :File):void

+ processXMIFile (xmiFile :File):void

+ getModel ():Model

+ addFunctionality (f:Functionality ):void

+ getFunctionalities ():Collection

+ selectTestCases ():void

+ generateOracles ():void

+ selectTestData ():void

+ generateTestCode ():void

Functionality

(from spaces)

- name :String

- numTestCases :int

- redefineSelection :boolean

- generateAll :boolean

- useModel :UseModel

- selectedPaths :Collection

- scenarios :Map

+ getName ():String

+ setName (name :String ):void

+ getNumScenarios (): int

+ getNumTestCases ():int

+ setNumTestCases (num :int ):void

+ isRedefineSelection ():boolean

+ setRedefineSelection (b:boolean ):void

+ isGenerateAll ():boolean

+ setGenerateAll (b:boolean ):void

+ getUseModel ():UseModel

+ setUseModel (model :UseModel ):void

+ getSelectedPaths ():Collection

+ selectPaths ():void

+ addScenario (scenario :Scenario ,path :Path):void

+ getScenario (path :Path):Scenario

+ getMethodName ():String

+ getMethodArgs ():String

+ getClassName ():Stringfunctionalities-

*

xmiparser

uml

Model

xmi

XMIParser

dataselector

(from spaces)

DataSelector

<< interface >>

ConstraintDataSelector

oraclegenerator

(from spaces)

OracleGenerator

<< interface >>

ConstraintToCodeConversor

persistence

(from spaces)

<< interface >>

PersistenceManager

Serializer

testcaseselector

(from spaces)

TestCaseSelector

gui

(from spaces)

TestCaseSelectionForm

SpacesGUI

ocl

(from spaces)

OCLToJavaConversor

OCLDataSelector

scenarios-1..*

ConstraintEvaluationException

packager

(from spaces)

<< interface >>

Packager

ZipPackager

TestArtifactsGenerator

<< interface >>

TestCodeGenerator

Figura 4.5: Visão Geral do Projeto da Ferramenta SPACES

implementação determina a linguagem de especificação de restrições a ser usada, no caso dos

módulosGerador de OráculoseSeletor de Dados de Teste, a linguagem na qual o código de

teste será produzido, no caso do móduloGerador de Artefatos de Teste, além das estratégias

de persistência e empacotamento a ser empregadas, no caso dos módulosGerenciador de

PersistênciaeEmpacotador. Dessa forma, a ferramenta pode ser expandida para dar suporte

à utilização de outras linguagens de especificação de restrições, além de OCL (e.g. Object

4.3 Projeto 50

Z), e à geração de código de teste em diferentes linguagens deprogramação, além de Java

(e.g. C++), bastando, para tanto, que estas interfaces sejam implementadas de acordo com

as características de cada linguagem. A seguir vemos os detalhes do projeto de cada módulo

da ferramenta.

4.3.1 Gerenciador de Persistência

As funcionalidadesAbrir e Salvar Projetoda ferramenta SPACES são implementadas

pelo módulo Gerenciador de Persistência. Esse módulo possibilita o salvamento e a posterior

recuperação das informações de um projeto, que ficam armazenadas na instância corrente da

classeSpaces, em qualquer etapa do processo. Estas informação são salvase recuperadas a

partir de arquivos com a extensão ‘spc’, Figura 4.6.

Figura 4.6: SPACES: Tela Salvar Projeto

Como pode ser visto na Figura 4.7, isso é feito através da interfacePersistenceManager

que define as operaçõesreadObject()e saveObject(). A primeira operação recebe como

parâmetro um objetoFile no qual estão armazenadas as informações, e retorna o objeto

salvo no mesmo estado em que se encontrava quando foi salvo. Asegunda operação recebe

o objeto a ser salvo e um objetoFile que indica o arquivo no qual serão armazenadas as

informações. A classeSerializeré a implementação desta interface a partir do mecanismo

de serialização de objetos da linguagem Java.

4.3 Projeto 51

<< interface >>

PersistenceManager

(from spaces::persistence )

+ readObject (file :File ):Object

+ saveObject (obj :Object ,file :File):void

Serializer

(from spaces::persistence )

Figura 4.7: SPACES: Gerenciador de Persistência

4.3.2 Parser XMI

O parser XMI é o módulo responsável pela recuperação e disponibilização das infor-

mações relacionadas à especificação dos componentes para o restante da ferramenta. Sua

funcionalidade é executada a partir do submenuLer Arquivo XMIdo menuArquivo. O par-

ser foi construído segundo a especificação 1.2 do padrão[OMG02], a mais utilizada pelas

ferramentas de modelagem. Apesar dos arquivos XMI gerados pelas ferramentas de modela-

gem conterem apenas elementos do metamodelo UML, o número deinformações passíveis

de representação torna estes arquivos bastante complexos.Em função disso e, uma vez que

as informações necessárias para a execução da ferramenta serestringem ao conjunto de arte-

fatos definidos pelo método AFCT, optamos por trabalhar apenas com um subconjunto dos

elementos XMI no metamodelo UML. Dessa forma, todos os elementos XMI externos ao

subconjunto, que representavam, portanto, informações inúteis para a ferramenta de teste,

eram ignorados durante oparse. O subconjunto dos elementos XMI considerados pelopar-

serpode ser visto no Anexo B.

Uma vez que arquivos XMI são arquivos XML em essência, fizemosuso de umpar-

serXML para implementar oparserXMI. O parserXML escolhido foi oXerces4, um dos

subprojetos mantidos pelaApache Software Foundation5. Além disso, analisamos a possi-

bilidade de utilização da técnica SAX (Simple API for XML) [Meg05], que é baseada em

eventos, ou da técnica DOM (Document Object Model) [W3C05], que é baseada em árvo-

res. Optamos pela primeira, pois é a mais indicada para o nosso contexto (parsede leitura

4http://xml.apache.org/xerces2-j/5http://www.apache.org/

4.3 Projeto 52

apenas) além de ser mais rápida. A técnica DOM é mais indicadapara situações de leitura e

escrita em arquivos XML, uma vez que é baseada em uma estrutura de dados[Idr05].

Para utilizar a técnica SAX é preciso implementar uma classeque realize o tratamento

dos eventos que são produzidos peloparserXML, principalmente os eventos de abertura e

fechamento de elementos (tags) do arquivo XML. Essa classe, denominadaHandler, é res-

ponsável pelo processamento das informações contida no arquivo XML. No nosso caso, em

função do grande número de elementos XMI que precisavam ser tratados, optamos por criar

não apenas uma classehandlermas um conjunto delas, uma para cada elemento XMI pas-

sível de tratamento, e, uma vez que boa parte do comportamento destas classes era comum,

abstraímos esse código em uma superclasse abstrata comum a todas. Assim, à medida que

os elementos eram lidos peloparserXML, o tratamento dos eventos era delegado aohandler

correspondente e este processava as informações.

API UML

As informações processadas peloparserXMI são disponibilizadas para os demais mó-

dulos da ferramenta sob a forma de uma API (Application Programming Interface) desenvol-

vida para possibilitar o acesso aos elementos de um modelo UML. Esta API é composta por

classes correspondentes ao subconjunto do metamodelo UML que especifica os artefatos uti-

lizados pelo método AFCT. Dessa forma, quando um elemento XMI é lido, a classehandler

que o trata instancia um objeto correspondente na API a partir das informações recuperadas.

No Anexo C são apresentadas as classes que compõem a API assimcomo o relacionamento

entre elas.

Como pode ser visto no Anexo B, cada elemento XMI possui um identificador (xmi.id)

que pode ser referenciado por outros elementos em diferentes partes do arquivo. Em fun-

ção disso, criamos uma tabela de identificação na qual associamos, para cada elemento XMI

lido, seu identificador e o objeto criado na API. Assim, quando um elemento XMI faz refe-

rência a outro, através do atributoxmi.idref, acessamos essa tabela com oxmi.idreferenciado

para obtermos uma referência ao objeto correspondente e estabelecer a associação entre este

objeto e o objeto criado para o elemento.

A classe Model (Figura C.1) é ofacadeda API. Ela armazena, dentre outros elementos,

as classes, interfaces, associações, pacotes, atores e casos de uso especificados no modelo

4.3 Projeto 53

UML. Em função disso, o objeto Model na tabela de identificação possui, além da entrada

associada aoxmi.id, outra associada a uma chave especial que é utilizada, ao final do parsedo

arquivo XMI, para recuperar este objeto e disponibilizar sua referência – e conseqüentemente

todas as informações que armazena – para os demais módulos daferramenta.

4.3.3 Seletor de Casos de Teste

Uma vez concluído oparsedo arquivo XMI, a ferramenta SPACES identifica as funcio-

nalidades e os respectivos cenários de uso especificados para o componente e inicia automa-

ticamente a Seleção de Casos de Teste (menuProjeto, submenuSelecionar Casos de Teste),

apresentando a tela de seleção, Figura 4.8. Nesta tela, são exibidos o nome e o número

de cenários identificados (CI) em cada funcionalidade. Para proceder a seleção, o usuário

precisa fornecer, na colunaCTS, o número de casos de testes a ser selecionado para cada

funcionalidade, ou optar por gerar todos os casos de teste possíveis (um para cada cenário

identificado), assinalando a colunaGT. A colunaRS, por sua vez, indica se a seleção reali-

zada para a funcionalidade correspondente deve ser redefinida, ou seja, quando desmarcada,

determina que o procedimento de seleção não deve ser realizado para a funcionalidade em

questão visto que o conjunto de casos de teste obtido anteriomente já é satisfatório. Em fun-

ção disso, o botãoSelecionarsó estará habilitado se esta coluna estiver assinalada em pelo

menos uma funcionalidade.

Figura 4.8: SPACES: Tela de Seleção de Casos de Teste

4.3 Projeto 54

Ao clicar no botãoSelecionar, a seleção dos casos de teste das funcionalidades cujas

colunasRSestejam marcadas será realizada conforme os procedimentosdo método AFCT.

Será criado um modelo de uso para cada funcionalidade cujas probabilidades dos arcos con-

correntes serão inicialmente iguais e, para cada funcionalidade na qual a colunaGT não

esteja marcada, será apresentado o modelo de uso correspondente destacando os caminhos

(casos de teste) escolhidos na seleção, Figura 4.9.

Figura 4.9: SPACES: Modelo de Uso

Neste momento, o usuário pode alterar as probabilidades do modelo de uso conforme

as decisões tomadas na fase de planejamento do método AFCT. Isso é feito clicando-se

com o botão direito do mouse sobre o vértice de onde partem os arcos concorrentes cujas

probabilidades devem ser alteradas. Dessa forma, será exibida a tela de alteração de proba-

bilidades, Figura 4.10, possibilitando a mudança no valor da probabilidade de todos os arcos

concorrentes do vértice em questão. Uma vez alteradas as probabilidades, o modelo de uso

correspondente é atualizado para exibir os novos valores, Figura 4.11.

4.3 Projeto 55

Figura 4.10: SPACES: Tela de Edição de Probabilidades

Figura 4.11: SPACES: Probabilidades Alteradas no Modelo deUso

Para que as novas probabilidades sejam consideradas, é preciso re-executar o procedi-

mento de seleção ativando novamente o submenuSelecionar Casos de Testedo menuProjeto

para ter acesso à tela de seleção. Esse procedimento deve serrepetido até que o conjunto de

casos de teste selecionado seja satisfatório para o usuário.

Implementação

Na Figura 4.12, vemos as classes que compõem o módulo seletorde casos de teste. A

classefacadedo módulo é a classeTestCaseSelectorque implementa a maioria dos algorit-

mos definidos pelo método AFCT e apresentados no Capítulo 3. As classesUseModel, Node

e Link representam, respectivamente, os modelos de uso criados para as funcionalidades,

seus vértices e seus arcos. A classePath, por sua vez, armazena a coleção das ações dos

arcos que constituem um caminho dentro do modelo de uso.

Cada modelo de uso possui referência para três vértices: o inicial e o final, que são

fixos e possuem, respectivamente, os valores 0 e 1; e o atual, que depende do estado do

modelo de uso e que, inicialmente, corresponde ao vértice inicial. Quando o modelo de uso

é criado, na classeTestCaseSelectora partir da coleção de diagramas de cenário recebida

4.3 Projeto 56

Link

- action :String

- probability :float

- traverse :boolean

- visited :boolean

+ getNodeTo ():Node

+ setNodeTo (node :Node ):void

+ getLabel ():String

+ setLabel (label :String ):void

+ getProbability ():float

+ setProbability (probability :float ):void

+ isTraverse ():boolean

+ setTraverse (v:boolean ):void

+ wasVisited ():boolean

+ setVisited (v:boolean ):void

Node

- value :int

- links :Collection

+ getValue ():int

+ setValue (value :int ):void

+ addLink (link :Link ):boolean

+ getLinks ():Collection

nodeTo-

TestCaseSelector

- getUseCaseScenarios (usecase:UseCase,diagrams :Collection ):Collection

+ createUseModel (scenarioDiagrams :Collection ):UseModel

+ selectPaths (um :UseModel ,n:int ):Collection

+ getAllPaths (uModel :UseModel ):Collection

+ toScenarioDiagrams (um :UseModel ):Collection

- toUseModelAction (st :Stimulus ):String

- toStimulus (action :String ):Stimulus

+ toPathString (sd :SequenceDiagram ):String

Path

- actions :Collection

+ addAction (action :String ):void

+ insertAction (action :String ):void

+ getActions ():Collection

+ toActionsString ():String

UseModel

+ getFirstNode ():Node

+ addAction (action :String ):void

+ addFinalAction (action :String ):void

~getPaths ():Collection

- getPaths (node :Node ):Collection

~selectPath (n:int ):Collection

- selectPath (p:Path,startNode :Node ):void

- selectableLinks (links :Collection ):Collection

~selectPosition (probs :float[] ): int

- fixProbs (probs :float[] ):void

+ reset ():void

FirstNode- LastNode-currentNode-

Figura 4.12: SPACES: Seletor de Casos de Teste

como parâmetro pela operaçãocreateUseModel(), as mensagens dos diagramas (Stimulus)

são convertidas em ações, através da operaçãotoUseModelAction(). Estas ações são então

adicionadas ao modelo de uso através da operaçãoaddAction(). Caso já exista no modelo

de uso um arco com a mesma ação partindo do vértice atual, o vértice atual passará a ser o

vértice destino do arco em questão. Por outro lado, caso não exista um arco com a ação a ser

adicionada partindo do vértice atual, será adicionado neste vértice um novo arco com a ação

em questão e este arco terá como destino um novo vértice que passará a ser o atual. Esse

processo é repetido para todas as ações do diagrama com exceção da última, que é adicionada

a partir da operaçãoaddFinalAction(). Esta operação, como a anterior, adiciona um novo

arco ao vértice atual, porém, este terá como destino o vértice final e o vértice atual passará a

ser novamente o inicial, reiniciando o processo para a adição das ações correspondentes às

mensagens do próximo diagrama.

A seleção dos caminhos no modelo de uso, e, conseqüentemente, dos casos de teste, é

feita a partir da operaçãoselectPaths()da classeTestCaseSelector. Esta operação recebe

como parâmetro o modelo de uso e o número de caminhos a ser selecionado, realizando a

seleção de acordo com os algoritmos do método AFCT.

4.3 Projeto 57

4.3.4 Gerador de Oráculos

Após a definição do conjunto de casos de teste selecionados para as funcionalidades do

componente, é preciso gerar oráculos que sejam responsáveis pelo veredicto destes casos de

teste. Esta funcionalidade, acessada através do submenuGerar Oráculos de Testedo menu

Projeto, consiste na derivação de fragmentos de código a partir de restrições, inicialmente

escritas na linguagem OCL, de invariantes, especificadas naclassefacadeque implementa as

funcionalidades do componente, e de pré e pós-condições, especificadas para os cenários de

uso correspondentes aos casos de teste selecionados para asfuncionalidades do componente.

Estes fragmentos são responsáveis, em tempo de execução, pela determinação do resultado

de cada caso de teste.

Para proceder tal geração era imprescindível a precisão e a correção, tanto sintática

quanto semântica, das restrições OCL. Dessa forma o geradorprecisava prover meios de

detectar antecipadamente qualquer problema existente nasrestrições. Em função disso, op-

tamos pela utilização da ferramentaDresden OCL[HDF00]. Em linhas gerais, Dresden OCL

é um conjunto de módulos de suporte à análise e ao processamento de restrições OCL. Entre

os módulos disponíveis estão:

• Um Parser, gerado pela ferramenta SableCC6 a partir da especificação 1.3 da lingua-

gem OCL e responsável pela análise léxica e sintática das restrições.

• Um Analisador Semântico, que realiza verificações de consistência e checagem de

tipos de acordo com o modelo UML.

• Um Normalizador, que simplifica as restrições OCL para facilitar a geração decó-

digo. Entre os passos de normalização implementados estão aqualificação explícita

de nomes, a inserção de iteradores e de informações de tipo, ea expansão de iteradores

múltiplos.

• Um Gerador de Código, responsável pela geração de código fonte Java para ser usado

em instrumentação (inserção de código de teste na própria implementação a ser tes-

tada). Este código é baseado em uma biblioteca de classes própria que representa o

sistema de tipos OCL.

6http://www.sablecc.org

4.3 Projeto 58

Cada módulo implementa interfaces bem definidas, o que tornapossível a redefinição de

suas funcionalidades de acordo com as necessidades dos usuários. A comunicação entre os

módulos se dá a partir de uma estrutura de dados denominadaÁrvore Sintática Abstrataque

é construída durante oparse, Figura 4.13. A estrutura dessa árvore, Figura 4.14, consiste

na instanciação das regras gramaticais da linguagem OCL[OMG03b] a partir das restrições

processadas pela ferramenta.

NormalizadorAnalisador Semântico

Árvore SintáticaAbstrata

Parser Gerador de Código

Figura 4.13: Dresden OCL: Integração entre os Módulos

Figura 4.14: Dresden OCL: Exemplo de Árvore Sintática Abstrata

4.3 Projeto 59

Para o correto funcionamento, a ferramenta Dresden OCL precisa acessar, além das res-

trições OCL, o modelo UML sob o qual foram especificadas as restrições. Isso é feito a

partir da implementação de uma interface denominadaModelFacade. Esta interface define

a operaçãogetClassifier()que recebe o nome do classificador e, caso este exista no modelo

UML, retorna um objeto correspondente do tipoAnyda biblioteca de classes da ferramenta.

Assim sendo, para possibilitar a integração desta ferramenta com SPACES, foi preciso criar

uma implementação da interfaceModelFacadeque permitisse o acesso as informações do

modelo UML usado por SPACES. Esta classe, denominadaSPACESModelFacade, assim

como as interfaces que constituem o mecanismo de acesso ao modelo UML da ferramenta

Dresden OCL, pode ser vista na hierarquia da Figura 4.15.

<< interface >>

Type

(from tudresden ::ocl::check ::types )

+ navigateQualified (name :Type ,qualifiers :Type[] ):Type

+ navigateParameterized (name :Type ,params :Type[] ):Type

+ hasState (stateName :String ):boolean

<< interface >>

Any

(from tudresden ::ocl ::check::types )

<< interface >>

ModelFacade

(from tudresden ::ocl ::check::types )

+ getClassifier (name :String ):Any

SPACESModelFacade

(from spaces::ocl)

+ getPrimitiveType (paramType :String ):void

+ getType (class:Class):Type

+ toString ():String

Class

(from xmiparser ::uml )

Model

(from xmiparser ::uml )

model-

Figura 4.15: Integração Dresden OCL - SPACES: Acesso ao Modelo UML

A partir da implementação da interfaceModelFacadepela classeSPACESModelFacadee

o conseqüente acesso da ferramenta DresdenOCL às informações do modelo UML contidas

nos objetos da API UML, SPACES passou a se beneficiar das funcionalidades de verificação

(sintática e semântica) e de normalização de restrições OCL, implementadas pelos módulos

desta ferramenta. No entanto, uma vez que o módulo gerador decódigo padrão do Dresden

OCL produz código para instrumentação e baseado em uma API própria, tivemos que re-

implementar esta funcionalidade para a sua utilização no nosso contexto, ou seja, a geração

de código Java puro a ser executado a partir de classes de teste para determinar o resultado

dos casos de teste do componente.

4.3 Projeto 60

O módulo gerador de código é acessado pelo Dresden OCL a partir da interfaceCode-

Generator. Esta interface define a operaçãogetCode()que recebe como parâmetro a árvore

sintática abstrata produzida peloparser (tipo OCLTree) e produz um array de fragmentos

de código (tipoCodeFragment), um para cada restrição OCL submetida ao Dresden OCL.

A hierarquia das classes que constituem o mecanismo de geração de código da ferramenta

pode ser vista, de forma simplificada, na Figura 4.16.

<< interface >>

CodeGenerator

(from tudresden ::ocl ::codegen )

+ getCode (tree :OCLTree):CodeFragment[]

<< interface >>

CodeFragment

(from tudresden ::ocl ::codegen )

+ getCode ():String

...

OCLTree

(from tudresden ::ocl )

#ast :Start

<< interface >>

Switch

(from tudresden ::ocl ::parser ::node )

<< interface >>

Analysis

(from tudresden ::ocl ::parser ::analysis )

+ caseA<Element> (node :A<Element> ):void

...

AnalysisAdapter

(from tudresden ::ocl ::parser ::analysis )

DepthFirstAdapter

(from tudresden ::ocl ::parser ::analysis )

+ caseA<Element> (node :A<Element> ):void

+ inA<Element> (node :A<Element> ):void

+ outA<Element> (node :A<Element> ):void

...

JavaCodeGenerator

(from tudresden ::ocl ::codegen )

<< interface >>

Switchable

(from tudresden ::ocl ::parser ::node )

+ apply (s:Switch ):void

Node

(from tudresden ::ocl ::parser ::node )

Start

(from tudresden ::ocl ::parser ::node )

Figura 4.16: Dresden OCL: Gerador de Código

Para redefinir esta funcionalidade foi preciso, primeiramente, entender como se dá o pro-

cesso de geração de código do Dresden OCL. Este processo é parecido com o definido pela

técnica SAX[Meg05], uma adaptação do padrão de projetoVisitor [GHJV95], utilizado pelo

framework SableCC[ÉGG98] para oparsede arquivos XML, formato este que compreende

a descrição das restrições OCL sob a estrutura da árvore sintática abstrata.

Como dito anteriormente, a classeOCLTreerepresenta a árvore sintática abstrata. A

classe abstrataNode, por sua vez, define uma hierarquia de classes para representação dos

elementos desta árvore. Nessa hierarquia, a classeNodearmazena as informações comuns a

todos os elementos, enquanto suas subclasses, a exemplo da classeStart, armazenam as in-

formações específicas de cada elemento. Ambas as classes (OCLTreeeNode) implementam

a interfaceSwitchableque define a operaçãoapply(). Esta operação delega o tratamento das

4.3 Projeto 61

informações contidas na subárvore iniciada peloNodecorrespondente ou, no caso da classe

OCLTree, peloNodeinicial (ast), para o objetoSwitchrecebido como parâmetro, de forma

semelhante ao que ocorre com os objetosHandlerna técnica SAX. A interfaceSwitchdefine

uma hierarquia de tipos que implementam estratégias de tratamento das informações conti-

das nos elementos da árvore. No caso da geração de código, fazemos uso da subinterface

Analysis. Esta interface define métodos cujos nomes seguem a formacaseA<Element>()

em queA<Element>é nome das subclasses deNodeque representam os diversos elemen-

tos da árvore. Cada método recebe como parâmetro um objeto dasubclasse correspondente

e, à medida que a árvore vai sendo analisada, eles vão sendo chamados, possibilitando,

assim, o tratamento das informações contidas nestes objetos. A classeAnalysisAdapterim-

plementa o padrão de projetoAdapter[GHJV95] fornecendo implementaçõesdefaultpara

os métodos da interfaceAnalysis, enquanto que sua subclasseDepthFirstAdapterredefine

essas implementações segundo a estratégia de busca em profundidade. Como conseqüência

dessa redefinição, essa classe disponibiliza, além dos métodoscaseA<Element>(), os mé-

todosinA<Element>()e outA<Element>(), possibilitando tratamentos diferenciados para o

início e o final do processamento dos elemento da árvore.

A nossa implementação consistiu na redefinição da classeJavaCodeGenerator. Esta

classe que implementa a interfaceCodeGeneratore herda as implementações de análise ba-

seada em busca em profundidade da classeDepthFirstAdapter, redefine o comportamento

dos métodosinA<Element>(), outA<Element>()e/oucaseA<Element>()para os elemen-

tos que armazenam as informações relevantes ao nosso contexto, procedendo a geração de

expressões Java de acordo com a seqüência em que os elementossão lidos e o mapeamento

proposto pelo método AFCT (Anexo A). Dessa forma, a implementação da operaçãoget-

Code()da interfaceCodeGeneratoré bastante trivial, consistindo na chamada da operação

apply()da classeOCLTreefornecendo como parâmetro a instância corrente (this) da classe

JavaCodeGenerator. Isso dá início à análise da árvore e ao processamento das informações

contidas nos seus elementos.

As expressões Java que são geradas vão sendo armazenadas dentro de métodos cujos no-

mes seguem a formacheck_<nome_do_cenário>_<tipo_da_restrição> para pré e pós-

condições oucheck_<nome_da_restrição> para invariantes. Para restrições de pré ou pós-

condições, esses métodos recebem como parâmetro, além do objeto contexto da restrição,

4.3 Projeto 62

todos os parâmetros especificados para a operação em questãoe, no caso de pós-condições

de operações cujo tipo de retorno seja diferente devoid, o resultado da operação. O va-

lor de retorno do método produzido é umbooleanque indica o resultado da avaliação da

restrição para os parâmetros recebidos. Ao final do processo, esse código é utilizado na cria-

ção do objetoCodeFragmentque é retornado, na forma de um array unitário, pela operação

getCode().

Uma vez redefinida a funcionalidade de geração de código do Dresden OCL, o módulo

gerador de oráculos foi diretamente implementado a partir dos módulos desta ferramenta.

Na Figura 4.17, vemos as classes que compõem este módulo.

OCLToJavaConversor

(from spaces::ocl )

OracleGenerator

(from spaces::oraclegenerator )

+ processScenarioConstraint (constraint :String ,scenario :Scenario ):String

+ processInvariant (constraint :String ):String

+ getConstraintToCodeConversor ():ConstraintToCodeConversor

+ setConstraintToCodeConversor (ccc:ConstraintToCodeConversor ):void

<< interface >>

ConstraintToCodeConversor

(from spaces::oraclegenerator )

+ getConstraintLanguage ():String

+ getCodeLanguage ():String

+ convert (constraint :String ,model :Model ):String

<< interface >>

CodeGenerator

(from tudresden ::ocl ::codegen )

DepthFirstAdapter

(from tudresden ::ocl ::parser ::analysis )

JavaCodeGenerator

(from spaces::ocl )

Model

(from xmiparser ::uml )

Figura 4.17: SPACES: Gerador de Oráculos

A classeOracleGeneratoré a classefacadedo módulo. Para cada cenário selecionado de

cada funcionalidade especificada para o componente, identificamos suas restrições OCL de

pré e pós-condição, especificadas nos diagramas de seqüência correspondentes à especifica-

ção destes cenários, além das invariantes das classesfacadedo componente que fornecem as

funcionalidades em questão. As restrições de pré e pós-condição são então submetidas, jun-

tamente com o objetoScenariocorrespondente, à operaçãoprocessScenarioConstraint()e as

invariantes são submetidas à operaçãoprocessInvariant(). Estas operações fazem uso de um

objeto do tipoConstraintToCodeConversorpara converter as restrições nos fragmentos de

código que irão compor o oráculo. A classeOCLToJavaConversorfornece a implementação

da interfaceConstraintToCodeConversorpara a conversão de restrições OCL em fragmentos

de código Java. Essa implementação faz uso da classeJavaCodeGeneratorque redefinimos

4.3 Projeto 63

anteriormente para submeter as restrições às funcionalidades do Dresden OCL e, caso estas

estejam isentas de erro, obter, ao final do processo, os fragmentos de código Java correspon-

dentes.

Cada oráculo de teste é então constituído a partir dos fragmentos de código derivados das

restrições de pré e pós-condição do cenário correspondentee do código derivado das invari-

antes da classe que fornece a funcionalidade, sendo este último comum a todos os oráculos

de teste correspondentes aos cenários de uso selecionados para esta funcionalidade. Caso

haja algum problema com alguma restrição OCL, tanto de ordemsintática (relacionado à

gramática OCL) quanto semântica (relacionada ao modelo UML), esse processo será inter-

rompido e será exibida uma mensagem indicando a funcionalidade e o cenário no qual o erro

foi encontrado.

4.3.5 Seletor de Dados de Teste

Os dados de teste constituem, juntamente com os casos de teste e os oráculos, a especifi-

cação do teste. O seletor de dados de teste é o módulo responsável pela escolha de dados que

possam exercitar os diferentes cenários selecionados paracada funcionalidade especificada

para o componente. Esta funcionalidade, acessada através do submenuSelecionar Dados

de Testedo menuProjeto, realiza a derivação de valores a partir das pré-condições de cada

cenário, fazendo uso da técnica de particionamento por equivalência e, parcialmente, da téc-

nica de identificação de partições, ambas apresentadas na definição do método AFCT, no

Capítulo 3.

A implementação parcial da técnica de identificação de partições na versão inicial da

ferramenta foi uma decisão de projeto que tomamos para o não compromentimento das de-

mais etapas do desenvolvimento em função da complexidade inerente a esta atividade. Dessa

forma, algumas restrições podem não ser analisadas corretamente, ocasionando a não sele-

ção de dados de partições estabelecidas por estas restrições. Como o comportamento padrão

da ferramenta é gerar valores aleatórios para as variáveis necessárias à execução do teste

que não tenham sido referenciadas nas restrições OCL, a não identificação de partições pode

fazer com que sejam atribuídos valores aleatórios às variáveis que são referenciadas nos ter-

mos não avaliados das restrições, o que pode ocasionar o descumprimento das pré-condições

e, conseqüentemente, um resultado incorreto na execução doteste. Isso ocorre, principal-

4.3 Projeto 64

mente, com restrições mais complexas, envolvendo tipos objeto e operações sobre coleções.

Nesses casos, torna-se necessária uma interação do usuáriopara revisar e/ou alterar os dados

selecionados, o que, como veremos adiante, poderá ser feitono momento da execução dos

testes.

Na Figura 4.18, vemos as classes que constituem o módulo seletor de dados e o relacio-

namento entre elas.

DepthFirstAdapter

(from tudresden ::ocl ::parser ::analysis )

Model

(from xmiparser ::uml )

TestData

(from spaces::dataselector )

+ addCreateAction (order_type :String ,variable :String ):void

+ getCreateActions ():Map

+ addAddAction (colExp :String ,variable :String ):void

+ getAddActions ():Map

+ addDataSet (dataSet :Map ):void

+ getDataSets ():Collection

+ addUncoveredExpression (uncoveredExpression :String ):void

+ getUncoveredExpressions ():Collection

DataSelector

(from spaces::dataselector )

+ selectTestData (pre_constraint :String ,scenario :Scenario ):void

+ getConstraintDataSelector ():ConstraintDataSelector

+ setConstraintDataSelector (cdc:ConstraintDataSelector ):void

OCLDataSelector

(from spaces::ocl )

Scenario

(from spaces)

OCLTree

(from tudresden ::ocl )

<< interface >>

ConstraintDataSelector

(from spaces::dataselector )

+ selectData (constraint :String ,model :Model ):TestData

Figura 4.18: SPACES: Seletor de Dados

A classefacadedo módulo é a classeDataSelector. Ela fornece a operaçãoselectTest-

Data() que recebe como parâmetro a restrição de pré-condição e o cenário correspondente

para o qual será feita a seleção. A funcionalidade desta operação é delegada a um objeto

do tipoConstraintDataSelector. Esta interface especifica a operaçãoselectData()a ser im-

plementada por classes especializadas na seleção dos dadosa partir de restrições expressas

em diferentes linguagens. Essa operação recebe, como parâmetros, a restrição e o modelo

UML, e retorna um objetoTestDatacontendo os dados selecionados a partir desta restrição.

Como, inicialmente, SPACES trata apenas de restrições OCL,a implementação padrão desta

interface é fornecida pela classeOCLDataSelector.

Assim como no módulo gerador de oráculos, optamos por uma implementação baseada

na ferramenta Dresden OCL em função do arcabouço fornecido por esta ferramenta para a

4.3 Projeto 65

análise e o processamento de restrições OCL, e da integraçãopreviamente realizada entre

as duas ferramentas. Da mesma forma que a classeJavaCodeGeneratordo módulo gera-

dor de oráculos, a classeOCLDataSelectorestende a classeDepthFirstAdapterdo Dresden

OCL, herdando seu comportamento de análise baseada em buscaem profundidade sobre a ár-

vore sintática abstrata (OCLTree). Dessa forma, redefinimos o comportamento dos métodos

inA<Element>(), outA<Element>()e/oucaseA<Element>()com o objetivo de identificar as

expressões lógicas, as variáveis e os valores que estas poderiam assumir para satisfazer as

restrições.

Toda restrição é composta por pelo menos uma expressão lógica. As variáveis em questão

são parâmetros aos quais valores (dados) precisam ser atribuídos para determinar o resultado

das expressões. Estes parâmetros podem ser explícitos, a exemplo dos parâmetros de uma

operação na pré-condição correspondente, ou implícitos, acessados a partir do objeto con-

texto. Uma variável é dita simples quando é explícita e seu tipo é primitivo, ao passo que as

variáveis compostas são aquelas que consistem em atributosde objetos, podendo ser explí-

citas ou implícitas. O nome das variáveis compostas é construído a partir do nome do objeto

e do nome do atributo separados pelo caracter‘.’ . Como as linguagens de especificação de

restrições são livres de efeitos colaterais, não existem operações de atribuição e, conseqüen-

temente, toda variável referenciada em uma restrição precisa ter um valor atribuído fora de

seu escopo. Quando na restrição, estas variáveis são associadas a valores, através de opera-

dores relacionais (<, <=, =, <>, >=, >), estabelecem-se relações que são satisfeitas por faixas

de valores delimitados pelos valores em questão. Essas faixas de valores são as partições

de equivalência. Neste caso, as relações são decompostas deacordo com os padrões lógicos

estabelecidos pelo método AFCT e, para cada partição obtida, a seleção atua identificando o

domínio de dados que satisfaz a relação correspondente e escolhendo, dentro deste domínio,

valores aleatórios e valores próximos ou iguais (dependendo do operador relacional) ao(s)

valor(es) limite(s). Quando, por outro lado, a variável é explícita mas não é referenciada

diretamente na restrição ou não esteja relacionada a valores, nenhuma partição é identificada

e serão selecionados valores aleatórios dentro do domínio do tipo da variável. Neste caso,

particularmente para variáveis do tipoString, serão geradas cadeias de caracteres de tamanho

nulo e aleatório cujos caracteres também serão escolhidos de forma aleatória, e, para variá-

veis do tipoCollection, serão geradas coleções vazias e coleções com um número aleatório

4.3 Projeto 66

de valores. Isso ocorre por exemplo em objetos nos quais apenas um atributo é referenciado

na restrição sendo necessária a geração de valores aleatórios para os demais atributos.

Os valores selecionados são armazenados no objetoTestDatasob a forma de estruturas

de dados denominadasDataSets. CadaDataSetcontém um valor selecionado para cada va-

riável referenciada na restrição, e eles são gerados a partir da permutação de todos os valores

selecionados para estas variáveis. Isso significa que, se por exemplo uma restrição referencia

3 variáveis e foram selecionados, respectivamente, 3, 2 e 1 valor para estas variáveis, tere-

mos 3x2x1=6DataSets, o que corresponde ao número de execuções distintas que um caso

de teste pode ter. Nós implementamos o conceito deDataSetsob a forma de umaMap de

Mapsna qual as duas chaves são constituídas a partir do nome da variável e o valor (daMap

interna) é o dado que deve ser atribuído. Quando a variável é composta, a chave do 1o nível

(Map externa) é o nome do objeto que detém o atributo e a chave do 2o nível (Map interna)

é o atributo. Quando, porém, a variável é simples, a chave do 1o nível é a própria variável e

a chave do 2o nível é o caracter‘.’ , indicando que o nome da variável é o contido na chave

de 1o nível. Para variáveis do tipo coleção, o valor noDataSettambém será uma coleção

enquanto que, para as demais variáveis, o valor será único e do mesmo tipo da variável.

Além dos dados propriamente ditos, o processo de seleção precisa prover as informações

necessárias à criação das variáveis, principalmente as queenvolvem, objetos e coleções. Es-

tas informações ficam armazenadas nos objetosTestDatasob a forma deCreateActions. As

ações de criação são armazenadas em umaMapna qual a chave é usada para indicar a ordem

na qual a variável deve ser criada e o tipo da variável, sendo estas informações separadas pelo

caracter‘_’ , e o valor é usado para armazenar a coleção dos nomes das variáveis a ser cria-

das. Opcionalmente, quando estas variáveis destinam-se aopreenchimento de uma coleção,

seus nomes serão precedidos por um número indicando quantasvezes a variável deverá ser

criada e pelo caracter de separação‘_’ . Neste caso, deverá ser criada uma coleção na qual a

variável será adicionada. As informações que indicam que variáveis devem ser adicionadas e

em que coleções elas devem ser adicionadas está contida no objeto TestDatasob a forma de

AddActions. Como as de criação, as ações de adição também são armazenadas em umaMap.

Neste caso, a chave é usada para indicar o nome da variável coleção e o valor armazena a

coleção de variáveis que devem ser armazenadas.

Os objetosTestDatapossuem ainda uma coleção deStringsdenominadaUncoveredEx-

4.3 Projeto 67

pressionsque é usada para armazenar as expressões lógicas a partir dasquais não foi possível

derivar de forma automática nenhum valor para as variáveis envolvidas. Isso ocorre princi-

palmente em restrições que envolvam expressões complexas usando operações aninhadas

sobre coleções e/ou chamadas a operações de classes. Neste caso, essas expressões devem

ser posteriormente analisadas e valores devem ser selecionados de forma manual.

Por fim, uma vez que o objetoTestDatafoi construído a partir da restrição de pré-

condição, ele é atribuído, ao final da operaçãoselectTestData()da classeDataSelector, ao

objetoScenariocorrespondente.

4.3.6 Gerador de Artefatos de Teste

Uma vez realizada a seleção dos casos de teste, a geração dos oráculos e a seleção dos

dados, as informações fornecidas por estas atividades são usadas para a construção dos ar-

tefatos de teste. Esta funcionalidade, acessada através dosubmenuGerar Artefatos de Teste

do menuProjeto, consiste na produção do código de teste e de arquivos contendo os dados

selecionados, que devem ficar separados do código de teste para possibilitar a sua alteração

em tempo de execução, sem a necessidade de recompilação do código fonte dos testes. Na

Figura 4.19, vemos as classes que compõem o módulo gerador deartefatos de teste assim

como os relacionamentos entre elas.

A classefacadedo módulo é a classeTestArtifactsGenerator. Essa classe possui dois

atributos:destdir, que indica o local no qual deverão ser gerados os artefatos de teste, ecom-

pileTestCode, que informa se o código de teste gerado deve ser compilado pela própria fer-

ramenta. Para produzir o código de teste, o módulo utiliza uma implementação da interface

TestCodeGenerator. Esta interface define operações a ser implementadas por classes espe-

cializadas na geração de código de teste em uma determinada linguagem de programação e

de acordo com umframeworkde teste xUnit adequado a esta linguagem. A implementação

desta interface fornecida com a ferramenta SPACES é dada pela classeJavaTestCodeGene-

rator que realiza a geração de código de teste na linguagem Java e segundo o framework de

teste JUnit[GB05]. A hierarquia na qual o código de teste é gerado pela ferramenta pode ser

vista na Figura 4.20.

Para cada classefacadedo componente, ou seja, as classes que exportam suas funcio-

4.3 Projeto 68

TestArtifactsGenerator

- destdir :String

- compileTestCode :boolean

+ getDestDir ():String

+ setDestDir (dir :String ):void

+ isCompileTestCode ():boolean

+ setCompileTestCode (value :boolean ):void

+ getTestCodeGenerator ():TestCodeGenerator

+ setTestCodeGenerator (tcg :TestCodeGenerator ):void

+ getCompiler ():Compiler

+ setCompiler (c:Compiler ):void

+ generateTestClassFile (facadeClassName :String ,invariantCheckCodes :Collection ):void

+ generateTestClassFiles (functionalities :Collection ):void

- generateXMLDescriptorFile ():void

+ generateXMLTestDataFiles (functionalities :Collection ):void

<< interface >>

TestCodeGenerator

+ generateTestClass (facadeClassName :String ,invariantCheckCodes :Collection ):TestClass

+ generateTestClass (functionality :Functionality ,model :Model ):TestClass

+ generateAllTestClass (testclasses :Collection ):TestClass

JavaTestCodeGenerator

XMLGenerator

+ write (dom :Document , fileaname :String ):void

Model

(from xmiparser ::uml )

TestClass

- className :String

- code:StringBuffer

+ getClassName ():String

+ setClassName (name :String ):void

+ getCode ():String

+ appendCode (code:String ):void

+ appendCode (code:StringBuffer ):void

<< interface >>

Compiler

+ compile (testClassFiles :Collection ,destdir :String ):void

JavaCompiler

Figura 4.19: SPACES: Gerador de Artefatos de Teste

TestCase

(from junit ::framework )

TestDataAccess

(from spaces)

+ getStringData (params :Hashtable ,variable :String ,posicao :int ):String

+ getIntData (params :Hashtable ,variable :String ,posicao :int ): int

+ getDoubleData (params :Hashtable ,variable :String ,posicao :int ):double

+ getBooleanData (params :Hashtable ,variable :String ,posicao :int ):boolean

+ getByteData (params :Hashtable ,variable :String ,posicao :int ):byte

+ getLongData (params :Hashtable ,variable :String ,posicao :int ): long

+ getFloatData (params :Hashtable ,variable :String ,posicao :int ):float

<Facade>Test

#check_invariants (self :<Facade> ):boolean

- check_<Invariant1> (self :<Facade> ):boolean

- check_<Invariant2> (self :<Facade> ):boolean

...

<Functionality>Test

- check_<Scenario1>_pre (self :<Facade> ,... :i):boolean

- check_<Scenario1>_post (self :<Facade> ,...: i):boolean

+ test<Scenario1> ():void

- check_<Scenario2>_pre (self :<Facade> ,... :i):boolean

- check_<Scenario2>_post (self :<Facade> ,...: i):boolean

+ test<Scenario2> ():void

...

tda#

JTestCase

(from net ::wangs ::jtestcase )

<< create >> + JTestCase(xmlFileName :String ,className :String ):JTestCase

+ getNameOfTestCases (methodName :String ):Vector

+ getTestCaseParams (methodName :String ,testCase :String ):Hashtable

Figura 4.20: SPACES: Hierarquia do Código de Teste

nalidades, será gerada, a partir da operaçãogenerateTestClassFile()da classeTestArtifacts-

Generator, uma classe de teste abstrata cujo nome será o nome da classefacadeseguido do

sufixo Test . O papel desta classe, que estende a classeTestCasedo frameworkde teste, é

4.3 Projeto 69

armazenar o código gerado pelo módulo gerador de oráculos a partir das invariantes defi-

nidas para esta classe. A operaçãogenerateTestClassFiles(), por sua vez, gera, para cada

funcionalidade do componente, uma classe de teste com o nomeda funcionalidade seguido

do sufixoTest . Esta classe terá como super-classe a classe de teste geradapara a classefa-

cadeque fornece a funcionalidade em questão. Nestas classes de teste são adicionados, para

cada cenário de uso selecionado, os códigos produzidos pelogerador de oráculos a partir das

pré e pós-condições, além do método de teste correspondente, cujo nome é composto pelo

nome do cenário precedido da palavratest , e que, de uma forma geral, possui o formato

apresentado na Figura 4.21.

public void test<Nome_Cenario>(){

...

If (check_invariants(self) &&check_<Nome_Cenario>_pre(self, <Variáveis>)) {

self.<Nome_Funcionalidade>(<Variáveis>);assertTrue(check_invariants(self) &&

check_<Nome_Cenario>_post(self, <Variáveis>));}else{

fail();}

}

/* Criação e instanciação de variáveis com dados externos *//* através do objeto TestDataAccess e da API JTestCase */

/* Violação de pré-condição ou invariantes - Resultado Indefinido */

Figura 4.21: SPACES: Formato de Métodos de Teste para Cenários

Os dados de teste usados na instanciação das variáveis necessárias a execução das funcio-

nalidades do componente, e que estão localizados em arquivos a parte, são acessados pelos

métodos de teste de cada cenário a partir de um objeto do tipoTestDataAccess. Esta classe

implementa operações para recuperação de dados de todos os tipos primitivos da linguagem

Java. Essas operações recebem como parâmetro uma tabelahash, que associa as variáveis

aos dados disponíveis para o cenário em questão, o nome da variável através do qual a tabela

deve ser acessada, além de um inteiro para possibilitar a escolha determinística de um valor

quando a variável for do tipo coleção e estivermos criando osseus elementos.

Para prover a separação dos dados de teste em arquivos a partee o acesso a esses dados

a partir do código de teste, fizemos uso da ferramenta JTestCase[Wan05]. Trata-se de uma

ferramenta livre que utiliza um formato XML bem definido paraa representação de dados

4.3 Projeto 70

de teste e disponibiliza a infra-estrutura necessária parao acesso a esses dados a partir de

classes de teste JUnit. Como pode ser visto na Figura 4.22, o formato XML definido por

esta ferramenta suporta a representação de valores tanto para variáveis simples, de tipos

primitivos, quanto para variáveis complexas, como coleções e tabelashash. Esses dados

ficam agrupados de acordo com a classe e o método de teste a que pertencem, representados

respectivamente pelos elementosclasse method, além do atributotest-casedo elemento

method, que a ferramenta define como “caso de teste”, mas que, na verdade, indica diferentes

conjuntos de dados que podem ser usados no caso de teste representado pelo método em

questão, constituindo assim diferentes execuções de um mesmo caso de teste. Dessa forma,

em um mesmo arquivo XML do JTestCase, podem ser armazenados dados de diferentes

classes, métodos e execuções de teste.

<?xml version ="1.0" encoding = "UTF-8"?><tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="<URI of jtestcase.xsd>"><class name="class name">

<method name="method name" test-case="name your test case here"><params>

<param name="variable name" type="variable type">variable value</param>

<param name="variable name" type="java.util.Vector"value-type="value type">

<param>value1</param><param>value2</param>

</param>

<param name="variable name" type="java.util.Hashtable"key-type="key type" value-type="value type">

<param name="key1">value1</param><param name="key2">value2</param>

</param>

</params></method>

</class>

</tests>

<!-- variáveis simples -->

<!-- variáveis coleção -->

<!-- outros valores -->

<!-- variáveis tabela hash -->

<!-- outros valores -->

<!-- outros parâmetros -->

<!-- outros métodos -->

<!-- outras classes -->

Figura 4.22: JTestCase: Formato de Arquivos XML

As classes de teste das funcionalidades do componente acessam a API JTestCase atra-

vés da classeJTestCase. Essa classe, cujos objetos são construídos a partir dos nomes do

arquivo XML que contém os dados e da classe de teste neste arquivo, possui duas operações

principais:getNameOfTestCases(), que retorna os conjuntos de dados especificados para um

método de teste no arquivo XML; egetTestCaseParams(), que retorna uma tabelahashdas

4.3 Projeto 71

variáveis e seus respectivos valores especificados em um conjunto de dados de um método no

arquivo XML. Cada classe de teste de funcionalidade (<FunctionalityTest>) cria um objeto

JTestCasea partir do arquivo XML correspondente e, nos métodos de teste de cada cenário,

recuperam a tabelahashdos dados de cada conjunto. Essas tabelashashsão então usadas

para executar as operações da classeTestDataAccesse, dessa forma, instanciar as variáveis

necessárias a execução da funcionalidade.

Os arquivos XML do JTestCase são gerados pelo módulo a partirda operaçãoGene-

rateXMLTestDataFiles()da classeTestArtifactsGenerator. Este método, que recebe como

parâmetro a coleção de funcionalidades do componente (tipoFunctionality), cria, a partir

dosDataSetsde cada cenário selecionado das funcionalidades, objetosDocumentda API

DOM [W3C05] que representam a estrutura em árvore dos elementos XML que compõem

o formato definido pelo JTestCase. É gerado umDocumentpara cada funcionalidade e este

irá conter os dados para os métodos de teste correspondentesaos cenários selecionados na

funcionalidade. Esses objetos são então usados pelo métodowrite() da classeXMLGenera-

tor para a geração dos arquivos XML de dados, que terão o mesmo nome das classes de teste

correspondentes.

Uma vez gerado o código de teste para as funcionalidades do componente, será criado,

a partir do métodogenerateXMLDescriptorFile()da classeTestArtifactsGenerator, um ar-

quivo XML com informações relacionadas ao código gerado. Este arquivo, denominado

Descriptor.xmle cujo formato pode ser visto na Figura 4.23, descreve a correspondência

entre as classes de teste e as funcionalidades, e entre os métodos de teste e os cenários

selecionados nas funcionalidades. As informações contidas neste arquivo serão usadas pos-

teriormente para auxiliar o procedimento de execução dos testes.

<?xml version ="1.0" encoding = "UTF-8"?><component name="component name">

<functionality name="functionality name" test-class="functionality test-class"><scenario name="scenario name" test-method="scenario test-method"/>

</functionality>

</component>

<!-- outros cenários -->

<!-- outras funcionalidades -->

Figura 4.23: SPACES: Formato XML para Descrição do Código deTeste Gerado

Ao final do processo de geração dos artefatos, SPACES pode, opcionalmente, realizar

4.3 Projeto 72

a compilação do código de teste produzido. Para tanto, é preciso que o atributocompile-

TestCodeda classeTestArtifactsGeneratorapresente o valortrue e que a ferramenta tenha

acesso à implementação do componente através da variável deambienteCLASSPATH. A

compilação é feita através de um objeto do tipoCompiler. Essa interface define a operação

compile(), que recebe como parâmetro a coleção dos arquivos que contêmas classes de teste

e o local no qual o código compilado deverá ser depositado, e deve ser implementada de

acordo com as características de cada linguagem de programação usada na construção do

código de teste. Como a ferramenta gera código de teste em Java, a implementação padrão

desta interface é a classeJavaCompiler, que realiza a compilação do código a partir de uma

API fornecida com o kit de desenvolvimento de software da linguagem.

4.3.7 Empacotador

A última etapa da ferramenta SPACES é a construção de um pacote contendo os artefatos

de teste gerados para o componente (classes de teste, arquivos XML de dados e o arquivo

XML de descrição) e que deve ser distribuído juntamente com este para possibilitar a execu-

ção dos testes. O responsável pela construção deste pacote éo módulo empacotador. Como

vemos na Figura 4.24, as funcionalidades deste módulo são definidas pela interfacePacka-

ger. A operaçãopack()é responsável pela geração de um pacote com o nome definido pelo

parâmetropackFileNamea partir dos arquivos contidos no diretóriodir. Caso este diretó-

rio não exista ou ocorra algum problema no acesso ao sistema de arquivos, como falta de

permissões de leitura ou escrita, será lançada a exceçãoPackagerException. A operaçãoun-

Pack()realiza a operação inversa a da operaçãopack(), ou seja, extrai os arquivos contidos

no pacotepackFileName. Por fim, temos a operaçãodelete()que é utilizada para remover os

arquivos gerados após a construção do pacote de teste. A classeZipPackageré a implemen-

tação padrão desta interface. Esta classe, que foi implementada segundo o padrão de projeto

Singleton[GHJV95], procede a construção de pacotes de teste no padrão de compressãoZIP

a partir da API da linguagem Java. Os pacotes de teste são gerados sob a forma de arquivos

JAR (Java Archive), o formato de empacotamento padrão da linguagem Java.

4.4 Testador de Componentes 73

ZipPackager

+ getInstance ():ZipPackager

PackagerException

<< interface >>

Packager

+ pack (dir :String ,packFileName :String ):void

+ unPack(packFileName :String ):void

+ delete (file :File ):void

Figura 4.24: SPACES: Empacotador

4.4 Testador de Componentes

Como vimos, ao final do processamento da ferramenta SPACES, obtemos um pacote

contendo os artefatos de teste produzidos pela ferramenta apartir da especificação do com-

ponente a ser testado. Esse pacote deve então ser distribuído juntamente com o componente

para possibilitar a execução dos testes gerados pela ferramenta. Para tanto, também deve ser

distribuída uma aplicação capaz de proceder a execução dos casos de teste. Essa aplicação é

o Testador de Componentes. Entre as suas características gerais destacam-se:

• Implementação baseada emframeworksde teste xUnit, fazendo uso de toda a infra-

estrutura provida por estes para a execução de casos de teste.

• Acesso à implementação do componente e ao pacote de testes deforma estática, ou

seja, através doCLASSPATH7.

• Possibilidade de execução dos testes em 3 níveis:

Cenário(Apenas um cenário de uma funcionalidade)

Funcionalidade(Todos os cenários de uma funcionalidade)

Componente(Todos os cenários de todas as funcionalidades)

• Três resultados possíveis para cada execução de teste:Falha, Sucessoe Indefinição,

sendo este último reservado para situações em que o caso de teste não é executado em

função de violação das pré-condições correspondentes.

• Possibilidade de edição (incluindo adição e remoção) e validação dos dados de teste

de cada cenário dinamicamente, i.e. em tempo de execução.

7Variável de ambiente que define a localização das bibliotecas de classes necessárias à execução de uma

aplicação JAVA.

4.4 Testador de Componentes 74

• Persistência das alterações feitas nos dados de teste nos respectivos arquivos XML de

dados do pacote de teste.

Como SPACES gera código de teste, por padrão, na linguagem Java e segundo ofra-

meworkJUnit, desenvolvemos o testador baseado nesteframework. A tela principal da apli-

cação pode ser vista na Figura 4.25.

Figura 4.25: Testador de Componentes: Tela Principal

A funcionalidade a ser testada é escolhida no campoFunctionality Nameque apresenta

a lista de funcionalidades disponíveis além do valorAll, que corresponde a escolha de todas

as funcionalidades. A partir da escolha da funcionalidade,são exibidos, no campoScenario

Resultse sob a forma de árvore, os cenários de uso que foram selecionados para a mesma.

Essas informações são recuperadas do arquivo descritor contido no pacote de teste gerado

por SPACES. Uma vez escolhida a funcionalidade, é possível proceder a execução do teste de

todos os seus cenários, a partir do botãoRunsuperior, ou escolher um determinado cenário

no campoScenario Resultse proceder a sua execução através do botãoRuninferior. Como

ocorre no JUnit, o resultado da execução do(s) teste(s) é apresentado na barra de progresso e

nos contadores estatísticos, a saber:

• Runs, que indica o número de testes executados em face ao número total;

4.4 Testador de Componentes 75

• Errors, que indica o número de erros decorrentes de exceções não previstas lançadas

durante a execução do(s) teste(s);

• Failures, que informa o número de falhas detectadas na execução do(s)teste(s);

• Undefineds, que informa o número de testes que não foram executados em função de

violações de pré-condições e que, portanto, apresentam resultado indefinido.

O resultadoUndefinedfoi implementado como uma especialização do resultadoFailure

padrão do JUnit. Após a execução, caso todos os testes tenhamalcançado sucesso, a barra de

progresso apresentará a cor verde. Se, entretanto, algum teste apresentar resultado indefinido,

a barra assumirá a cor amarela. Por fim, caso algum teste falhe, seja devido a ocorrência de

umaFailure ou de umError, a barra de progresso passará a ter a cor vermelha.

Quando um cenário é escolhido no campoScenario Results, é possível alterar os dados

utilizados para executar o teste deste cenário e que estão contidos nos arquivos XML do

pacote de teste. Isso é feito a partir do botãoTest Data. Ao pressionar o botão, é exibida a

tela de alteração de dados, Figura 4.26. Nesta tela é possível escolher entre editar, adicionar

ou remover conjuntos de dados (DataSets) para o cenário de uso em questão. Cada conjunto

de dados será utilizado, durante o teste do respectivo cenário, em uma execução distinta do

procedimento de teste. Os dados de cada conjunto são alterados na tabela localizada na parte

inferior da tela. Essa tabela possui uma entrada para cada variável relevante para o cenário de

uso. As colunasType, Variablee Valueinformam, respectivamente, o tipo, o nome e o valor

atual de cada variável. A edição é feita clicando-se na colunaValuee digitando o novo valor

para a variável. Caso a variável seja do tipo coleção, será aberta uma nova tabela contendo

os elementos atuais da coleção e será possível adicionar novos elementos e editar ou remover

os elementos atuais.

Os dados alterados e/ou inseridos são validados sintática esemânticamente. A validação

sintática ocorre no momento em que se edita o valor de uma variável e consiste em verificar

se o novo valor está de acordo com o tipo da variável. A validação semântica, por sua vez,

ocorre quando é requisitado, através do botãoSave, o salvamento das alterações no arquivo

XML. Neste instante os objetos que contêm as variáveis serãoinstanciados e os dados serão

atribuídos. Se alguma exceção for lançada durante a atribuição dos dados, estes serão consi-

derados inválidos para o contexto de uso no componente. Casoisso ocorra, será solicitada a

4.5 Considerações Finais 76

Figura 4.26: Testador de Componentes: Tela de Alteração de Dados

atribuição de novos valores para as variáveis correspondentes. Caso não haja problemas com

os novos dados, estes serão salvos no arquivo XML correspondente e passarão a ser usados

na execução dos testes.

4.5 Considerações Finais

Neste capítulo, apresentamos as características e funcionalidades da ferramenta SPACES.

Vimos como sua arquitetura foi definida a partir das etapas dométodo AFCT, como é feito o

processamento dos arquivos XMI para possibilitar a integração da ferramenta com ferramen-

tas de modelagem UML e como cada módulo foi projetado para fornecer as funcionalidades

necessárias para a seleção de casos de teste, a geração de oráculos, a seleção de dados de

teste, a geração de artefatos de teste e o empacotamento dos artefatos. Também foram apre-

sentadas as ferramentas Dresden OCL[HDF00] e JTestCase[Wan05] que são utilizadas pela

ferramenta SPACES para auxiliar as funcionalidades dos módulos. Por fim, apresentamos o

Testador de Componentes, que é a aplicação responsável pelaexecução dos testes gerados

pela ferramenta. Vimos como é feita a execução dos testes gerados para todo o compo-

nente, para uma funcionalidade ou para um cenário de uso de uma funcionalidade, e como é

realizada a alteração dinâmica dos dados de teste.

Capítulo 5

Estudo de Caso

Neste capítulo apresentaremos uma aplicação a partir da qual será escolhido um compo-

nente a ser utilizado como estudo de caso para a avaliação tanto do método AFCT quanto da

ferramenta SPACES. O componente escolhido será especificado segundo as diretrizes do mé-

todo e então submetido à ferramenta com o objetivo de, a partir de indícios reais, demonstrar

a aplicabilidade do método, assim como o comportamento esperado da ferramenta.

5.1 Descrição da Aplicação e Escolha do Componente

A aplicação selecionada consiste em um sistema responsávelpelo gerenciamento centra-

lizado das reservas de uma cadeia de hotéis. O sistema permite realizar reservas em qualquer

hotel da cadeia e as reservas podem ser efetuadas por telefone, em uma central de reservas,

via Internet, ou diretamente em cada hotel. Cada reserva é feita a partir das informações

referentes ao hotel, ao tipo de quarto e ao período (compostopor uma data inicial e uma

data final) desejado. Para agilizar o processo, o sistema cadastra os dados dos clientes no

momento em que estes efetuam reserva em algum hotel pela primeira vez. Por fim, o sistema

está integrado com um sistema de contas, previamente desenvolvido, que é responsável por

computar o valor total devido pelo cliente com base no período de estadia e consumo rea-

lizado. A especificação completa do sistema é apresentada em[CD01]. Um dos artefatos

desta especificação é o diagrama de componentes apresentadona Figura 5.1.

Como pode ser visto no diagrama, as funcionalidades do sistema de reserva são imple-

mentadas por três componentes:

77

5.2 Especificando o Componente 78

<< application >>

Sistema de Reservas

IFConta

IFCliente

IFHotel

<< comp spec >>

Sistema de Contas

<< comp spec >>

Gerenciador de Clientes

<< comp spec >>

Gerenciador de Hoteis

Figura 5.1: Diagrama de Componentes do Sistema de Reservas

• O Sistema de Contas, que é responsável por realizar, de forma transparente, a comuni-

cação entre o Sistema de Reserva e o Sistema de Contas de cada hotel;

• O Gerenciador de Clientes, que reune as funcionalidades relacionadas ao cadastra-

mento e à disponibilização dos dados dos clientes para a cadeia de hotéis; e

• O Gerenciador de Hotéis, que é responsável pelo controle das reservas, assim como

dos quartos e tipos de quartos disponíveis em cada hotel.

Como estudo de caso, escolhemos o componenteGerenciador de Hotéisvisto que este

implementa as principais funcionalidades do Sistema de Reserva. Para tornar mais objetiva

a apresentação do processo de aplicação do método, optamos por especificar e testar as três

principais funcionalidades deste componente: a funcionalidadeFazer Reserva, que realiza as

reservas na cadeia de hotéis, a funcionalidadeAlterar Reserva, que permite a alteração das

características de uma reserva previamente efetuada, e a funcionalidadeCancelar Reserva,

que procede a exclusão de uma reserva previamente realizada.

5.2 Especificando o Componente

Para construir os artefatos que constituem a especificação dos componentes, fizemos uso

da versãoCommunity Editionda ferramenta de modelagemPoseidon for UML. De acordo

com o método AFCT, inicialmente, devemos construir o diagrama de funcionalidades do

5.2 Especificando o Componente 79

componente. Esse diagrama pode ser visto na Figura 5.2. Foram especificados três casos de

uso, um para cada funcionalidade a ser testada no componente. Uma vez que a funcionali-

dadeAlterar Reservadepende da execução das outras duas funcionalidades, o casode uso

correspondente inclui os demais. OSistema de Reservas(tipo SistemaDeReserva) é o ator

dos casos de usos pois ele atua como um cliente, interagindo com o componente para fazer

uso de suas funcionalidades.

SistemaDeReserva

Fazer Reserva

Cancelar Reserva

Alterar Reserva

<< include >>

<< include >>

Figura 5.2: Diagrama de Funcionalidades para o ComponenteGerenciador de Hotéis

Após a construção do diagrama de funcionalidades, o próximopasso é a construção

do modelo conceitual do componente, que, juntamente com o anterior, auxilia a atividade

de planejamento do método AFCT. O modelo conceitual do componenteGerenciador de

Hotéisé apresentado na Figura 5.3. O tipoGerenciadorDeHoteisé composto pelos hotéis

pertencentes a cadeia gerenciada. CadaHotel possui uma coleção de tipos de quarto (tipo

TipoQuarto), uma coleção de quartos (tipoQuarto) de um determinado tipo e uma coleção

de reservas (tipoReserva) efetuadas para os diversos tipos de quarto.

O modelo de informação do componenteGerenciador de Hotéis, apresentado na Fi-

gura 5.4, é um refinamento do modelo conceitual. Nele, as principais entidades do compo-

nente, que foram apresentadas no diagrama anterior, são detalhadas com atributos e opera-

ções e surgem também os tipos auxiliaresTReservaeDateque contribuem para a implemen-

tação das funcionalidades do componente. O objetivo deste diagrama é a disponibilização da

mínima informação da qual é necessário ter conhecimento a fimde se utilizar o componente.

5.2 Especificando o Componente 80

GerenciadorDeHoteis Hotelhoteis+

1..*

Quarto

quartos+1..*

Reservareservas+

*

TipoQuarto

tiposQuarto+1..*

*

tipoQuarto+1..* tipo+

Figura 5.3: Modelo Conceitual do ComponenteGerenciador de Hotéis

GerenciadorDeHoteis

+ fazerReserva (reserva :TReserva,codCliente :String ):void

+ getHotel (codigo :String ):Hotel

+ cancelarReserva (codHotel :String ,codReserva :String ):void

+ alterarReserva (reserva :TReserva,codCliente :String ):void

IFHotel

+ fazerReserva (reserva :TReserva,codCliente :String ):void

+ cancelarReserva (codHotel :String ,codReserva :String ):void

+ alterarReserva (reserva :TReserva,codCliente :String ):void

TReserva

- codigo :String

- tipoQuarto :String

- dFim :Date

- dInicio :Date

- hotel :String

+ getCodigo ():String

+ setCodigo (_codigo :String ):void

+ getTipoQuarto ():String

+ setTipoQuarto (_tipoQuarto :String ):void

+ getDInicio ():Date

+ setDInicio (_dInicio :Date ):void

+ getDFim ():Date

+ setDFim (_dFim:Date):void

+ getHotel ():String

+ setHotel (_hotel :String ):void

<< realize >>

SistemaDeReservas

gerenciadorHoteis+

Hotel

- nome :String

- codigo :String

+ fazerReserva (reserva :TReserva,cliente :String ):void

+ getTipoQuarto (codigo :String ):TipoQuarto

+ isPeriodoReservaOK (inicio :Date ,fim :Date ):boolean

+ verificarDisponibilidade (reserva :TReserva):boolean

+ gerarCodReserva ():String

+ cancelarReserva (codigo :String ):void

+ getReserva (codigo :String ):Reserva

+ getCodigo ():String

+ setCodigo (_codigo :String ):void

+ getNome ():String

+ setNome (_nome:String ):void

hoteis+1..*

Reserva

- codigo :String

- dInicio :Date

- dFim :Date

- cliente :String

- cancelada :boolean

+ Reserva(codigo :String ,inicio :Date,fim :Date,tq :TipoQuarto ,cliente :String ):Reserva

+ cancelar ():void

+ getCodigo ():String

+ setCodigo (_codigo :String ):void

+ getDInicio ():Date

+ setDInicio (_dInicio :Date ):void

+ getDFim ():Date

+ setDFim (_dFim:Date):void

+ getCliente ():String

+ setCliente (_cliente :String ):void

+ isCancelada ():boolean

+ setCancelada (_cancelada :boolean ):void

reservas+

*

TipoQuarto

- nome :String

- codigo :String

- preco :double

+ getCodigo ():String

+ setCodigo (_codigo :String ):void

+ getNome ():String

+ setNome (_nome:String ):void

+ getPreco ():double

+ setPreco (_preco :double ):void

tiposQuarto+

1..*

*

tipoQuarto+

Quarto

- numero :String

+ getNumero ():String

+ setNumero (_numero :String ):void

quartos+1..*

1..*

tipo+

Date

- day:int

- month :int

- year:int

+ today ():Date

+ before (d:Date):boolean

+ after (d:Date ):boolean

+ getDay ():int

+ setDay (_day:int ):void

+ getMonth (): int

+ setMonth (_month :int ):void

+ getYear (): int

+ setYear (_year:int ):void

Figura 5.4: Modelo de Informação do ComponenteGerenciador de Hotéis

O próximo passo na especificação do componente é a construçãodos diagramas de ce-

nários de uso. Cada funcionalidade pode apresentar diferentes fluxos de execução que são

exercidos em função das diferentes possibilidades de dadosde entrada permitidos. Cada

forma diferente de executar uma mesma funcionalidade, constitui um cenário de uso da fun-

cionalidade. No caso do componenteGerenciador de Hotéis, os cenários de uso identificados

para as funcionalidades estão listados na Tabela 5.1.

As Figuras 5.5, 5.6, 5.7, 5.8 e 5.9 mostram os diagramas de cenários de uso da funcio-

5.2 Especificando o Componente 81

Funcionalidade Cenários de Uso

Fazer Reserva Hotel Inexistente

Tipo de Quarto Inexistente

Período Inválido

Quarto não Disponível

Sucesso

Cancelar Reserva Hotel Inexistente

Reserva Inválida

Reserva Já Cancelada

Sucesso

Alterar Reserva Hotel Inexistente

Quarto não Disponível

Sucesso

Tabela 5.1: Cenários de Uso das Funcionalidades do ComponenteGerenciador de Hotéis

nalidadeFazer Reserva. O diagrama 5.5 especifica o cenário onde a reserva não pode ser

realizada porque o hotel desejado não pertence à rede de hotéis. O diagrama 5.6 especifica

o cenário onde a reserva não pode ser realizada porque o tipo de quarto desejado não existe

no hotel desejado. O diagrama 5.7 mostra o cenário onde a reserva não pode ser realizada

porque o período não é válido. O diagrama 5.8 especifica a situação em que a reserva não

pode ser realizada porque não existe quarto disponível do tipo desejado no hotel e período

desejados. Por fim, o diagrama 5.9 representa o cenário principal, onde a reserva é realizada

com sucesso.

Os diagramas que especificam os cenários de uso das funcionalidadesCancelar Reserva

eAlterar Reservaforam construídos de forma análoga aos da funcionalidadeFazer Reserva,

sendo, portanto, desnecessária a sua apresentação.

5.2 Especificando o Componente 82

:SistemaDeReservas :GerenciadorDeHoteis

1 : fazerReserva (reserva ,codCliente )

2 : hotelReserva := getHotel (codHotel )

3 : HotelInexistente

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

pre: not hoteis->exists(h:Hotel | h.codigo=reserva.hotel)

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

post: not hoteis->exists(h:Hotel | h.codigo = reserva.hotel)

Figura 5.5: Diagrama que descreve o cenário de usoHotel Inexistenteda funcionalidade

Fazer Reserva

:SistemaDeReservas :GerenciadorDeHoteis

1 : fazerReserva (reserva ,codCliente )

2 : hotelReserva := getHotel (codHotel )

:Hotel

3 : fazerReserva (reserva ,codCliente )

4 : tipoQuarto := getTipoQuarto (codTipo )

5 : TipoQuartoInexistente

6 : TipoQuartoInexistente

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

pre: hoteis->exists

(h:Hotel | h.codigo=reserva.hotel and

not h.tiposQuarto->exists(tq:TipoQuarto | tq.codigo=reserva.tipoQuarto))

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

post: hoteis->exists

(h:Hotel | h.codigo = reserva.hotel and

not h.tiposQuarto->exists(tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

(not h.reservas->exists(r | r.codigo=reserva.codigo and

r.dFim=reserva.dFim and

r.dInicio=reserva.dInicio and

r.tipoQuarto.codigo=reserva.tipoQuarto and

r.cliente=codCliente and

r.cancelada = false)))

Figura 5.6: Diagrama que descreve o cenário de usoTipo de Quarto Inexistenteda funcio-

nalidadeFazer Reserva

5.2 Especificando o Componente 83

:SistemaDeReservas :GerenciadorDeHoteis

1 : fazerReserva (reserva ,codCliente )

2 : hotelReserva := getHotel (codHotel )

:Hotel

3 : fazerReserva (reserva ,codCliente )

4 : tipoQuarto := getTipoQuarto (codTipo )

5 : ?:= isPeriodoReservaOK (dInicio ,dFim )

6 : PeriodoInvalido

7 : PeriodoInvalido

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

pre: hoteis->exists ( h:Hotel | h.codigo=reserva.hotel and

( h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

reserva.dInicio.after(reserva.dFim) ) )

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

post: hoteis->exists

(h:Hotel | h.codigo = reserva.hotel and

h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

(not h.reservas->exists(r | r.codigo=reserva.codigo and

r.dFim=reserva.dFim and

r.dInicio=reserva.dInicio and

r.tipoQuarto.codigo=reserva.tipoQuarto and

r.cliente=codCliente and

r.cancelada = false)))

Figura 5.7: Diagrama que descreve o cenário de usoPeríodo Inválidoda funcionalidade

Fazer Reserva

5.2 Especificando o Componente 84

:SistemaDeReservas :GerenciadorDeHoteis

1 : fazerReserva (reserva ,codCliente )

2 : hotelReserva := getHotel (codHotel )

:Hotel

3 : fazerReserva (reserva ,codCliente )

4 : tipoQuarto := getTipoQuarto (codTipo )

5 : ?:= isPeriodoReservaOK (dInicio ,dFim )

6 : ?:= verificarDisponibilidade (reserva )

7 : QuartoNaoDisponivel

8 : QuartoNaoDisponivel

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

pre: hoteis->exists ( h:Hotel | h.codigo=reserva.hotel and

( h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

reserva.dInicio.before(reserva.dFim) and

( h.reservas->select( cancelada=false and tipoQuarto.codigo=reserva.tipoQuarto and

( ((dInicio.before(reserva.dInicio) or dInicio = reserva.dInicio) and reserva.dInicio.before(dFim)) or

(dInicio.before(reserva.dFim) and (reserva.dFim.before(dFim) or reserva.dFim = dFim)) or

( (reserva.dInicio.before(dInicio) or reserva.dInicio = dInicio) and

(reserva.dFim.after(dFim) or reserva.dFim = dFim) ) )

)->size = h.quartos->select(tipo.codigo=reserva.tipoQuarto)->size ) ) )

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

post: hoteis->exists

(h:Hotel | h.codigo = reserva.hotel and

h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

(not h.reservas->exists(r | r.codigo=reserva.codigo and

r.dFim=reserva.dFim and

r.dInicio=reserva.dInicio and

r.tipoQuarto.codigo=reserva.tipoQuarto and

r.cliente=codCliente and

r.cancelada = false)))

Figura 5.8: Diagrama que descreve o cenário de usoQuarto Não Disponívelda funcionali-

dadeFazer Reserva

5.2 Especificando o Componente 85

:SistemaDeReservas :GerenciadorDeHoteis

1 : fazerReserva (reserva ,codCliente )

:Hotel

3 : fazerReserva (reserva ,codCliente )

2 : hotelReserva := getHotel (codHotel )

4 : tipoQuarto := getTipoQuarto (codTipo )

5 : ?:= isPeriodoReservaOK (dInicio ,dFim )

6 : ?:= verificarDisponibilidade (reserva )

7 : codReserva := gerarCodReserva ()

:Reserva

8 : reservaCriada := Reserva(codReserva ,dInicio ,dFim ,tipoQuarto ,cliente )

9 : return

10 : return

11 : return

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

pre: hoteis->exists ( h:Hotel | h.codigo=reserva.hotel and

( h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

reserva.dInicio.before(reserva.dFim) and

( h.reservas->select( cancelada=false and tipoQuarto.codigo=reserva.tipoQuarto and

( ((dInicio.before(reserva.dInicio) or dInicio = reserva.dInicio) and reserva.dInicio.before(dFim)) or

(dInicio.before(reserva.dFim) and (reserva.dFim.before(dFim) or reserva.dFim = dFim)) or

( (reserva.dInicio.before(dInicio) or reserva.dInicio = dInicio) and

(reserva.dFim.after(dFim) or reserva.dFim = dFim) ) )

)->size < h.quartos->select(tipo.codigo=reserva.tipoQuarto)->size ) ) )

<<constraint>>

context GerenciadorDeHoteis::fazerReserva(reserva:TReserva; codCliente:String)

post: hoteis->exists

(h:Hotel | h.codigo = reserva.hotel and

h.tiposQuarto->exists (tq:TipoQuarto | tq.codigo=reserva.tipoQuarto) and

(h.reservas->exists(r | r.codigo=reserva.codigo and

r.dFim=reserva.dFim and

r.dInicio=reserva.dInicio and

r.tipoQuarto.codigo=reserva.tipoQuarto and

r.cliente=codCliente and

r.cancelada = false)))

Figura 5.9: Diagrama que descreve o cenário de usoSucessoda funcionalidadeFazer Re-

serva

5.3 Aplicando o Método AFCT 86

5.3 Aplicando o Método AFCT

Nesta seção descrevemos a aplicação das atividades do método AFCT, através da fer-

ramenta SPACES, para proceder o teste funcional do componente Gerenciador de Hotéisa

partir da especificação anteriormente construída.

5.3.1 Planejando o Teste

Uma vez que optamos por especificar e testar apenas três funcionalidades do compo-

nente, vamos concentrar o planejamento de teste sobre o número de casos de teste a ser

selecionado para cada funcionalidade. Como vimos na definição do método AFCT, cada

cenário de uso é um caso de teste em potencial. Dessa forma, temos um total de 12 casos de

teste passíveis de realização e, assim sendo, não haveria problema algum em testar o com-

ponente de forma exaustiva. Porém, vamos optar por realizarapenas 10 casos de teste para

exemplificar a atividade de seleção do método AFCT.

Para determinar como os 10 casos de teste seriam distribuídos entre as funcionalidades,

analisamos estas de acordo com a sua Frequência de Uso e a Criticalidade da ocorrência de

uma falha. Os resultados desta análise são apresentados na Tabela 5.2.

Funcionalidade Freqüência de Uso Criticalidade No de Casos de Teste

Fazer Reserva Alta Alta 4

Alterar Reserva Baixa Alta 3

Cancelar Reserva Baixa Baixa 3

Tabela 5.2: Determinando o número de casos de teste a ser realizados para cada funcionali-

dade

Além disso, decidimos que, para cada funcionalidade, seriam priorizados o cenário de

sucesso e os cenários alternativos mais complexos. Uma vez concluída a etapa de planeja-

mento dos testes, passamos para a especificação dos testes.

5.3 Aplicando o Método AFCT 87

5.3.2 Especificando os Testes

A partir desta etapa, faremos uso da ferramenta SPACES para executar automaticamente

as atividades do método AFCT. Para tanto, precisamos exportar a especificação construída

para o componente na forma de um arquivo XMI e acessar o arquivo a partir da ferramenta

SPACES (menuArquivo/Abrir Arquivo XMI). No Poseidon, o arquivo XMI é obtido através

do menuFile/Export Project to XMI. A seguir, vemos como as atividades que constituem os

artefatos da especificação dos testes do componenteGerenciador de Hotéissão realizadas

pela ferramenta.

Selecionando os Casos de Teste

Ao iniciarmos a funcionalidade de seleção de casos de teste da ferramenta SPACES

(menuProjeto/Selecionar Casos de Teste), é exibida a tela apresentada na Figura 5.10. Nesta

tela indicamos qual o número de casos de teste que devem ser selecionados para cada fun-

cionalidade, de acordo com as decisões tomadas na etapa de planejamento.

Figura 5.10: Selecionando os casos de teste com SPACES

Ao clicar no botãoSelecionar, serão apresentados os modelos de uso gerados para as

funcionalidadesFazer Reservae Cancelar Reserva. No caso da funcionalidadeAlterar Re-

serva, um vez que o teste é realizado de forma exaustiva (coluna GT marcada), o modelo

de uso não é exibido pois os casos de teste serão gerados sem necessidade de seleção. Nos

modelos de uso apresentados, os cenários selecionados estarão em destaque. Caso a seleção

5.3 Aplicando o Método AFCT 88

não tenha priorizado os cenários principais e alternativosmais complexos, como decidido

no planejamento dos testes a serem realizados para cada funcionalidade, as probabilidades

do modelo deverão ser alteradas para refletir as decisões do planejamento e a seleção de-

verá ser refeita até obtermos um conjunto de testes satisfatório. Ao final, os modelos de uso

obtidos para as funcionalidadesFazer Reservae Cancelar Reservasão os apresentados nas

Figuras 5.11 e 5.12.

Figura 5.11: Modelo de Uso para a Funcionalidade Fazer Reserva

5.3 Aplicando o Método AFCT 89

Figura 5.12: Modelo de Uso para a Funcionalidade Cancelar Reserva

Gerando os Oráculos

Após a seleção dos casos de teste, a próxima etapa no método AFCT é a geração dos orá-

culos. Para tanto, acionamos o menuProjeto/Gerar Oráculosda ferramenta SPACES. Feito

isso, são gerados métodos Java correspondentes às restrições OCL de pré e pós-condição

especificadas para cada cenário de uso de cada funcionalidade. Caso invariantes tenham sido

especificadas para a classe responsável pela disponibilização das funcionalidades do com-

ponente (classefacade), estas também serão convertidas em métodos Java. No componente

Gerenciador de Hotéis não foram especificadas invariantes,sendo gerados métodos apenas

para as pré e pós-condições dos cenários. Os métodos Java gerados a partir das restrições

OCL do cenário de usoPeríodo Inválidoda funcionalidadeFazer Reservasão apresentados

nas Figuras 5.13 e 5.14.

5.3 Aplicando o Método AFCT 90

private boolean check_PeriodoInvalido_pre(GerenciadorDeHoteis self, TReserva reserva,String codCliente){

boolean value_1 = false;

}

java.util.Iterator it1 = self.getHoteis().iterator();Hotel h;while (it1.hasNext()){

h = (Hotel) it1.next();boolean value_2 = false;java.util.Iterator it2 = h.getTiposQuarto().iterator();TipoQuarto tq;while (it2.hasNext()){

tq = (TipoQuarto) it2.next();boolean value_3 = tq.getCodigo().equals(reserva.getTipoQuarto());if (value_3){

value_2 = true;break;

}}Date value_4 = reserva.getDFim();boolean value_5 = value_2 && reserva.getDInicio().after(value_4);boolean value_6 = h.getCodigo().equals(reserva.getHotel()) && value_5;if (value_6){

value_1 = true;break;

}}boolean value_7 = value_1;return value_7;

Figura 5.13: Oráculo correspondente à pré-condição do cenário Período Inválidoda Funcio-

nalidadeFazer Reserva

Selecionando os Dados de Teste

Com a funcionalidade de seleção de dados de teste, acionada pelo menu Pro-

jeto/Selecionar Dados de Testeda ferramenta SPACES, procedemos a derivação dos valores

de entrada necessários para a execução das funcionalidadesdo componenteGerenciador

de Hotéisnos diversos cenários de uso especificados. A partir das partições de equivalência

identificadas nas restrições de pré-condição, foram gerados, para cada cenário de uso, valores

aleatórios para os dados de entrada da funcionalidade, assim como valores necessários para

preparar o estado do componente para a execução da funcionalidade no cenário em questão.

Exemplos de dados de teste selecionados para o cenário de usoTipo de Quarto Inexistente

da funcionalidadeFazer Reservasão apresentados na Figura 5.15. Como vimos, durante a

apresentação da ferramenta SPACES, no Capítulo 4, apesar daatividade de seleção de dados

de teste ser realizada de forma automática, algumas vezes o conjunto de dados selecionados

para um determinado cenário pode ser inconsistente com a suapré-condição em função da

não avaliação de alguns trechos da restrição, o que torna necessária uma revisão dos dados

5.3 Aplicando o Método AFCT 91

private boolean check_PeriodoInvalido_post(GerenciadorDeHoteis self, TReserva reserva,String codCliente){

boolean value_1 = false;

value_6;

}

java.util.Iterator it1 = self.getHoteis().iterator();Hotel h;while (it1.hasNext()){

h = (Hotel) it1.next();boolean value_2 = false;java.util.Iterator it2 = h.getTiposQuarto().iterator();TipoQuarto tq;while (it2.hasNext()){

tq = (TipoQuarto) it2.next();boolean value_3 = tq.getCodigo().equals(reserva.getTipoQuarto());if (value_3){

value_2 = true;break;

}}boolean value_4 = false;java.util.Iterator it4 = h.getReservas().iterator();Reserva r;while (it4.hasNext()){

r = (Reserva) it4.next();boolean value_5 = r.getCodigo().equals(reserva.getCodigo()) &&

r.getDFim().equals(reserva.getDFim()) &&r.getDInicio().equals(reserva.getDInicio()) &&.getTipoQuarto().getCodigo().equals(reserva.getTipoQuarto()) &&

r.getCliente().equals(codCliente) && r.isCancelada() == false;f (value_5){

value_4 = true;break;

}}boolean value_6 = ! value_4;boolean value_7 = h.getCodigo().equals(reserva.getHotel()) && value_2 &&if (value_7){

value_1 = true;break;

}}boolean value_8 = value_1;return value_8;

r

i

Figura 5.14: Oráculo correspondente à pós-condição do cenário Período Inválidoda Funcio-

nalidadeFazer Reserva

de teste gerados para aferir o resultado correto dos testes.Como veremos adiante, durante a

execução dos testes, isso foi necessário para alguns cenários do componenteGerenciador de

Hotéis.

5.3.3 Construindo e Empacotando os Artefatos de Teste

Uma vez construída a especificação dos testes (casos de teste, oráculos e dados de teste)

partimos para a sua implementação. Ao acionar o menuProjeto/Gerar Artefatos de Testeda

ferramenta SPACES, será gerada a hierarquia de classes de teste apresentada na Figura 5.16.

5.3 Aplicando o Método AFCT 92

codCliente

codigo

eserva hoteltipoQuarto

reserva.dInicio monthyear

reserva.dFim monthyear

tq

codigo

= "<;X2HF"

= ["fRD9T)=^w/ZXSGXL>Z0%^iN?6+y6`{AM","6Ri<BUOEfIv1Jyy8itu:9pFE4",

"d;1-knsK,q#YT`fy)uO^xy#f_>8RM{m9P-W{@SX{OSqlRC{LiL-TC",

r = "BiYMc{rt>cIG+?]>#IA5W.RXTPB4r28n+4XQSPMl^]?zPS1"= "%#98b5@6"

= 28530= 7710

= 9193= 28766

1.1303589441531831E38,2.230561619400798E38]

= ["","(s@(uA;,%",“)"]

"7vD-xtqz_ODX)Qu@%,,t","}?dHcY#+65m}yR(uv9Ba_HwRUMQ#7dwbo?1pM{","BiYMc{rt>cIG+?]>#IA5W.RXTPB4r28n+4XQSPMl^]?zPS1"]

= ["?Yfc`lQ.zA]}7l,:1Q6`*3G",

"LHk(S^@0?*=n`3pqBNHh80;wb+V%c'zbZ","<52VDLiDw4Ebf","dr2'15|rQ>5"]

nome

= "{?e|uLXz<N[AU#XGR;-GQxYls?<oQ"

= 4266

= 31092

= ["'kSXNa[(A:9YTVhTDZ_}vF5_V[@c`:bvR_O.=19.5JcNa%l&wmJy","I&`spVd?7YN:Lu0Nf-=F^[WP#*ryUTbd@{1L6mXacK5","dw:2jhnYW9AQ[cG#{y"]

= [4.927761449765372E37,

h

codigo

day

day

nome

preco

{{

{{

{Figura 5.15: Dados selecionados para o cenárioTipo de Quarto Inexistenteda funcionalidade

Fazer Reserva

A classeGerenciadorDeHoteisTestestende a classeTestCasedo framework JUnit e é res-

ponsável por disponibilizar, via herança, a verificação dasinvariantes da classeGerencia-

dorDeHoteispara as classes de teste de cada funcionalidade. Estas classes de teste pos-

suem, além dos métodos de teste de cada cenário, os métodos para verificação das pré e

pós-condições dos cenários, que, juntamente com o método deverificação de invariantes

herdado, constituem os oráculos de teste gerados durante a especificação dos testes.

Além das classes de teste, também são gerados arquivos XML contendo os dados de teste

selecionados na etapa de especificação dos testes. Esses arquivos são acessados pelas classes

de teste a partir da API disponibilizada pela ferramenta JTestCase.

Por fim, o código de teste compilado e os arquivos de dados são empacotados na forma de

um arquivo JAR que será utilizado para testar a implementaçao do componente desenvolvido.

5.3 Aplicando o Método AFCT 93

junit.framework.TestCaseGerenciadorDeHoteisTest

+ check_invariants (self :GerenciadorDeHoteis ):boolean

FazerReservaTest

- check_TipoQuartoInexistente_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_TipoQuartoInexistente_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testTipoQuartoInexistente ():void

- check_PeriodoInvalido_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_PeriodoInvalido_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testPeriodoInvalido ():void

- check_QuartoNaoDisponivel_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_QuartoNaoDisponivel_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testQuartoNaoDisponivel ():void

- check_Sucesso_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_Sucesso_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testSucesso ():void

AlterarReservaTest

- check_HotelInexistente_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_HotelInexistente_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testHotelInexistente ():void

- check_QuartoNaoDisponivel_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_QuartoNaoDisponivel_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testQuartoNaoDisponivel ():void

- check_Sucesso_pre (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

- check_Sucesso_post (self :GerenciadorDeHoteis ,reserva :TReserva,codCliente :String ):boolean

+ testSucesso ():void

CancelarReservaTest

- check_ReservaInvalida_pre (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

- check_ReservaInvalida_post (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

+ testReservaInvalida ():void

- check_ReservaJaCancelada_pre (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

- check_ReservaJaCancelada_post (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

+ testReservaJaCancelada ():void

- check_Sucesso_pre (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

- check_Sucesso_post (self :GerenciadorDeHoteis ,codHotel :String ,codReserva :String ):boolean

+ testSucesso ():void

Figura 5.16: Hierarquia de Classes de Teste geradas para o ComponenteGerenciador de

Hoteis

5.3.4 Executando o Teste

A execução dos testes gerados pela ferramenta SPACES é feitapelo Testador de Com-

ponentes, uma aplicação baseada no framework JUnit. Para tanto, é preciso que o pacote

contendo os artefatos de teste gerados esteja presente noCLASSPATHda aplicação. Na Fi-

gura 5.17, vemos a tela de execução dos testes gerados para todas as funcionalidades do

componenteGerenciador de Hotéis. Nesta execução, testamos uma implementação da es-

pecificação do componente na qual a funcionalidadeCancelar Reservafoi incorretamente

codificada. A implementação da funcionalidade deveria lançar uma exceção correspondente

à mensagem de saída do cenárioReserva Já Canceladae, no cenárioSucesso, deveria alterar

5.3 Aplicando o Método AFCT 94

o estado do objetoReservacorrespondente aos dados de entrada, indicando o seu cancela-

mento. Ambos os comportamentos foram omitidos para que pudéssemos observar esses

defeitos durante a execução dos testes. O testador executoutodos os 10 casos de teste e

identificou com umX na cor preta, os cenários falhos da funcionalidadeCancelar Reserva.

Além disso, três testes tiveram resultado indefinido em função de inconsistências no con-

junto de dados selecionados para os respectivos cenários (Quarto Nao Disponivele Sucesso

da funcionalidadeAlterar Reservae Sucessoda funcionalidadeFazer Reserva). Esses cená-

rios foram marcados com umX na cor amarela. Os demais cenários foram marcados com o

símbolo√

na cor verde, indicando que foram executados com sucesso. Como houve pelo

menos uma falha na execução dos testes, a barra de progresso assumiu a cor vermelha ao

final da execução.

Figura 5.17: Execução dos Testes: Detecção de Falhas

5.3 Aplicando o Método AFCT 95

Após a correção dos defeitos de implementação da funcionalidadeCancelar Reserva, os

testes foram novamente executados e os resultados obtidos podem ser vistos na Figura 5.19.

Desta vez, os testes dos cenários que falharam na execução anterior foram bem sucedidos.

No entanto, os cenários com dados inconsistentes ainda permaneceram com o resultado dos

testes indefinido. Em função disso, a barra de progresso assumiu a cor amarela. Neste

momento, acessamos os dados que foram selecionados para cada cenário, através do botão

Test Datado testador e, na tela de edição de dados que é exibida, confrontamos os dados

selecionados com a restrição de pré-condição OCL do respectivo cenário com o objetivo de

detectar e alterar os dados inválidos.

Figura 5.18: Execução dos Testes: Revisando os Dados de Teste

Por fim, após a alteração dos dados inválidos, os testes foramexecutados uma terceira

vez e, como pode ser visto na Figura 5.19, a barra de progressoassumiu a cor verde em sinal

5.4 Considerações Finais 96

da ausência de falhas.

Figura 5.19: Execução dos Testes: Testes Ok

5.4 Considerações Finais

Neste capítulo, apresentamos a aplicação do método AFCT e daferramenta SPACES na

especificação e teste de um componente de um sistema de reservas de hoteis. A especificação

do componente foi construída passo a passo, de acordo com as diretrizes do método AFCT.

Foi feito um levantamento das funcionalidades e cenários deuso do componente a partir dos

quais foi realizada a etapa de planejamento do método. Em seguida, já com o auxílio da

ferramenta SPACES, foi construída a especificação dos testes a partir da seleção de casos

de teste, da geração de oráculos e da seleção de dados de teste. Após a especificação dos

5.4 Considerações Finais 97

testes, as informações geradas foram utilizadas pela ferramenta para realizar a construção dos

artefatos de teste. Neste momento foram geradas as classes de teste e os arquivos XML de

dados, necessários à execução dos testes do componente. Estes artefatos foram empacotados

na forma de um arquivo JAR para possibilitar a execução dos testes por parte da aplicação

Testador de Componentes. Durante a execução, utilizamos inicialmente uma implementação

na qual uma das funcionalidades do componente havia sido incorretamente codificada em

relação à sua especificação. Ao executar os testes, o testador de componentes identificou as

falhas existentes nesta funcionalidade. Além das falhas, também foram obtidos resultados

indefinidos para alguns testes em função de uma limitação da atividade de seleção de dados

de teste da ferramenta SPACES. Após a correção das falhas de implementação e a alteração

dos dados incorretos, os testes foram novamente executadose todos obtiveram êxito.

Embora tenha sido realizada em um estudo de caso simples, a aplicação do método AFCT

provou ser, de fato, bastante fácil uma vez que os artefatos que foram desenvolvidos são

semelhantes aos que já são normalmente produzidos pelos processos de desenvolvimento.

Além disso, a utilização da ferramenta SPACES para a realização automática de tarefas

mais complexas e/ou sistemáticas do método fez com que estasatividades pudessem ser

realizadas rapidamente e, em que pese o fato de em alguns momentos precisarmos realizar

manualmente parte da atividade de seleção dos dados de teste, em função da limitação exis-

tente na ferramenta, ainda assim o processo de teste pode seraplicado de maneira bastante

prática mesmo em sistemas mais complexos, evitando a necessidade de alocação de grande

parte dos recursos organizacionais. Por fim, vimos que o conjunto de testes gerado pela

ferramenta SPACES aliado ao Testador de Componentes facilita o trabalho do fornecedor

do componente no que diz respeiro à execução e análise de resultado dos testes. Com a

distribuição de ambos para os clientes, os testes produzidos e a infra-estrutura de execução

podem auxiliar a verificação dos componentes do ponto de vista de integração, no momento

da composição das aplicações.

A aplicação do método AFCT nos permite ainda avaliar o quantoda especificação do

componente foi coberta. Caso optássemos pela seleção de todos os casos de teste, obte-

ríamos uma cobertura de 100% da especificação, o que nem sempre é possível em função

dos recursos disponíveis para a realização dos testes. No entanto, uma vez que foram se-

lecionados os cenários de uso mais críticos das funcionalidades, a cobertura obtida pode

5.4 Considerações Finais 98

ser considerada satisfatória e nos dá uma certa margem de segurança a cerca da confiabili-

dade do componente. É importante salientar que a cobertura da especificação é uma métrica

mais adequada para o teste funcional. A cobertura de código poderia ser medida durante a

execução dos testes, a partir do uso de ferramentas extras, aexemplo da ferramenta Clover1.

1http://www.cenqua.com/clover

Capítulo 6

Conclusão

Apresentamos neste trabalho a proposta de um método de testefuncional automático para

verificação de componentes de software a partir de especificações UML e restrições OCL.

A idéia de seu desenvolvimento surgiu a partir da necessidade de realizar adaptações em

um método semelhante que foi desenvolvido anteriormente dentro do Grupo de Pesquisa em

Teste de Software da UFCG, o método FCT (Functional Component Testing). As adaptações

precisavam ser realizadas para possibilitar o desenvolvimento de uma ferramenta de suporte

que automatizasse suas atividades. No entanto, essas adaptações levaram à redefinição de

boa parte das atividades do método FCT, o que acabou resultando na proposta de um novo

método de teste fortemente baseado no anterior, o método AFCT (Automatic Functional

Component Testing). Aliado a elaboração do método AFCT, o trabalho também contemplou

o desenvolvimento de uma ferramenta de suporte para a realização de suas atividades de

forma automática, a ferramenta SPACES (SPecification bAsed Component tESter).

Como mostrado, o método AFCT utiliza artefatos UML produzidos por processos de de-

senvolvimento durante as fases de análise e projeto. Estes artefatos, que são criados segundo

um formato apresentado neste trabalho, são utilizados à medida que vão sendo desenvolvi-

dos, possibilitando, assim, a aplicação do processo de teste de forma paralela ao processo

de desenvolvimento. A partir destes artefatos, é feito o planejamento de teste, quando são

tomadas decisões relacionadas a realização das atividadesde teste frente aos recursos dispo-

níveis. Uma vez definidos os recursos disponíveis para as atividade de teste, é realizada a

especificação dos testes. Essa fase consiste na geração de modelos de teste a partir dos quais

são derivados os casos de teste, oráculos e dados, que constituem os pilares para a realização

99

6.1 Contribuições 100

do teste funcional do componente. Essas informações são então utilizadas para a construção

do código de teste que será responsável pela aferição das funcionalidades do componente.

Por fim, o código de teste é executado e, com base nas informações produzidas, é feita a ava-

liação de conformidade entre a implementação e a especificação do componente. Cada uma

destas atividades foi sistematicamente especificada a partir de algoritmos ou da aplicação

de técnicas de eficiências reconhecidamente comprovadas. Aferramenta SPACES, que foi

desenvolvida a partir de características pré-determinadas e cujo projeto é descrito em um dos

capítulos deste documento, realiza a automação de todas as etapas do método, a exceção do

planejamento de teste, em função do alto grau de subjetividade presente nesta atividade. No

entanto, em função da complexidade relacionada à seleção dedados, a implementação atual

fornece um suporte parcial a essa atividade, tornando necessária uma revisão do conjunto

de dados selecionados a fim de garantir que os testes são executados dentro das condições

previstas.

Para avaliarmos a aplicabilidade do método proposto em um sistema real e a funciona-

lidade da ferramenta de suporte, um estudo de caso foi realizado e seus resultados foram

apresentados neste documento. No estudo de caso, um componente foi especificado e tes-

tado de forma automática, segundo as diretrizes do método AFCT, a partir da ferramenta

SPACES. Os resultados observados trouxeram indícios reaisda facilidade de aplicação do

método e da confiabilidade da ferramenta.

6.1 Contribuições

Antes da realização deste trabalho, haviam poucos métodos de teste funcional aplicados

a componentes de software e que dispusessem de ferramentas de suporte para automatizar as

suas atividades. O que ocorria de fato era que os trabalhos originalmente desenvolvidos para

softwares OO eram utilizados, de forma adaptada, para o teste de componentes de software.

Com isso essas técnicas não eram capazes de cobrir características específicas dos compo-

nentes, a exemplo do teste de propriedades. Outro problema era a distância normalmente

existente entre a definição destas técnicas e a sua real utilização pela indústria. Isso era de-

vido, principalmente, ao desenvolvimento de especificações exclusivamente para propósitos

de teste e em linguagens ou formalismos de difícil assimilação ou de uso extremamente res-

6.2 Trabalhos Futuros 101

trito. No nosso trabalho, definimos um método de teste funcional aplicado à componentes

de software, cujas atividades são quase que inteiramente automatizadas por uma ferramenta

de suporte. O método utiliza especificações UML, uma linguagem largamente utilizada na

indústria, e de artefatos normalmente desenvolvidos nas etapas de análise e projeto dos pro-

cessos de desenvolvimento. Além disso, como os artefatos são utilizados pelo método à

medida em que vão sendo produzidos, os defeitos podem ser descobertos cada vez mais

cedo, reduzindo assim, o custo de correção.

Durante o desenvolvimento da ferramenta SPACES, foram empregadas aplicações de

software livre para auxiliar a implementação de algumas funcionalidades. Com isso, tornou-

se necessário o estabelecimento de contatos com a comunidade desenvolvedora dessas apli-

cações para que pudéssemos adaptá-las as nossas necessidades. Dessa interação surgiram

propostas de novas funcionalidades e a decoberta de defeitos, o que acabou contribuindo

para o aperfeiçoamento das próprias aplicações[ALB+05]. Além disso, é de nosso interesse

que, ao final do seu desenvolvimento, a ferramenta SPACES seja disponibilizada sob alguma

licença livre, possibilitando, assim, a sua expansão para diferentes contextos de execução e

contribuindo para a difusão da prática de teste automático e, de forma mais ampla, para a

melhoria dos produtos de software desenvolvidos na comunidadeopen source.

Por fim, como contribuição indireta, as idéias e algoritmos propostos neste trabalho po-

dem servir de base para a automação de métodos semelhantes, contribuindo para um melhor

entendimento do problema de geração e execução automática de casos de teste.

6.2 Trabalhos Futuros

Com a finalização deste trabalho, tivemos a possibilidade defazer uma análise do mesmo,

a qual resultou no seguinte conjunto de propostas para a sua continuidade:

• Complementar o método em termos de sua abrangência: Assim como ocorre com

o método FCT, o método AFCT limita-se a verificar os componentes individualmente,

entretanto um método completo deveria considerar também a possibilidade de realiza-

ção de testes de integração e sistema. No Grupo de Pesquisa emTeste de Software da

UFCG já foi desenvolvido um trabalho acerca de testes de integração de componentes

[GMF04], assim, como trabalho futuro, propomos a incorporação das idéias apresen-

6.2 Trabalhos Futuros 102

tadas neste trabalho para compor um método de teste automático mais abrangente, que

contemple o teste dos componentes tanto na perspectiva do fornecedor quanto na do

cliente.

• Definir a licença de uso e distribuição da ferramenta: Como é nosso objetivo dis-

ponibilizar a ferramenta como um software livre, precisamos definir a sua política de

utilização e distribuição de forma compatível com as licenças dos softwares livres em-

pregados na implementação de suas funcionalidades. Além disso, precisamos definir

qual será a contribuição da comunidadeopen sourceno desenvolvimento das próximas

versões da ferramenta.

• Promover a evolução da ferramenta: Embora já tenhamos uma primeira versão

da ferramenta, algumas de suas funcionalidades ainda precisam ser aperfeiçoadas, a

exemplo da seleção de dados, que fornece um suporte parcial àatividade do método

AFCT, realizando-o de forma semi-automática. Nesse sentido, um ponto a ser estu-

dado pode ser a adoção de bancos de dados de teste para domínios de componentes,

de forma a maximizar o aproveitamento dos dados selecionados nesta etapa e diminuir

a complexividade desta atividade. Além disso, uma vez que propomos, como trabalho

futuro, o desenvolvimento de um método mais abrangente, é necessário que a fer-

ramenta SPACES também seja evoluída para incorporar funcionalidades referentes ao

teste de integração de componentes. Nesse sentido, os pacotes de teste atualmente pro-

duzidos para os componentes poderiam servir de base para o desenvolvimento de uma

infra-estrutura mais ampla de teste de integração. Outro ponto em que a ferramenta

deve evoluir diz respeito à incorporação das funcionalidades de execução e análise de

resultados dos testes, atualmente implementadas pelo Testador de Componentes. Isso

não foi possível de ser realizado neste trabalho porque era preciso realizar um estudo

mais aprofundado sobre técnicas de alteração dinâmica da variável CLASSPATHda

linguagem Java. Como, no início da execução, a ferramenta que gera o código de

teste não conhece as classes de teste que vão ser geradas durante a execução, é preciso

alterar dinamicamente a variávelCLASSPATHpara adicionar as classes geradas e pos-

sibilitar a sua instanciação e execução. Assim, a tendênciaé que a ferramenta SPACES

evolua para duas versões: uma versão voltada para os fornecedores, que seria capaz

6.2 Trabalhos Futuros 103

de gerar, executar e analisar o resultado de testes para os componentes individuais, e

uma versão voltada para os clientes, que forneça, a partir dos testes produzidos pela

primeira versão, a infra-estrutura necessária à geração, execução e análise de resultado

de testes de integração de componentes.

• Submeter o trabalho a outros estudos de caso: Apesar de desenvolvermos um estudo

de caso que nos forneceu uma visão prática da aplicação do método AFCT e do uso

da ferramenta SPACES, para adquirirmos uma maior confiança acerca da viabilidade

prática do método, precisamos aplicá-lo a um maior número deestudos de caso e

realizar um estudo experimental que possa exercitar cada uma de suas características

em diferentes contextos.

Bibliografia

[ALB+05] Wilkerson L. Andrade, Helton S. Lima, Daniel L. Barbosa, Patrícia D. L. Ma-

chado, and Jorge C. A. Figueiredo. Experiência no Uso de Ferramentas Livres

para o Teste Funcional de Componentes de Software. InVI Workshop Software

Livre (WSL), Porto Alegre, 2005.

[BAMF04a] Daniel L. Barbosa, Wilkerson L. Andrade, Patrícia D. L. Machado, and Jorge

C. A. Figueiredo. SPACES – Uma Ferramenta para Teste Funcional de Com-

ponentes. InAnais da Sessão de Ferramentas do XVIII Simpósio Brasileirode

Engenharia de Software (SBES), Brasília, 2004. Prêmio de Melhor Ferramenta

do Evento.

[BAMF04b] Daniel L. Barbosa, Wilkerson L. Andrade, Patrícia D. L. Machado, and Jorge

C. A. Figueiredo. Um Método Automático para Verificação Funcional de

Componentes. InIV Workshop de Desenvolvimento Baseado em Componentes

(WDBC), pages 23–28, João Pessoa, 2004.

[Bei90] Boris Beizer. Software Testing Techniques. Van Nostrand Reinhold, 2nd edi-

tion. 1990.

[Bei95] Boris Beizer.Black-Box Testing: Techniques for Functional Testing of Software

and Systems. John Wiley & Sons, 1995.

[Bin99] Robert V. Binder.Testing Object-Oriented Systems: Models, Patterns and To-

ols. Addison-Wesley, 1999.

[BL01] Lionel Briand and Yvan Labiche. A UML-Based Approach to System Testing.

In Proceedings of UML 2001 - The Unified Modeling Language. Modeling

104

BIBLIOGRAFIA 105

Languages, Concepts, and Tools, volume 2185 ofLecture Notes in Computer

Science, pages 194–208, 2001.

[CCD+02] A. Cavarra, C. Crichton, J. Davies, A. Hartman, T. Jeron, andL. Mounier.

Using UML for Automatic Test Generation. In Joost-Pieter Katoen and Per-

dita Stevens, editors,Proceedings of the 8th International Conference on Tools

and Algorithms for the Construction and Analysis of Systems(TACAS’2002),

volume 2280 ofLecture Notes in Computer Science, Grenoble, France, April

2002. Springer.

[CD01] John Cheesman and John Daniels.UML Components: A Simple Process for

Specifying Component-Based Software. Addison-Wesley, 2001.

[CM94] J. J. Chilenski and S. P. Miller. Applicability of modified condition/decision

coverage to software testing.Software Engineering Journal, 7(5):193–200,

1994.

[CR99] Juei Chang and Debra J. Richardson. Structural specification-based testing:

Automated support and experimental evaluation. In Oscar Nierstrasz and Mi-

chel Lemoine, editors,ESEC/FSE ’99, volume 1687 ofLecture Notes in Com-

puter Science, pages 285–302. Springer-Verlag / ACM Press, 1999.

[CWO03] MH. Chen, Y. Wu, and J. Offutt. UML-Based Integration Testing for

Component-Based Software. InProceedings of the 2nd International Con-

ference on COTS-Based Software Systems (ICCBSS), February2003, 2003.

[dF03] Carina M. de Farias. Um Método de Teste Funcional para Verificação de Com-

ponentes.Dissertação de Mestrado - COPIN, UFCG, 2003.

[FJJV97] Jean-Claude Fernandez, Claude Jard, Thierry Jeron, and Cesar Viho. An Expe-

riment in Automatic Generation of Test Suites for Protocolswith Verification

Technology.Science of Computer Programming, 29(1-2):123–146, 1997.

[FL00] P. Fröhlich and J. Link. Automated Test Case Generation fromDynamic Mo-

dels. InProceedings of ECOOP 2000, LNCS 1850, pages 472–491. Springer,

2000.

BIBLIOGRAFIA 106

[GB05] Erich Gamma and Kent Beck. JUnit Testing Framework. Disponível em

http://www.junit.org, Março, 2005.

[GHJV95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design

Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley,

Reading, MA, 1995.

[GMF04] Cidinha Costa Gouveia, Patrícia D. L. Machado, and Jorge C. A. Figueiredo.

Uma Estratégia de Teste de Integração de Componentes. InIV Workshop de

Desenvolvimento Baseado em Componentes (WDBC), pages 63–68, João Pes-

soa, 2004.

[Gra94] J. Grabowski. SDL and MSC Based Test Case Generation: An Overall View

of the SaMsTaG Method, 1994.

[Har00] Mary J. Harrold. Testing: A Roadmap. InProceedings of the 22th International

Conference on Software Engineering (ICSE-00), pages 61–72, NY, June 4–11

2000. ACM Press.

[HDF00] Heinrich Hussmann, Birgit Demuth, and Frank Finger. Modular Architecture

for a Toolset Supporting OCL. InProceedings of UML 2000 - The Unified

Modeling Language. Advancing the Standard. Third International Conference,

pages 278–293, York, UK, 2000.

[Idr05] Nazmul Idris. Should I use SAX or DOM? Disponível em

http://developerlife.com/saxvsdom/default.htm, Janeiro, 2005.

[Lar98] Craig Larman. Applying UML and Patterns: an Introduction to Object-

Oriented Analysis and Design. Prentice Hall PTR, Upper Saddle River/NJ,

1998.

[Mac00] Patrícia D. L. Machado. Testing from structured algebraic specifications. In

AMAST: 8th International Conference on Algebraic Methodology and Software

Technology, volume 1816 ofLecture Notes in Computer Science. Springer-

Verlag / ACM Press, 2000.

BIBLIOGRAFIA 107

[Meg05] David Megginson. SAX 2.0 - The Simple API for XML. Disponívelem

http://www.saxproject.org/, Janeiro, 2005.

[MS01] J. D. McGregor and D. A. Sykes.A Practical Guide to Testing Object-Oriented

Software. Addison-Wesley, 2001.

[MTY01] E. Martins, C. Toyota, and R. Yanagawa. Constructing Self-Testable Software

Components. InProceedings of the 2001 International Conference on Depen-

dable Systems and Networks (DSN ’01), Washington - Brussels- Tokyo, pages

151–160. IEEE, 2001.

[OA99] Jeff Offutt and Aynur Abdurazik. Generating Tests from UML Specifications.

In Robert France and Bernhard Rumpe, editors,UML’99 - The Unified Mode-

ling Language. Beyond the Standard. Second International Conference, Fort

Collins, CO, USA, October 28-30. 1999, Proceedings, volume 1723 ofLNCS,

pages 416–429. Springer, 1999.

[OMG02] OMG. XML Metadata Interchange (XMI) Specification - Version1.2, 2002.

[OMG03a] OMG. UML Object Constraint Language (OCL) Specification - Version 2.0,

2003.

[OMG03b] OMG. Unified Modeling Language (UML) Specification - Version1.5, 2003.

[PBB97] Cécile Péraire, Stéphane Barbey, and Didier Buchs. Test Selection for Object-

Oriented Software Based on Formal Specifications, 1997.

[PTLP99] Stacy J. Prowell, Carmen J. Trammell, Richard C. Linger, andJesse H. Poore.

Cleanroom Software Engineering - Technology and Process. The SEI Series in

Software Engineering. Addison-Wesley, 1999.

[RPG01] Matthias Riebisch, Ilka Philippow, and Marco Götze. UML-Based Statistical

Test Case Generation, 2001.

[Som03] Ian Sommerville.Engenharia de Software. Addison-Wesley Publishing Com-

pany, 2003.

BIBLIOGRAFIA 108

[Szy98] Clemens Szyperski.Component Software: Beyond Object-Oriented Program-

ming. ACM Press and Addison-Wesley, New York, NY, 1998.

[TB02] J. Tretmans and E. Brinksma. Côte de Resyste - Automated Model Based Tes-

ting. In M. Schweizer, editor,Proceedings of Progress 2002 - 3rd Workshop on

Embedded Systems, STW Technology Foundation, Utrecht, TheNetherlands,

pages 246–255, 2002.

[VDR00] M. E. Vieira, M. S. Dias, and D. J. Richardson. Object-Oriented Specification-

Based Testing Using UML Statechart Diagrams. InProceedings of ICSE2000

- Workshop on Automated Program Analysis, Testing, and Verification - June,

2000, Limerick, Ireland, 2000.

[W3C05] W3C. Document Object Model. Disponível em http://www.w3.org/DOM/, Ja-

neiro, 2005.

[Wan05] Yuqing Wang. JTestCase. Disponível em http://jtestcase.sourceforge.net/,

Março, 2005.

[ÉGG98] Étienne Gagnon and Etienne Gagnon. Sablecc, an object-oriented compiler

framework. InIn Proceedings of TOOLS, pages 140–154, 1998.

Apêndice A

Mapeamento OCL – Java

Neste apêndice, descrevemos o mapeamento proposto da linguagem de restrições OCL

para a linguagem de programação Java. Esta conversão teve como objetivo obter um código

fonte equivalente de forma que a mesma restrição descrita emOCL pudesse ser avaliada, em

tempo de execução, pelo código de teste gerado pelo método AFCT. Para tanto, procuramos

respeitar todas as características inerentes a linguagem OCL, a exemplo da ausência de efei-

tos colaterais, uma vez que trata-se de uma linguagem puramente de expressão. Além disso,

como OCL é uma linguagem de semântica formal, procuramos utilizar um sub-conjunto bem

definido e validado pela comunidade de desenvolvedores, de tipos, operadores, operações e

contruções da linguagem java. O mapeamento proposto para asconstruções mais usuais de

OCL pode ser visto a seguir.

A.1 Declaração de Context

OCL context <context> [ :: <operation> ( [<op_params>] ) [:<result_type>]]

inv | pre | post <const-name> ...

Java boolean check_<const-name>(<context> self [, <op_params>]

[, <result_type> result]) { ... }

Tabela A.1: Mapeamento OCL – Java: Declaração de Context

109

A.3 Operadores (Numéricos, Relacionais e Lógicos) 110

A.2 Tipos

OCL Java

Integer int

Real double

Boolean boolean

String java.lang.String

Model Type Model Type

Collection java.util.Collection

Set java.util.Set

Sequence java.util.ArrayList

Bag java.util.ArrayList

OCLAny java.lang.Object

Tabela A.2: Mapeamento OCL – Java: Tipos

A.3 Operadores (Numéricos, Relacionais e Lógicos)

OCL Java

+, -, *, /, - Unário +, -, *, /, - Unário

Módulo (mod) Resto da Divisão (%)

Divisão Inteira (div) /

Valor Absoluto (<numeric_expression>.abs)Math.abs(<numeric_expression>)

Truncamento (<real_expression>.floor) Math.floor(<real_expression>)

<, >, <=, >= <, >, <=, >=

Igualdade (=) == (Tipos Primitivos), equals (Objetos)

Diferença (<>) != (Tipos Primitivos), !equals (Objetos)

not !

and &&

or ||

xor ˆ

Tabela A.3: Mapeamento OCL – Java: Operadores(continua)

A.5 Construções Básicas 111

OCL Java

implies (a implies b) !a || (a && b)

Tabela A.3: Mapeamento OCL – Java: Operadores

A.4 Operações sobre Strings

OCL Java

<string_expression>.concat( <string_expression>.concat(

<string_expression2>) <string_expression2>)

<string_expression>.size <string_expression>.length()

<string_expression>.toLower <string_expression>.toLowerCase()

<string_expression>.toUpper <string_expression>.toUpperCase()

<string_expression>.substring( <string_expression>.substring(

<integer_expression>, <integer_expression2>)<integer_expression>, <integer_expression2>)

Tabela A.4: Mapeamento OCL – Java: Operações sobre Strings

A.5 Construções Básicas

OCL Java

false false

true true

Literal Integer Literal int

Literal Real Literal double

Literal String (‘...’) Literal java.lang.String (“...”)

self <Tipo> self (parâmetro do método check...)

parâmetros de entrada (pre e post) parâmetros do método check...

result <Tipo> result (parâmetro do método check...)

if <boolean_expression> then y else w endifif (<boolean_expression>) y; else w;

let x=y in <x_expression> <Tipo> x = y; <x_expression>

Tabela A.5: Mapeamento OCL – Java: Construções Básicas

A.6 Construções Collection 112

A.6 Construções Collection

OCL Java

<collection_expression>->size java.util.Collection <collection_expression> =

obj.get<collection_expression>();

int size = <collection_expression>.size();

<collection_expression>->count( java.util.Collection <collection_expression> =

<element_expression>) obj.get<collection_expression>();

Object[] elements = <collection_expression>.toArray();

int count = 0;

for (int i = 0; i < elements.lenght; i++){

if (elements[i].equals(<element_expression>))

count++;

}

<collection_expression>->isEmpty java.util.Collection <collection_expression> =

obj.get<collection_expression>();

boolean value = <collection_expression>.isEmpty();

<collection_expression>->notEmpty java.util.Collection <collection_expression> =

obj.get<collection_expression>();

boolean value = !<collection_expression>.isEmpty();

<collection_expression>->includes( java.util.Collection <collection_expression> =

<element_expression>) obj.get<collection_expression>();

boolean value = <collection_expression>.contains(

<element_expression>);

<collection_expression>->includesAll( java.util.Collection <collection_expression> =

<collection_expression2>) obj.get<collection_expression>();

boolean value = <collection_expression>.containsAll(

<collection_expression2>);

<collection_expression>->exists( boolean value = false;

<element_variable> : <type> | <expression>)java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

Tabela A.6: Mapeamento OCL – Java: Construções Collection

(continua)

A.6 Construções Collection 113

OCL Java

<collection_expression>.iterator();

<type> <element_variable>;

while (<collection_expression>It.hasNext()){

<element_variable> = (<type>)

<collection_expression>It.next();

if (<expression>){

value = true;

break;

}

}

<collection_expression>->forAll( boolean value = true;

<element_variable> : <type> | <expression>)java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

<collection_expression>.iterator();

<type> <element_variable>;

while (<collection_expression>It.hasNext()){

<element_variable> = (<type>)

<collection_expression>It.next();

if (!(<expression>)){

value = false;

break;

}

}

<collection_expression>->collect( java.util.Collection value = new java.util.ArrayList();

<element_variable> : <type> | <expression>)java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

<collection_expression>.iterator();

<type> <element_variable>;

while (<collection_expression>It.hasNext()){

Tabela A.6: Mapeamento OCL – Java: Construções Collection

(continua)

A.6 Construções Collection 114

OCL Java

<element_variable> = (<type>)

<collection_expression>It.next();

value.add(<element_variable>.get<expression>());

}

<collection_expression>->select( java.util.Collection value = new java.util.ArrayList();

<element_variable> : <type> | <expression>)java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

<collection_expression>.iterator();

<type> <element_variable>;

while (<collection_expression>It.hasNext()){

<element_variable> = (<type>)

<collection_expression>It.next();

if (<expression>)

value.add(<element_variable>);

}

<collection_expression>->reject( java.util.Collection value = new java.util.ArrayList();

<element_variable> : <type> | <expression>)java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

<collection_expression>.iterator();

<type> <element_variable>;

while (<collection_expression>It.hasNext()){

<element_variable> = (<type>)

<collection_expression>It.next();

if (!(<expression>))

value.add(<element_variable>);

}

<collection_expression>->union( java.util.Collection value = new java.util.ArrayList();

<collection_expression2>) java.util.Collection <collection_expression> =

obj.get<collection_expression>();

Tabela A.6: Mapeamento OCL – Java: Construções Collection

(continua)

A.6 Construções Collection 115

OCL Java

java.util.Collection <collection_expression2> =

obj.get<collection_expression2>();

value.addAll(<collection_expression>);

value.addAll(<collection_expression2>);

<collection_expression>->intersection( java.util.Collection value = new java.util.ArrayList();

<collection_expression2>) java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Collection <collection_expression2> =

obj.get<collection_expression2>();

java.util.Iterator <collection_expression2>It =

<collection_expression2>.iterator();

while (<collection_expression2>It.hasNext()){

Object element = <collection_expression2>It.next();

if (<collection_expression>.contains(element))

value.add(element);

}

<collection_expression>->including( java.util.Collection value = new java.util.ArrayList();

<element_expression>) java.util.Collection <collection_expression> =

obj.get<collection_expression>();

value.addAll(<collection_expression>);

value.add(<element_expression>);

<collection_expression>->excluding( java.util.Collection value = new java.util.ArrayList();

<element_expression>) java.util.Collection <collection_expression> =

obj.get<collection_expression>();

value.addAll(<collection_expression>);

value.remove(<element_expression>);

<collection_expression>->sum() Number value = new Double(0.0);

java.util.Collection value = new java.util.ArrayList();

java.util.Collection <collection_expression> =

obj.get<collection_expression>();

java.util.Iterator <collection_expression>It =

Tabela A.6: Mapeamento OCL – Java: Construções Collection

(continua)

A.6 Construções Collection 116

OCL Java

<collection_expression>.iterator();

Number element;

while (<collection_expression>It.hasNext()){

element = (Number)<collection_expression>It.next();

value = new Double(value.doubleValue() +

element.doubleValue());

}

Tabela A.6: Mapeamento OCL – Java: Construções Collection

Apêndice B

Subconjunto XMI Utilizado

Neste apêndice, apresentamos o DTD (Document Type Definition) que descreve o sub-

conjunto dos elementos definidos pelo padrão XMI (XML Metadata Interchange) nos quais

estão contidas as informações referentes aos artefatos UMLnecessários para a aplicação do

método AFCT e que, portanto, foram considerados durante a construção do móduloParser

XMI da ferramenta SPACES. A seguir vemos a descrição destes elementos.

<!ENTITY % XMI.element.att

’xmi.id ID #IMPLIED’ >

<!ENTITY % XMI.link.att

’xmi.idref IDREF #IMPLIED’ >

<!ELEMENT XMI (UML:Model, UML:Diagram * ) >

<!ATTLIST XMI

xmi.version CDATA #FIXED "1.2" >

<!ELEMENT UML:Model (UML:Class | UML:Interface | UML:Asso ciation |

UML:Constraint | UML:DataType | UML:Stereotype |

UML:Generalization | UML:Abstraction | UML:Actor |

UML:UseCase | UML:Include | UML:Extend |

UML:Collaboration | UML:Comment | UML:Package ) * >

<!ATTLIST UML:Model

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

117

118

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Class (UML:Attribute | UML:Operation | UML: Constraint |

UML:Comment) * >

<!ATTLIST UML:Class

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

isActive CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Attribute (UML:Expression, UML:Structura lFeature.type) * >

<!ATTLIST UML:Attribute

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

ownerScope (instance|classifier) #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Expression >

<!ATTLIST UML:Expression

language CDATA #IMPLIED

body CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:StructuralFeature.type (UML:DataType | UM L:Class |

UML:Interface) >

<!ELEMENT UML:Operation (UML:Parameter) * >

<!ATTLIST UML:Operation

name CDATA #IMPLIED

119

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

ownerScope (instance|classifier) #IMPLIED

isQuery CDATA #IMPLIED

concurrency (sequential|guarded|concurrent) #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Parameter (UML:Parameter.type) * >

<!ATTLIST UML:Parameter

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

kind (in|inout|out|return) #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Parameter.type (UML:DataType | UML:Class | UML:Interface) >

<!ELEMENT UML:Interface (UML:Operation) * >

<!ATTLIST UML:Interface

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Association (UML:AssociationEnd) * >

<!ATTLIST UML:Association

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

120

<!ELEMENT UML:AssociationEnd (UML:MultiplicityRange,

UML:AssociationEnd.participant) * >

<!ATTLIST UML:AssociationEnd

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

isNavigable CDATA #IMPLIED

ordering (unordered|ordered) #IMPLIED

aggregation (none|aggregate|composite) #IMPLIED

targetScope (instance|classifier) #IMPLIED

changeability (changeable|frozen|addOnly) #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:MultiplicityRange >

<!ATTLIST UML:MultiplicityRange

lower CDATA #IMPLIED

upper CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:AssociationEnd.participant (UML:Class | U ML:Interface |

UML:UseCase | UML:Actor) >

<!ELEMENT UML:Constraint (UML:BooleanExpression, UML:C lass) * >

<!ATTLIST UML:Constraint

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:BooleanExpression >

<!ATTLIST UML:BooleanExpression

language CDATA #IMPLIED

body CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:DataType >

<!ATTLIST UML:DataType

121

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Stereotype >

<!ATTLIST UML:Stereotype

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Generalization (UML:Generalization.chil d,

UML:Generalization.parent) * >

<!ATTLIST UML:Generalization

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Generalization.child (UML:Class | UML:Int erface |

UML:UseCase | UML:Actor) >

<!ELEMENT UML:Generalization.parent (UML:Class | UML:In terface |

UML:UseCase | UML:Actor) >

<!ELEMENT UML:Abstraction (UML:Stereotype, UML:Depende ncy.client,

UML:Dependency.supplier) * >

<!ATTLIST UML:Abstraction

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Dependency.client (UML:Class) >

<!ELEMENT UML:Dependency.supplier (UML:Interface | UML: Class) >

122

<!ELEMENT UML:Actor >

<!ATTLIST UML:Actor

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:UseCase >

<!ATTLIST UML:UseCase

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Include (UML:Include.addition, UML:Inclu de.base) * >

<!ATTLIST UML:Include

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Include.addition (UML:UseCase) >

<!ELEMENT UML:Include.base (UML:UseCase) >

<!ELEMENT UML:Extend (UML:Extend.base, UML:Extend.exte nsion) * >

<!ATTLIST UML:Extend

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Extend.base (UML:UseCase) >

123

<!ELEMENT UML:Extend.extension (UML:UseCase) >

<!ELEMENT UML:Collaboration (UML:CollaborationInstanc eSet,

UML:Object * , UML:Stimulus * ,

UML:CreateAction * , UML:CallAction * ,

UML:SendAction * , UML:ReturnAction * ,

UML:DestroyAction * , UML:Comment * ) * >

<!ATTLIST UML:Collaboration

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:CollaborationInstanceSet (UML:Collabora tion) * >

<!ATTLIST UML:CollaborationInstanceSet

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Object (UML:Instance.classifier, UML:Com ment * ) * >

<!ATTLIST UML:Object

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Instance.classifier (UML:Class | UML:Inte rface) >

<!ELEMENT UML:Stimulus (UML:Stimulus.sender, UML:Stimu lus.receiver,

UML:Stimulus.dispatchAction) * >

<!ATTLIST UML:Stimulus

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

124

<!ELEMENT UML:Stimulus.sender (UML:Object) >

<!ELEMENT UML:Stimulus.receiver (UML:Object) >

<!ELEMENT UML:Stimulus.dispatchAction (UML:CreateActi on | UML:CallAction |

UML:SendAction | UML:ReturnAction |

UML:DestroyAction) >

<!ELEMENT UML:CreateAction >

<!ATTLIST UML:CreateAction

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isAsynchronous CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:CallAction (UML:Operation, UML:Argument * ) * >

<!ATTLIST UML:CallAction

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isAsynchronous CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Argument (UML:Expression) * >

<!ATTLIST UML:Argument

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:SendAction >

<!ATTLIST UML:SendAction

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isAsynchronous CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:ReturnAction >

<!ATTLIST UML:ReturnAction

125

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isAsynchronous CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:DestroyAction >

<!ATTLIST UML:DestroyAction

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isAsynchronous CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Comment (UML:Comment.annotatedElement) * >

<!ATTLIST UML:Comment

name CDATA #IMPLIED

visibility (public|protected|private|package) #IMPLIE D

isSpecification CDATA #IMPLIED

body CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Comment.annotatedElement (UML:Class | UML :Object) >

<!ELEMENT UML:Package (UML:Class | UML:Interface | UML:As sociation |

UML:Constraint | UML:DataType | UML:Stereotype |

UML:Generalization | UML:Abstraction | UML:Actor |

UML:UseCase | UML:Include | UML:Extend |

UML:Collaboration | UML:Comment | UML:Package) * >

<!ATTLIST UML:Package

name CDATA #IMPLIED

isSpecification CDATA #IMPLIED

isRoot CDATA #IMPLIED

isLeaf CDATA #IMPLIED

isAbstract CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:Diagram (UML:SimpleSemanticModelElement |

UML:GraphElement.contained | UML:Diagram.owner) * >

126

<!ATTLIST UML:Diagram

isVisible CDATA #IMPLIED

name CDATA #IMPLIED

zoom CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:SimpleSemanticModelElement >

<!ATTLIST UML:SimpleSemanticModelElement

presentation CDATA #IMPLIED

typeInfo CDATA #IMPLIED

%XMI.element.att; %XMI.link.att;>

<!ELEMENT UML:GraphElement.contained (UML:GraphElemen t.contained * ) >

<!ELEMENT UML:Diagram.owner (UML:CollaborationInstanc eSet?) >

Apêndice C

API UML

Neste apêndice apresentamos a estrutura dos classificadores (classes e interfaces) que

fazem parte daAPI UML cujos objetos são instanciados pelo móduloParser XMIda ferra-

menta SPACES, a partir da especificação do componente a ser testado, e que são utilizados

como entrada para os demais módulos da ferramenta. Para facilitar a apresentação, os clas-

sificadores foram agrupadas de acordo com as associações existentes e com o papel que

desempenham na API. Relacionamentos entre classificadoresde grupos diferentes são apre-

sentados a partir da atribuição da tonalidade cinza àquelesque não fazem parte do grupo

atual.

127

128

Element

(from xmiparser ::uml )

#name :String = ""

#_abstract :boolean

#leaf :boolean

#root :boolean

#specification :boolean

+ getName ():String

+ setName (_name:String ):void

+ isAbstract ():boolean

+ setAbstract (__abstract :boolean ):void

+ isLeaf ():boolean

+ setLeaf (_leaf :boolean ):void

+ isRoot ():boolean

+ setRoot (_root :boolean ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

Model

(from xmiparser ::uml )

+ getDiagrams ():Collection

+ addDiagram (diagram :Diagram ):void

+ removeDiagram (diagram :Diagram ):void

+ toString ():String

+ equals (obj :Object ):boolean

Package

(from xmiparser ::uml )

+ getAssociations ():Collection

+ addAssociation (association :Association ):void

+ removeAssociation (association :Association ):void

+ getInterfaces ():Collection

+ addInterface (_interface :Interface ):void

+ removeInterface (_interface :Interface ):void

+ getClasses ():Collection

+ addClass (_class:Class):void

+ removeClass (_class:Class ):void

+ getPackages ():Collection

+ addPackage (_package :Package):void

+ removePackage (_package:Package):void

+ getDataTypes ():Collection

+ addDataType (dataType :DataType ):void

+ removeDataType (dataType :DataType ):void

+ getAbstractions ():Collection

+ addAbstraction (abstraction :Abstraction ):void

+ removeAbstraction (abstraction :Abstraction ):void

+ getGeneralizations ():Collection

+ addGeneralization (generalization :Generalization ):void

+ removeGeneralization (generalization :Generalization ):void

+ getStereotypes ():Collection

+ addStereotype (stereotype :Stereotype ):void

+ removeStereotype (stereotype :Stereotype ):void

+ getActors ():Collection

+ addActor (actor :Actor ):void

+ removeActor (actor :Actor ):void

+ getUseCases ():Collection

+ addUseCase(usecase:UseCase):void

+ removeUseCase (usecase:UseCase):void

+ getIncludes ():Collection

+ addInclude (include :Include ):void

+ removeInclude (include :Include ):void

+ getExtends ():Collection

+ addExtend (extend :Extend ):void

+ removeExtend (extend :Extend ):void

+ getCollaborations ():Collection

+ addCollaboration (collaboration :Collaboration ):void

+ removeCollaboration (collaboration :Collaboration ):void

+ getComments ():Collection

+ addComment (comment :Comment ):void

+ removeComment (comment :Comment ):void

+ toString ():String

+ equals (obj :Object ):boolean

+ getClass (name :String ):Class

+ getAssociations (_class:Class):Collection

Stereotype

(from xmiparser ::uml )

+ toString ():String

Comment

(from xmiparser ::uml )

- name :String = ""

- visibility :String = ""

- specification :boolean

- body :String = ""

+ getBody ():String

+ getName ():String

+ isSpecification ():boolean

+ getVisibility ():String

+ setBody (string :String ):void

+ setName (string :String ):void

+ setSpecification (b:boolean ):void

+ setVisibility (string :String ):void

+ getAnnotatedElement ():Commentable

+ setAnnotatedElement (object :Commentable ):void

+ equals (obj :Object ):boolean

+ toString ():String

<< interface >>

Commentable

(from xmiparser ::uml )

+ addComment (comment :Comment ):void

+ getComments ():Collection

+ removeComment (comment :Comment ):void

annotatedElement-

Association

(from xmiparser ::uml )

AssociationElement

(from xmiparser ::uml )

DataType

(from xmiparser ::uml )

Collaboration

(from xmiparser ::uml )

comments-

Diagram

(from xmiparser ::uml )

diagrams-

associations#

packages#

dataTypes#

stereotypes#

collaborations#

comments

#

Class

(from xmiparser ::uml )

comments-

Interface

(from xmiparser ::uml )

interfaces#classes#

Abstraction

(from xmiparser ::uml )

stereotype-

client-

Generalization

(from xmiparser ::uml )

child-parent-

abstractions#

generalizations#

Actor

(from xmiparser ::uml )

Extend

(from xmiparser ::uml )

Include

(from xmiparser ::uml )

UseCase

(from xmiparser ::uml )

addition-

base-

base-extension-

actors#

usecases#

extends#

includes#

Figura C.1: API UML: Visão Geral

129

Class

(from xmiparser ::uml )

- active :boolean

- constraint :Collection = new ArrayList()

- comment :Collection = new ArrayList()

+ getFeatures ():Collection

+ addFeature (feature :Feature):void

+ removeFeature (feature :Feature ):void

+ getConstraints ():Collection

+ addConstraint (constraint :Constraint ):void

+ removeConstraint (constraint :Constraint ):void

+ getComments ():Collection

+ addComment (comment :Comment ):void

+ removeComment (comment :Comment ):void

+ isActive ():boolean

+ setActive (_active:boolean ):void

+ toResumedString ():String

+ toString ():String

+ equals (obj :Object ):boolean

Classifier

(from xmiparser ::uml )

+ toResumedString ():String

DataType

(from xmiparser ::uml )

+ toResumedString ():String

+ toString ():String

Feature

(from xmiparser ::uml )

+ SK_INSTANCE:String = "instance"

+ SK_CLASSIFIER:String = "classifier"

#visibility :String = ""

#_static :boolean

#name :String = ""

#specification :boolean

+ getVisibility ():String

+ setVisibility (_visibility :String ):void

+ isStatic ():boolean

+ setStatic (__static:boolean ):void

+ getName ():String

+ setName (_name:String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

Interface

(from xmiparser ::uml )

- operation :Collection = new ArrayList()

+ getOperations ():Collection

+ addOperation (operation :Operation ):void

+ removeOperation (operation :Operation ):void

+ toResumedString ():String

+ toString ():String

+ equals (obj :Object ):boolean

<< interface >>

Type

(from xmiparser ::uml )

+ getName ():String

+ toResumedString ():String

Operation

(from xmiparser ::uml )

- query :boolean

- concurrency :String = ""

- _abstract :boolean

- leaf :boolean

- root :boolean

+ getParameters ():Collection

+ addParameter (parameter :Parameter ):void

+ removeParameter (parameter :Parameter ):void

+ getExceptions ():Collection

+ addException (exception :Exception ):void

+ removeException (exception :Exception ):void

+ isQuery ():boolean

+ setQuery (_query :boolean ):void

+ getConcurrency ():String

+ setConcurrency (_concurrency :String ):void

+ isAbstract ():boolean

+ setAbstract (__abstract :boolean ):void

+ isLeaf ():boolean

+ setLeaf (_leaf :boolean ):void

+ isRoot ():boolean

+ setRoot (_root :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

+ getParametersWithoutReturnParameter ():Collection

+ getReturnParameter ():Parameter

Attribute

(from xmiparser ::uml )

- _final :boolean

- _transient :boolean

- _volatile :boolean

- initialValue :String = ""

+ getType ():Type

+ setType (type :Type ):void

+ isFinal ():boolean

+ setFinal (__final :boolean ):void

+ isTransient ():boolean

+ setTransient (__transient :boolean ):void

+ isVolatile ():boolean

+ setVolatile (__volatile :boolean ):void

+ getInitialValue ():String

+ setInitialValue (_initialValue :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

type-

Parameter

(from xmiparser ::uml )

+ DK_IN:String = "in"

+ DK_OUT:String = "out"

+ DK_INOUT:String = "inout"

+ DK_RETURN:String = "return"

- kind :String = ""

- _final :boolean

- name :String = ""

- specification :boolean

+ getType ():Type

+ setType (type :Type ):void

+ getKind ():String

+ setKind (_kind :String ):void

+ isFinal ():boolean

+ setFinal (__final :boolean ):void

+ getName ():String

+ setName (_name:String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

type-

parametros-

Commentable(from xmiparser ::uml )

Element

(from xmiparser ::uml )

AssociationElement

(from xmiparser ::uml )

features-

operations-

Comment

(from xmiparser ::uml )

annotatedElement-

Constraint

(from xmiparser ::uml )

- name :String = ""

- specification :boolean

- language :String = ""

- body :String = ""

+ getName ():String

+ setName (_name:String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ getLanguage ():String

+ setLanguage (_language :String ):void

+ getBody ():String

+ setBody (_body :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

comments-

constraints-

Exception

(from xmiparser ::uml )

- type :String = ""

+ getType ():String

+ setType (_type :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

exceptions-

Figura C.2: API UML: Hierarquia de Tipo

130

Association

(from xmiparser ::uml )

+ getAssociationEnds ():Collection

+ addAssociationEnd (associationEnd :AssociationEnd ):void

+ removeAssociationEnd (associationEnd :AssociationEnd ):void

+ toString ():String

+ equals (obj :Object ):boolean

AssociationEnd

(from xmiparser ::uml )

+ OK_UNORDERED:String = "unordered"

+ OK_ORDERED:String = "ordered"

+ AK_NONE:String = "none"

+ AK_AGGREGATE:String = "aggregate"

+ AK_COMPOSITE:String = "composite"

+ SK_INSTANCE:String = "instance"

+ SK_CLASSIFIER:String = "classifier"

+ CK_CHANGEABLE:String = "changeable"

+ CK_FROZEN:String = "frozen"

+ CK_ADDONLY:String = "addOnly"

- name :String = ""

- visibility :String = ""

- specification :boolean

- navegable :boolean

- ordering :String = ""

- aggregation :String = ""

- targetScope :String = ""

- changeability :String = ""

+ getMultiplicity ():MultiplicityRange

+ setMultiplicity (multiplicityRange :MultiplicityRange ):void

+ getParticipant ():AssociationElement

+ setParticipant (participant :AssociationElement ):void

+ getName ():String

+ setName (_name:String ):void

+ getVisibility ():String

+ setVisibility (_visibility :String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ isNavegable ():boolean

+ setNavegable (_navegable :boolean ):void

+ getOrdering ():String

+ setOrdering (_ordering :String ):void

+ getAggregation ():String

+ setAggregation (_aggregation :String ):void

+ getTargetScope ():String

+ setTargetScope (_targetScope :String ):void

+ getChangeability ():String

+ setChangeability (_changeability :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

associationEnds-

2

AssociationElement

(from xmiparser ::uml )

+ VK_PUBLIC:String = "public"

+ VK_PROTECTED:String = "protected"

+ VK_PACKAGE:String = "package"

+ VK_PRIVATE:String = "private"

#visibility :String = ""

+ getVisibility ():String

+ setVisibility (_visibility :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

participant-

MultiplicityRange

(from xmiparser ::uml )

- lower :int

- upper :int

+ getLower (): int

+ setLower (_lower :int ):void

+ getUpper ():int

+ setUpper (_upper :int ):void

+ toString ():String

+ equals (obj :Object ):boolean

multiplicity-

Element

(from xmiparser ::uml )

Figura C.3: API UML: Associação

Abstraction

(from xmiparser ::uml )

- specification :boolean

+ getStereotype ():Stereotype

+ setStereotype (stereotype :Stereotype ):void

+ getClient ():Class

+ setClient (client :Class):void

+ getSupplier ():Classifier

+ setSupplier (supplier :Classifier ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

Class

(from xmiparser ::uml )client-

Classifier

(from xmiparser ::uml )

supplier-

Stereotype

(from xmiparser ::uml )

stereotype-

Generalization

(from xmiparser ::uml )

- specification :boolean

+ getChild ():AssociationElement

+ setChild (child :AssociationElement ):void

+ getParent ():AssociationElement

+ setParent (parent :AssociationElement ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

AssociationElement

(from xmiparser ::uml )

child- parent-

Figura C.4: API UML: Generalização e Abstração

131

Collaboration

(from xmiparser ::uml )

+ getObjects ():Collection

+ addObject (object :Object ):void

+ removeObject (object :Object ):void

+ getStimulus ():Collection

+ addStimulus (stimulus :Stimulus ):void

+ removeStimulus (stimulus :Stimulus ):void

+ getCreateActions ():Collection

+ addCreateAction (createAction :CreateAction ):void

+ removeCreateAction (createAction :CreateAction ):void

+ getCallActions ():Collection

+ addCallAction (callAction :CallAction ):void

+ removeCallAction (callAction :CallAction ):void

+ getSendActions ():Collection

+ addSendAction (sendAction :SendAction ):void

+ removeSendAction (sendAction :SendAction ):void

+ getReturnActions ():Collection

+ addReturnAction (returnAction :ReturnAction ):void

+ removeReturnAction (returnAction :ReturnAction ):void

+ getDestroyActions ():Collection

+ addDestroyAction (destroyAction :DestroyAction ):void

+ removeDestroyAction (destroyAction :DestroyAction ):void

+ getComments ():Collection

+ addComment (comment :Comment ):void

+ removeComment (comment :Comment ):void

+ toString ():String

+ equals (obj :Object ):boolean

CollaborationInstanceSet

(from xmiparser ::uml )

- name :String = ""

- specification :boolean

+ getCollaboration ():Collaboration

+ setCollaboration (collaboration :Collaboration ):void

+ getName ():String

+ setName (_name:String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

collaboration-

Diagram

(from xmiparser ::uml )

+ DK_CLASS_DIAGRAM:String = "ClassDiagram"

+ DK_USECASE_DIAGRAM:String = "UseCaseDiagram"

+ DK_SEQUENCE_DIAGRAM:String = "SequenceDiagram"

#name :String = ""

#type :String = ""

+ getName ():String

+ setName (_name:String ):void

+ getType ():String

+ setType (_type :String ):void

+ toString ():String

+ equals (obj :Object ):boolean

Object

(from xmiparser ::uml )

- name :String = ""

- visibility :String = ""

- specification :boolean

+ getInstanceClassifier ():Classifier

+ setInstanceClassifier (instanceClassifier :Classifier ):void

+ getName ():String

+ setName (_name:String ):void

+ getVisibility ():String

+ setVisibility (_visibility :String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ getComments ():Collection

+ addComment (comment :Comment ):void

+ removeComment (comment :Comment ):void

+ toString ():String

+ equals (obj :Object ):boolean

objects-

2..*

SequenceDiagram

(from xmiparser ::uml )

+ getCollaboration ():Collaboration

+ setCollaboration (collaboration :Collaboration ):void

+ toString ():String

+ equals (obj :Object ):boolean

collaboration

-

Stimulus

(from xmiparser ::uml )

- name :String = ""

- specification :boolean

+ getSender ():Object

+ setSender (sender :Object ):void

+ getReceiver ():Object

+ setReceiver (receiver :Object ):void

+ getDispatchAction ():Action

+ setDispatchAction (dispatchAction :Action ):void

+ getName ():String

+ setName (_name:String ):void

+ isSpecification ():boolean

+ setSpecification (_specification :boolean ):void

+ toString ():String

+ equals (obj :Object ):boolean

sender- receiver-

stimuli-

1..*

Commentable(from xmiparser ::uml )

Element

(from xmiparser ::uml )

Classifier

(from xmiparser ::uml )

instanceClassifier-

Action

(from xmiparser ::uml )

dispatchAction-

ReturnAction

(from xmiparser ::uml )

SendAction

(from xmiparser ::uml )

DestroyAction

(from xmiparser ::uml )

CreateAction

(from xmiparser ::uml )

CallAction

(from xmiparser ::uml )

returnActions- destroyActions- sendActions- callActions-createActions-

Comment

(from xmiparser ::uml )

annotatedElement-

comments-

comments-

Figura C.5: API UML: Colaboração e Diagrama de Seqüência

132

Action

(from xmiparser ::uml )

#name :String = ""

#isSpecification :boolean

#isAsynchronous :boolean

+ isAsynchronous ():boolean

+ isSpecification ():boolean

+ getName ():String

+ setAsynchronous (isAsynchronous :boolean ):void

+ setSpecification (isSpecification :boolean ):void

+ setName (name :String ):void

+ equals (obj :Object ):boolean

+ toString ():String

+ toExpressionString ():String

CallAction

(from xmiparser ::uml )

+ getArguments ():Collection

+ addArgument (argument :Argument ):void

+ removeArgument (argument :Argument ):void

+ getOperation ():Operation

+ setOperation (operation :Operation ):void

+ equals (obj :Object ):boolean

+ toString ():String

+ toExpressionString ():StringCreateAction

(from xmiparser ::uml )

+ equals (obj :Object ):boolean

+ toString ():String

DestroyAction

(from xmiparser ::uml )

+ equals (obj :Object ):boolean

+ toString ():String

ReturnAction

(from xmiparser ::uml )

+ equals (obj :Object ):boolean

+ toString ():String

SendAction

(from xmiparser ::uml )

+ equals (obj :Object ):boolean

+ toString ():String

Argument

(from xmiparser ::uml )

- name :String = ""

- isSpecification :boolean

- value :String = ""

+ isSpecification ():boolean

+ getName ():String

+ setSpecification (isSpecification :boolean ):void

+ setName (name :String ):void

+ getValue ():String

+ setValue (value :String ):void

+ equals (obj :Object ):boolean

+ toString ():String

arguments-*

Operation

(from xmiparser ::uml )

operation

-

Figura C.6: API UML: Hierarquia de Ações

UseCase

(from xmiparser::uml)

+toString():String

Include

(from xmiparser::uml)

-specification:boolean

+getAddition():UseCase

+setAddition(addition:UseCase):void

+getBase():UseCase

+setBase(base:UseCase):void

+isSpecification():boolean

+setSpecification(_specification:boolean):void

+toString():String

+equals(obj:Object):boolean

addition-base-

Extend

(from xmiparser::uml)

-specification:boolean

+getBase():UseCase

+setBase(base:UseCase):void

+getExtension():UseCase

+setExtension(extension:UseCase):void

+isSpecification():boolean

+setSpecification(_specification:boolean):void

+toString():String

+equals(obj:Object):boolean

base-

extension-

Actor

(from xmiparser::uml)

+toString():String

AssociationElement

(from xmiparser::uml)

Figura C.7: API UML: Ator e Caso de Uso