Mostrando postagens com marcador testes. Mostrar todas as postagens
Mostrando postagens com marcador testes. Mostrar todas as postagens

quarta-feira, 3 de março de 2010

Objetos fake em diversas linguagens para divertir minha vó

Quase todas as linguagens que trabalhei possuem ferramentas para criar objetos fakes e assim auxiliar na construção de testes. Mas e se essas ferramentas não existissem? Estaríamos perdidos? Claro que não! Mocks e stubs podem ser criados de diversas formas diferentes nas diversas linguagens.

Esse é um ótimo assunto pra se discutir em um jantar de família. Quer coisa mais divertida que explicar pra sua avó como construir objetos fake em diversas linguagens? Por exemplo um simples stub que conta quantas vezes um método que notifica visualização de um filme em um serviço externo é chamado. Algo similar ao que o código abaixo faz. Diversão garantida!

servico.should_receive(:notificar_visualizacao!).with(filme).once

Em Ruby

Em ruby podemos simplesmente criar uma nova classe em tempo de execução ou adicionar um método a um objeto qualquer. É a maneira mais simples de garantir um sorrisão da sua vó tamanha facilidade. No caso do exemplo abaixo resolvi adicionar métodos a um objeto qualquer e ao final executo o teste utilizando o rspec.

servico_fake = Object.new
servico_fake.instance_eval do
def notificar_visualizacao!(filme)
@filme_visualizado = filme
@quantidade_visualizacoes = quantidade_visualizacoes + 1
end
def filme_visualizado
@filme_visualizado
end
def quantidade_visualizacoes
@quantidade or 0
end
end

filme = Filme.new
filme.servico = servico_fake
filme.visualizar!

servico_fake.filme.should be_equal(filme)
servico_fake.quantidade.should == 1

Em Python

Em Python é possivel alterar métodos de instancias em tempo de execução, mas não é possivel adicionar métodos a um objeto da classe object. Neste momento minha vozinha fica decepcionada. Para fazer algo semelhante ao que fizemos em ruby teríamos então que instanciar um objeto da classe original e só depois modificar o método. Ficaria mais ou menos assim:

filme_visualizado = None
quantidade_visualizacoes = 0

def notificar_visualizacao_fake(filme):
global filme_visualizado, filme_visualizado
filme_visualizado = filme
quantidade_visualizacoes += 1

servico_fake = ServicoExterno()
servico_fake.notificar_visualizacao = notificar_visualizacao_fake

Esta opção passa a ser ruim quando o construtor da classe original executa algumas tarefas que são custosas para o nosso teste. Por isso, acho que em python o melhor para o este caso é criar uma classe em tempo de execução, deixando minha vó um pouco mais alegre, conforme exemplo abaixo:

class ServicoFake(object):
def __init__(self):
self.filme_visualizado = None
self.quantidade_visualizacoes = 0
def notificar_visualizacao(self,filme):
self.filme_visualizado = filme
self.quantidade_visualizacoes += 1

servico_fake = ServicoFake()

filme = Filme()
filme.servico = servico_fake
filme.visualizar()

assert servico_fake.filme_visualizado is filme
assert servico_fake.quantidade_visualizacoes == 1

Em Java

Uma forma "simples" de fazer em Java é criando uma nova classe para herdar a original e sobrescrever somente o método desejado. Mas aí cairíamos no mesmo problema que discutimos sobre um possível construtor com código muito custoso na superclasse. E minha vó, que apesar de repetir muitas vezes as mesmas histórias, não gosta de ouvir as nossas repetidas.

Uma alternativa que temos é extrair uma interface da classe original para que em nosso teste a gente possa implementar essa interface da maneira que quisermos. Ficaria mais ou menos assim:

//ServicoExterno.java
public interface ServicoExterno {
void notificarVisualizacao(Filme filme);
}

//ServicoExternoHTTP.java
public class ServicoExternoHTTP implements ServicoExterno {
public ServicoExternoHTTP() {
//Faz um monte de coisas
}
public void notificarVisualizacao(Filme filme) {
//Faz mais coisas ainda
}
}

Então em nosso teste a gente cria uma classe implementando a interface recém criada:

public class ServicoExternoFake implements ServicoExterno {
public int quantidade_visualizacoes = 0;
public Filme filme_visualizado = null;

public void notificarVisualizacao(Filme filme) {
filme_visualizado = null;
quantidade_visualizacoes++;
}
}

E depois, mesmo que neste ponto minha vó já esteje dormindo, a gente utiliza a classe fake criada no teste:

ServicoExternoFake servicoFake = new ServicoExternoFake();

