quarta-feira, 20 de fevereiro de 2008

Automação de testes funcionais e de aceitação

Testes funcionais são aqueles que encaram o sistema a ser testado como uma função que mapeia um conjunto de valores de entrada em um conjunto de valores de saída sem se preocupar com a forma como esse mapeamento foi implementado.

Um teste de aceitação (ou Story Test, ou Customer Test) descreve um cenário (de sucesso ou não) com uma expectativa do cliente em relação à história ou funcionalidade. Como o nome sugere, ele ajuda a equipe a entender quando uma história ou funcionalidade está completa ou aceita.

Diversas ferramentas e frameworks têm surgido para auxiliar sua vida. Entre as mais conhecidas temos o Selenium e o Watir podem facilitar sua vida no teste de aplicações web.

Percebemos que teremos um problema ao utilizar o Selenium quando o projeto funciona apenas no navegador internet explorer. Isto ocorre, pois o Selenion IDE que é uma ferramenta que facilita a gravação dos scripts é um addin para o mozilla firefox e quando o sistema não funciona no navegador mozilla não é possível o utilizar.

Situação ideal: Ter uma aplicação que funciona em ambos os browsers, gravar os scripts no Selenium e realizar os teste utilizando os navegadores.

Situação alternativa: Para aplicações que funcionam somente no navegador internet explorer, o que nos deixa como melhor opção a utilização do Watir que possui como facilidade o aplicativo IE Developer Toolbar.

O Watir é opensource e com seus comandos e funções particulares permite então que possamos controlar os objetos HTML e JavaScript presentes na aplicação Web, simulando os cliques em links, botões e os demais campos. Watir tem sido usado para testes de sistemas, testes funcionais e teste de aceitação do usuário. Como seu próprio nome diz (Web Application Testing in Ruby),

Para o uso do Watir torna-se necessário a instalação do interpretador Ruby e do pacote que contém o Watir. O Watir não utiliza da técnica conhecida como Record/Playback, onde o arquiteto de testes grava o script através do movimento do mouse e teclado.

Os scripts são escritos mesmo “na unha”, portanto o recomendado é que o responsável pelo uso do Watir tenha algum conhecimento de lógica de programação e dos atributos das tags HTML. Esse conhecimento em desenvolvimento se torna importante, visto que, estamos falando de programação de scripts e para que possamos ter um produto final de qualidade, podem ser aplicados todos os conceitos e boas práticas de programação.

Existe um facilitador que é a instalação do aplicativo IE Developer Toolbar que se integra na barra de ferramentas do Internet Explorer facilitando na identificação dos atributos utilizados na automação.

Gestão de testes

A gestão de testes é a parte principal de um processo de testes. A gestão é importante para o planejamento e controle das atividades de um projeto de teste. A gestão de testes pode ser implementada por meio de ferramentas automatizadas (test management system).

Estas ferramentas devem oferecer um repositório central e padronizado aonde os lideres de testes poderão criar suítes com os casos de teste, atribuir os casos de testes aos testadores, acompanhar o status da execução dos testes e emitir os relatórios com métricas e estatísticas.

Entre as ferramentas que auxiliam na gestão de testes temos:
  • TestLink
  • RTH
  • Testopia
  • TestMaster
  • Testitool
  • Test Case Web
  • QaManager
Entre estas as ferramentas mais comentadas foram o TestLink e o RTH. A escolha do TestLink é hoje a escolha mais apropriada, pois seu desenvolvimento é o mais ativo e por ser uma ferramenta mais utilizada no mercado.

O TestLink é uma aplicação Open Source cujo principal objetivo é gerenciar as atividades de teste de um projeto tais como test cases e test suítes. Ele é útil, pois é possível organizar os requisitos, associar os test cases aos requisitos garantindo então a rastreabilidade entre eles. Outra vantagem é a integração com a ferramenta de gestão de defeitos Mantis que será também utilizada no sistema.

Outra vantagem é o sistema ser desenvolvido em php e utilizar a base de dados mysql, que também é a plataforma utilizada por outras ferramentas do sistema o que demandará menos recursos.

Gestão de defeitos

A gestão de defeitos é uma das atividades mais importantes de um processo de teste de software. Através da gestão de defeitos podemos acompanhar a qualidade do software em teste com base nos defeitos cadastrados pelos testadores ao longo de um ciclo de testes.