Filme filme = new Filme();
filme.setServico(servicoFake);
filme.visualizar();

assertSame(filme,servicoFake.filme_visualizado);
assertEquals(1,servicoFake.quantidade_visualizacoes);

Parece que minha avó não gostou da história. Ela começou tão dinâmica e foi ficando cadas vez mais devagar. Sem dúvida eu deveria ter contato ao contrário.

terça-feira, 9 de fevereiro de 2010

Upload de arquivo com selenium server no firefox

Para fazer upload de arquivo com selenium é preciso alterar seu selenium-server.jar liberando permissão para que seja possível manipular campos do tipo file por javascript. Para fazer isso, extraia o selenium-server.jar:

jar -xvf selenium-server.jar

Crie um arquivo chamado user.js no diretório customProfileDirCUSTFF contendo um código semelhante com o exibido abaixo:

user_pref("signed.applets.codebase_principal_support", true);

user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p0.id", "http://localhost");
user_pref("capability.principal.codebase.p0.subjectName", "");

Repare que a liberação de acesso é feita por host. No caso do exemplo estou liberando apenas para o ambiente local. Se desejar liberar outros hosts baixa adicionar outros conforme o exemplo abaixo:

user_pref("signed.applets.codebase_principal_support", true);

user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p0.id", "http://localhost");
user_pref("capability.principal.codebase.p0.subjectName", "");

user_pref("capability.principal.codebase.p1.granted", "UniversalFileRead");
user_pref("capability.principal.codebase.p1.id", "http://globo.com");
user_pref("capability.principal.codebase.p1.subjectName", "");

Feito isso, basta gerar novamente o jar e executá-lo.

jar cvfm selenium-server.jar ./META-INF/MANIFEST.MF -C ./ .
java -jar selenium-server.jar

Já será possivel preencher o path de qualquer arquivo no campo como se ele fosse apenas um campo de textos. Isso no Firefox é claro.

quinta-feira, 9 de julho de 2009

Teste de aceitação automático com múltiplas configurações no rails

Dado que uma aplicação possua duas maneiras ou mais de funcionar de acordo com alguns parâmetros de inicialização, como proceder com os testes de aceitação automáticos sem ter que iniciar e parar o servidor entre cada cenário ou grupo de cenários?

A solução simples, mas bem funcional que adotamos, foi ter uma task rake para inicializar vários servidores no ambiente local, cada um com um environment e uma porta diferente. Assim, ao testar uma determinada funcionalidade com determinada configuração basta acessar o servidor naquela porta.

A task rake que poderia até mesmo ser um shell script, ficou mais ou menos como mostrado abaixo. Créditos ao Tiago PacMan, mestre em shell, que fez a linha que finaliza os servidores.


desc 'Inicia servidores para teste de aceitação'
task "server:test" do
system 'script/server -d -e test_conf_1 -p 3001'
sleep 1
system 'script/server -d -e test_conf_2 -p 3002'
sleep 1
system 'tail -f log/test.log'
system "ps aux | awk '/3001/{print $2}' | xargs kill -9"
sleep 1
system "ps aux | awk '/3002/{print $2}' | xargs kill -9"
sleep 1
end

Repare que para este caso fizemos uma modificação no environments.rb para unificar os logs de todas os ambientes de teste.

Já no ambiente de integração contínua, onde utilizamos apache com passenger, foi mais simples ainda, bastando definir para diferentes VirtualHosts RailsEnvs diferentes.

quinta-feira, 16 de abril de 2009

Esperando o resultado de uma chamada assíncrona no Watir

Em alguns casos quando uma determinada requisição assíncrona demora para retornar, o teste de aceitação implementado com Watir pode falhar. Principalmente quando os testes estão rodando em alguma ferramenta de integração contínua.

Para evitar essa falha irritante, o usual é colocar alguns sleeps após as ações que disparam as chamadas ajax. Contudo, isso não garante que o teste não vá falhar, especialmente se o sleep for baixo. Em contrapartida se forem colocado muitos sleeps altos poderá haver uma demora muito grande para rodar todos os testes.

Uma solução possível é esperar que um determinado elemento html seja inserido ou removido da página para então continuar executando os testes. Para isso implementei um método genérico que recebe um bloco de verificação. Veja o exemplo de uso dele:

espera { @browser.text.include? 'Ajax retornado' }

O código do método é bem simples e está descrito abaixo. Ele recebe um bloco assumindo que quando este retornar verdadeiro significa que a espera deve terminar, caso contrário ele continuará esperando e verificando, com um timeout de 30 segundos.

def espera
Timeout::timeout(30) do
while not yield
sleep 1
end
end
end