Podemos então, para realizar o controle e a gestão dos defeitos no sistema, utilizar sistemas automatizados que nos auxiliem. Estas ferramentas devem possuir um local aonde os testadores cadastram os bugs de forma organizada, devem possuir também um local para acompanhar o ciclo de vida dos defeitos e emitir relatórios de gestão.

Entre as ferramentas de gestão de defeitos temos três que são bastante utilizadas no mercado. Entre elas temos o Bugzilla, o Jira e o Mantis.

Segue abaixo uma tabela de comparação entre estas ferramentas:

System








Customizable workflow

Unicode support

LDAP user authen.












Bugzilla








No[9]

Yes

Yes

JIRA








Yes

Yes

Yes

Mantis Bug Tracker








Yes

Yes

Yes


Retirado de http://en.wikipedia.org/wiki/Comparison_of_issue

O Jira será excluído da comparação, pois o projeto não é um projeto livre e sim pago para projetos Comerciais. Ficamos então com a comparação entre o Mantis e o Bugzilla.

Comparando as duas ferramentas vemos então que o Mantis é mais simples de instalar e de utilizar, e possui segundo a matriz acima um workflow configurável o que o torna mais extensível. Minha escolha cai sobre o Mantis então por sua simplicidade e também por ser bastante robusto. A simplicidade ajuda a usuários não técnicos executar o cadastro de defeitos.

Entre as diversas funcionalidades oferecidas pelo Mantis temos:

  • Pode ser executado em qualquer plataforma.
  • Suporta vários bancos de dados.
  • Suporta múltiplos mecanismos de autenticação.
  • Ciclo de vida personalizável.
  • Gerador interno de relatório de defeitos.
  • Controle de acesso e permissão por usuário.
  • Mecanismo para a criação de campos personalizáveis.
  • Notificação por meio de e-mails automáticos.
  • Integração com ferramentas de controle de versão.
  • Interface webservices para integração com outras ferramentas.

sexta-feira, 25 de janeiro de 2008

O padrão Singleton

O Singleton é um dos padrões de projeto mais simples existentes. É um padrão de projeto de software (do inglês Design Pattern). Este padrão garante a existência de apenas uma instância de uma classe, mantendo um ponto global de acesso ao seu objeto.

Nota linguística: O termo vem do significado em inglês quando se resta apenas uma carta nas mãos, num jogo de baralho.

O GoF nos diz que para uma classe ser um singleton, deve-se garantir que haverá apenas uma instância na aplicação e que deve-se fornecer um ponto de acesso à mesma. Mas como garantir que haverá apenas uma instância? É de conhecimento comum que, para criar uma instância de uma classe, devemos chamar o seu construtor. Assim, para resolvermos o problema, devemos restringir o acesso ao construtor, tornando-o um método privado. Em seguida, deve-se utilizar um método público que faça o controle da instanciação, de modo que ela só possa ser feita uma vez. A Figura 1 mostra o diagrama de classes do singleton:

Figura 1: Diagrama de Classes do Singleton.

A implementação mais comum do singleton pode ser vista no código apresentado na Listagem 1 e comumente é chamada de singleton com lazy load.

public class Singleton {
private static Singleton instance;

private Singleton() { }

public static Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
}

Implementação do Singleton.

O diagrama de classes da Figura 1 e o código da Listagem 1 mostram apenas o necessário para a instanciação do singleton. Obviamente, a classe deve conter outros atributos e métodos.
A Figura 2 mostra um diagrama de seqüências que representa o processo de criação de um singleton. Quando um objeto chama o singleton pela primeira vez, é realizada a instanciação da classe. Os demais acessos irão utilizar a instância já criada.

Figura 2: Diagrama de Seqüências de um Singleton.

O código da Listagem 1 pode ser problemático em ambientes multi-threaded, ou seja, ele não é uma solução thread-safe. Se uma thread chamar o método getInstance() e for interrompida antes de realizar a instanciação, uma outra thread poderá chamar o método e realizar a instanciação. Neste caso, duas instâncias serão construídas, o que fere os requisitos do singleton.


Uma solução para este problema seria utilizar o atributo synchronized em getInstance(), como visto na Listagem 2 , para que uma outra thread não possa acessá-lo até que a thread que o acessou pela primeira vez tenha terminado de fazê-lo.