quinta-feira, 26 de fevereiro de 2009

Duck typing e os testes de aceitação automáticos

Uma discussão frequente em minha equipe é sobre maneiras de garantir que nossos sistemas estejam funcionando adequadamente por meio de testes automáticos. Em nossos ultimos projetos temos conseguido alcançar 100% de cobertura de testes unitários, e nos sistemas legados temos aumentado a cobertura aos poucos.

No entanto, estamos cientes de que nem 100% de cobertura de testes unitários garantem a ausência de bugs. Isso nos remete aos testes de aceitação automáticos, não só por causa do funcionamento das telas e fluxos de navegação, mas também dos possíveis problemas que um refactoring com um pouco menos de atenção pode causar em uma linguagem baseada em duck typing.

Darei um exemplo em ruby para ilustrar um bug que pode ser introduzido sem que seus testes unitários percebam. Digamos que você tenha as seguintes classes:

class Filme < ActiveRecord::Base do
def alugar
self.estoque.remover self
end
end
class Estoque < ActiveRecord::Base do
def remover(filme)
# ...
end
end

No seu spec que verifica o método alugar da classe Filme você colocaria um mock mais ou menos como mostrado abaixo:

@estoque = mock_model(Estoque)
@estoque.should_receive(:remover).with(@filme).once

Um dia você percebe que o nome do método remover não está explicando muito bem o significado, e resolve fazer um refactoring renomeando-o para remover_fita_do_filme.

Você altera os testes da classe Estoque, renomeia o método e logo depois recebe uma ligação de uma empresa de telefonia te oferecendo serviços excepcionais que você nunca precisou. Puto da vida você desliga o telefone, roda os testes e todos passam! Serviço feito!

Perceberam o problema? Sem um teste de aceitação automático ou no mínimo um teste manual, você não perceberia que a classe Filme continua referenciando o método antigo, remover, da classe Estoque. Esse tipo de problema não passaria em uma linguagem que não segue duck typing, pois a etapa de verificação de código da compilação serviria como uma espécie de teste unitário de tipos e nomes.

Um problema mais evidente seria um método que recebe um objeto que precisa ter determinado método, ou seja implementar determinada interface. Veja o exemplo abaixo, que mostra que ao alugar um item, no caso um Filme, adiciona-se todas as tags dele às tags preferenciais do cliente:

class Cliente < ActiveRecord::Base do
def alugar(item)
#...
adiciona_tags_preferenciais item.tags
end
end
class Filme < ActiveRecord::Base do
def tags
# ...
end
end

Se mudarmos o nome do método tags em Filme e também corrigirmos o uso de dele no método alugar da classe Cliente tudo funcionará bem. Mas, como usamos duck typing não temos como garantir que outros objetos, digamos alugáveis, tenham sido alterados. Se por exemplo esta locadora também alugas livros, teríamos um erro evidente:

class Cliente < ActiveRecord::Base do
def alugar(item)
#...
adiciona_tags_preferenciais item.tags_principais
end
end
class Filme < ActiveRecord::Base do
def tags_principais
# ...
end
end
class Livro < ActiveRecord::Base do
def tags
# ...
end
end

Esse tipo de erro só seria pego num teste manual se lembrássemos de testar o aluguel de livros também. Por isso é tão importante o teste de aceitação automático em uma linguagem com duck typing. Em Java por exemplo, que a interface precisa ser explícita, o erro seria pego no que poderíamos considerar o teste unitário que a compilação executa. As classes Filme e Livro implementariam a interface Alugavel por exemplo:

public interface Alugavel {
List tagsPrincipais();
}

Mas é claro que o uso de uma liguagem menos dinâmica não remove a necessidade de testes de aceitação automáticos. O valor desse tipo de testes é independente da linguagem, e sua implementação é importantíssima para garantir fluxos de navegação e integração entre os diversos componentes do sistema.

terça-feira, 1 de abril de 2008

Mock de classe no PHPUnit executa o construtor

O mock de classes no PHPUnit não funciona muito bem, o ideal, claro é mocar interfaces, mas quando isso não é possível é preciso estar ciente de que o construtor da classe mocada será executado e portanto o teste ficará um tanto falho.

É possivel identificar facilmente esse problema com o código abaixo. No caso mocarei a classe ClasseUtilizada para ser utilizada na classe ClasseASerTestada. Ao executar o teste é possível verifique que o echo dentro do construtor será executado erroneamente.
class ClasseUtilizada {   
function ClasseUtilizada() {
echo "\n[passou no construtor de ClasseUtilizada]\n";
}
public function fazAlgumaCoisa() {
return "uhuuu";
}
}
class ClasseASerTestada {
function ClasseASerTestada($in) {
echo "\n[" . $in->fazAlgumaCoisa() . "]\n";
}
}
class MeuTeste extends PHPUnit_Framework_TestCase {
/**
* @Test
*/
public function teste_com_classe() {
$mock = $this->getMock("ClasseUtilizada");
$mock->expects($this->once())
->method('fazAlgumaCoisa')
->will($this->returnValue(2));
new ClasseASerTestada($mock);
}
}
Uma forma de contornar isso é se aproveitar das características dinâmicas do PHP para simplesmente criar um Mock de uma classe que não existe. Mas pra isso é preciso informar no método getMock não só o nome de uma classe inexistente, como também a lista de métodos que a classe teria. Veja como ficaria abaixo:
    /**
* @Test
*/
public function teste_com_classe_inexistente_indicando_metodos() {
$mock = $this->getMock("ClasseUtilizadaMock", Array('fazAlgumaCoisa'));
$mock->expects($this->once())
->method('fazAlgumaCoisa')
->will($this->returnValue(4));
new ClasseASerTestada($mock);
}
Se por um acaso você não indicar o segundo parâmetro, que é um array com os métodos que a classe possui, um erro parecido com este será exibido: "Fatal error: Call to undefined method Mock_ClasseUtilizadaMock_4322e2fd :: fazAlgumaCoisa()".

O problema dessa solução é o caso em que a classe a ser utilizada possui verificação de tipo no recebimento dos parâmetros. Como a classe mock criada dinamicamente não é do tipo esperado, ocorrerá um erro. Veja um exemplo de como isso aconteceria abaixo:
class ClasseUtilizada {   
function ClasseUtilizada() {
echo "\n---->passou no construtor de ClasseUtilizada<-----\n";
}
public function fazAlgumaCoisa() {
return "uhuuu";
}
}
class ClasseASerTestadaComVerificacaoDeTipo {
function ClasseASerTestada(ClasseUtilizada $in) {
echo "\n[" . $in->fazAlgumaCoisa() . "]\n";
}
}
class MeuTeste extends PHPUnit_Framework_TestCase {
/**
* @Test
*/
public function teste_com_classe_inexistente_na_classe_com_verificacao_de_tipo() {
$mock = $this->getMock("ClasseUtilizadaMock", Array('fazAlgumaCoisa'));
$mock->expects($this->once())
->method('fazAlgumaCoisa')
->will($this->returnValue(5));
new ClasseASerTestadaComVerificacaoDeTipo($mock);
}
}
Nesse caso o erro exibido será "Argument 1 passed to ClasseASerTestadaComVerificacaoDeParametro :: ClasseASerTestadaComVerificacaoDeParametro() must be an instance of ClasseUtilizada"

Para esses casos não tem jeito, é preciso alterar as classes para que exista uma interface a ser mocada. Mas eu sugiro que não utilize a verificação de tipo nos parâmetros, caso contrário, seria melhor deixar de lado uma linguagem dinâmica como PHP e usar uma mais estática como Java.

quarta-feira, 22 de agosto de 2007

Selenium IDE aplicado a interfaces com Ajax

Fazer testes automatizados com o Selenium IDE numa interface web que possui chamadas assíncronas pode se tornar um problemão. Um exemplo simples é de um link que buscaria através de HttpRequest um recurso no servidor:

<a href="javascript:void(buscarDados())">Clique aqui</a>
Neste caso para testar o clique no link usando o selenium temos dois problemas. Se usarmos a função click o teste seguinte poderá falhar caso o servidor demore alguns segundos para responder. E se usarmos a função clickAndWait ocorrerá um timeout pois o selenium espera que a página inteira mude.

Para resolver esse tipo de situação é que existem as funções iniciadas por wait. Elas aguardam um bom período até que determinado elemento ou texto sejam impressos na página. Dessa forma podemos utilizar a função click e posteriormente fazer aguardar por determinado recurso, o que indicaria que o servidor respondeu.

No caso do nosso exemplo a função de callback do ajax, ao receber os dados do servidor imprime uma tag H1 do html com o texto "Dados do servidor". Dessa forma o teste ficaria na seguinte ordem:

open/teste.html
clicklink=Clique aqui
waitForElementPresent//h1
verifyText//h1Dados do servidor

Repare que após o clique no link, utilizamos a função waitForElementPresent para aguardar que o html tenha inserido uma tag h1 em qualquer parte dele. Depois que este é impresso eu utilizo a função verifyText para verificar se há alguma tag h1 inserida no html que possua o texto "Dados do servidor".