public class Singleton {
private static Singleton instance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
}
Implementação thread-safe com synchronized.

O problema com o synchronized é que a sincronização é bastante custosa. Estima-se que métodos sincronizados sejam cerca de cem vezes mais lentos que métodos não sincronizados.

Uma alternativa simples, rápida e thread-safe é a instanciação do singleton assim que ele for declarado. Veja a Listagem 3 .

public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() { return instance; }
}
Implementação thread-safe com instanciação estática.

3. Utilizações comuns

Muitos projetos necessitam que algumas classes tenham apenas uma instância. Por exemplo, em uma aplicação que precisa de uma infraestrutura de log de dados, pode-se implementar uma classe no padrão singleton. Desta forma existe apenas um objeto responsável pelo log em toda a aplicação que é acessível unicamente através da classe singleton. Outra utilização comum é utilizar o padrão singleton sobre a fábrica de conexões com o banco.

3. Conclusões
Além de ser um dos padrões mais simples, o singleton é também um dos mais criticados e mal usados. Uma situação em que realmente é necessário que exista apenas uma instância de uma classe é difícil e o seu uso pode levar a muitos problemas. O uso abusivo de singletons leva a soluções onde a dependência entre objetos é muito forte e a testabilidade é fraca.

Adicionalmente, os singletons são difíceis de escalar, pois é difícil garantir uma única instância de um singleton em um cluster, onde existem várias JVMs. Um outro problema é que os singletons dificultam o hot redeploy por permanecerem em cache, bloqueando mudanças de configuração. Por esses e outros problemas, deve-se ter cuidado com o uso abusivo de singletons.

Padrões de projeto de software

Os padrões de projeto de software ou padrões de desenho de software, também muito conhecido pelo termo original em inglês: Design Patterns, descrevem soluções para problemas recorrentes no desenvolvimento de sistemas de software orientados a objetos. Um padrão de projeto estabelece um nome e define o problema, a solução, quando aplicar esta solução e suas conseqüências.

Os padrões de projeto visam facilitar a reutilização de soluções de desenho - isto é, soluções na fase de projeto do software, sem considerar reutilização de código. Também acarretam um vocabulário comum de desenho, facilitando comunicação, documentação e aprendizado dos sistemas de software.

Um Padrão de Projeto pode ser definido como “a solução para um problema em um contexto”. Isto significa que os padrões de projeto nos oferecem soluções prontas para utilizarmos em determinados problemas que podemos enfrentar quando desenvolvemos um software orientado a objetos.

Para realizar a descrição de um padrão de projeto devemos especificar o seu nome, que o identifica e descreve; o contexto, ou seja, a situação que está gerando se está na qual se deve aplicar o padrão; o problema, que é a meta que está se tentando atingir neste contexto; a solução proposta pelo padrão para o problema no contexto especificado e as conseqüências da aplicação do padrão, podendo ser elas boas ou ruins. Para compreender melhor esse conceito, veja o exemplo abaixo, que não pode ser considerado um padrão de projeto, servindo apenas para ajudar a compreender o modo como um padrão de projeto deve ser definido.

  • Nome: Chegar à reunião!
  • Problema: Como chegar no horário à reunião?
  • Contexto: Você está trancado dentro de casa!
  • Solução: Pule a janela, entre no carro e corra até o trabalho.
  • Conseqüências: Você deve achar a chave ou pular a janela para entrar em casa.

Podemos classificar os padrões de projeto de várias maneiras, porém o mais comum é classificá-los de acordo com os tipos de problemas que eles resolvem. De acordo com esse critério, os padrões podem ser:

  • Criação: resolvem os problemas da criação de objetos;
  • Estruturais: que lidam com os problemas de relacionamentos entre objetos;
  • Comportamentais: que lidam com os problemas de atribuição de responsabilidades aos objetos.

Os padrões de projeto solucionam muitos dos problemas que os projetistas enfrentam no dia a dia, e de diversas maneiras diferentes. Nesta série de post tentarei mostrar alguns dos padrões mais comuns, explicando o contexto, o problema, a solução e suas conseqüências com exemplos simples e acessíveis. Todos os exemplos estarão escritos na linguagem Java, mas são aplicáveis em qualquer linguagem orientada a objetos como .Net, Delphi, C++ ou outras.