A versão do Selenium IDE utilizada é 0.8.7 e a versão do Firefox 2.0.0.1

quarta-feira, 16 de maio de 2007

Teste unitário em funções javascript utilizando o JUnit

Um dos grandes problemas do javascript é a falta de testabilidade. Vou mostrar aqui nesse artigo uma forma que encontrei de testar as funções de arquivos javascripts utilizando o JUnit. Ah! Claro, isso só é possível se você estiver utilizando o Java 6, que possui suporte a linguagens de script e já vem nativamente com suporte a javascript.

Primeiro criamos o arquivo funcoes.js contendo uma função para validar um determinado formulário. O objetivo é retornar verdadeiro se todos os campos estiverem preenchidos e falso no caso contrário. O formulário html deve ter dois campos: nome e senha. Inicialmente deixaremos essa função sem nenhum código, pois criaremos os testes primeiro:
function validaFormulario(f)
{
return false;
}

Em seguida criamos uma classe para realizar os testes unitários contendo o método testValidaFormulario e outras duas classes internas para servir de Mock do formulário html e dos campos do formulário, HtmlFormMock e HtmlFieldMock respectivamente. Segue o código:
package junit.script;
import junit.framework.TestCase;

public class FuncoesTest extends TestCase
{
public void testValidaFormulario() throws FileNotFoundException, ScriptException, NoSuchMethodException
{
//Aqui entrarão os testes
}

public class HtmlFormMock implements Serializable
{
public HtmlFieldMock nome = new HtmlFieldMock();
public HtmlFieldMock senha = new HtmlFieldMock();
}
public class HtmlFieldMock implements Serializable
{
public String name;
public String value;
}
}

No método testValidaFormulario da classe de testes começamos então importanto o script funcoes.js, e criando um Invocable para poder executar funções no javascript:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
engine.eval( new FileReader( "C:/Projetos/testes/web/funcoes.js" ) );
Invocable invocable = (Invocable) engine;

Em seguida preenche-se o Mock de formulário da seguinte forma:
HtmlFormMock f1 = new HtmlFormMock();
f1.nome.name = "nome";
f1.nome.value = "Tiago";
f1.senha.name = "senha";
f1.senha.value = "123456";

Tendo o invocador de funções de script (Invocable) e o objeto mock, basta alterar os atributos deste objetos para utilizá-lo como parâmetro do formulário por diversas vezes. Veja abaixo os testes que defini para esta função, repare que fiz dois testes para verificar se o campo "nome" e o campo "senha" existem no formulário, pois é possível que criem um formulário html sem o campos:

//Todo formulario preenchido
Boolean b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertTrue("Formulario todo preenchido deve retornar true", b.booleanValue());

//Senha preenchida com nulo
f1.senha.value = null;
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Senha preenchida com nulo deve retornar falso", b.booleanValue());

//Senha preenchida com vazio
f1.senha.value = "";
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Senha preenchida com vazio deve retornar falso", b.booleanValue());

//Nome preenchido com nulo
f1.senha.value = "outra";
f1.nome.value = null;
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Nome preenchido com nulo deve retornar falso", b.booleanValue());

//Nome preenchido com vazio
f1.nome.value = "";
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Nome preenchido com vazio deve retornar falso", b.booleanValue());

//Campo nome do formulario nao existir deve retornar falso
f1.nome = null;
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Campo nome do formulario nao existe entao deve retornar falso", b.booleanValue());

//Campo senha nao pode ser nulo
f1.nome = new HtmlFieldMock();
f1.nome.name = "nome";
f1.nome.value = "Tiago";
f1.senha = null;
b = (Boolean) invocable.invokeFunction("validaFormulario", new Object[] { f1 } );
assertFalse("Campo senha do formulario nao existe entao deve retornar falso", b.booleanValue());

Executando esse teste com o JUnit teremos uma falha logo no primeiro teste, pois o este espera que haja um retorno de true e nossa função javascript está sempre retornando false. Alteremos então a função javascript dando-lhe um código aceitável:
function validaFormulario(f)
{
if( f.nome && f.nome.value && f.nome.value.length() > 0 &&
f.senha && f.senha.value && f.senha.value.length() > 0 )
{
return true;
}
return false;
}

Pronto, tendo a função javascript codificada, basta rodarmos o teste no JUnit novamente para verificar que finalmente a função está funcionando de acordo com o específicado. E o melhor, sempre que ocorrer uma alteração no código dela, saberemos se houve alguma quebra de contrato.

Também é possível testar métodos de classes javascript. Basta utilizar o método invokeMethod da interface Invocable, ao invés de invokeFunction. Mas isso eu deixo pra outro artigo.