Hoje lendo o blog do Nerdson (muito bacana), vi um negócio engraçado e que se tornou mais engraçado pelo fato de conhecer um caso real que usou esta técnica.
Numa empresa que trabalhei anteriormente, tinha um brother recém-promovido à programador (Ou anapropégua* como preferir). Acontece que este carinha tinha um gerente super-star, que se parecia muito com o Dragãozinho do Nerdson.
O pobre infeliz (do programador), estava com a corda no pescoço, com uns 3 projetos em atraso e 1 que tinha que terminar naquele mesmo dia, desta forma, 18:30 hs na mesa do programador estavam: O próprio, O "coordenador" dele, e o Gerente Super-Star.
O pobre programador chegou a um momento de quase um AVC e choramingou: "Vou la fora dar uma respirada, e volto logo". O gerente então lasca: "Sente aí e quando você terminar, pode respirar!".
Veja o Post do Nerdson para ter uma ilustração desta técnica incrível.
*anapropégua = ANAlista, PROgramador e fí duma ÉGUA.
segunda-feira, 30 de março de 2009
sexta-feira, 20 de março de 2009
Reparando uma injustiça com a Integração Automatizada.
A algum tempo atrás crucifiquei o Sr. Cruise Control, na verdade não a ferramenta, mas o fato de automatizar esta integração. Porém, esses dias vi um novo cenário (que eu não tinha pensado) e que a automatização da Integração pode ser um GRANDE aliado.
Imagine você que seu desenvolvimento esteja tão, ou simplesmente, evoluído o bastante para termos várias "formas" de testes automatizados. Imagine que um de seus testes seja algo como, apagar TODA uma base de dados, e popula-la novamente com os dados mais atuais da aplicação, e só depois executar uma bateria de testes automatizados.
O bom e velho Build de 10 minutos vai pra roça !!!
Considere o exemplo acima apenas um EXEMPLO, pois podem existir várias situações em que sua integração ultrapasse, em muito, o tempo de 10 minutos.
Resumindo, você deve ter "2 momentos" de integração:
* A integração local: que seria a integração realizada em sua maquina, fazendo os testes unitários e, possivelmente, testes de integração;
* E o momento da integração automatizada, que conta com todas as vantagens da ferramenta, como relatórios e afins.
Imagine você que seu desenvolvimento esteja tão, ou simplesmente, evoluído o bastante para termos várias "formas" de testes automatizados. Imagine que um de seus testes seja algo como, apagar TODA uma base de dados, e popula-la novamente com os dados mais atuais da aplicação, e só depois executar uma bateria de testes automatizados.
O bom e velho Build de 10 minutos vai pra roça !!!
Considere o exemplo acima apenas um EXEMPLO, pois podem existir várias situações em que sua integração ultrapasse, em muito, o tempo de 10 minutos.
Resumindo, você deve ter "2 momentos" de integração:
* A integração local: que seria a integração realizada em sua maquina, fazendo os testes unitários e, possivelmente, testes de integração;
* E o momento da integração automatizada, que conta com todas as vantagens da ferramenta, como relatórios e afins.
sexta-feira, 17 de outubro de 2008
sábado, 13 de setembro de 2008
Novas atividades
Moçada,
sei que estou meio ausente, e voltar a ativa com um post que não tem nada haver com o blog é desastroso, mas tenho um bom motivo:
A mais de 2 meses venho estudando Ruby on Rails, direcionei todas as minhas atenções e horarios vagos para isso e estou percebendo que devo dedicar mais tempo ainda. Bem, o caso é que criei um novo blog pra falar sobre Ruby on Rails, e venho convidar a todos que se interessem pelo assunto a visita-lo. Bem, os assuntos que se relacionem com testes eu postarei nos 2 blogs, então se não se interessar por Rails, pode aguardar os posts sobre testes aqui mesmo.
Até mais
sei que estou meio ausente, e voltar a ativa com um post que não tem nada haver com o blog é desastroso, mas tenho um bom motivo:
A mais de 2 meses venho estudando Ruby on Rails, direcionei todas as minhas atenções e horarios vagos para isso e estou percebendo que devo dedicar mais tempo ainda. Bem, o caso é que criei um novo blog pra falar sobre Ruby on Rails, e venho convidar a todos que se interessem pelo assunto a visita-lo. Bem, os assuntos que se relacionem com testes eu postarei nos 2 blogs, então se não se interessar por Rails, pode aguardar os posts sobre testes aqui mesmo.
Até mais
quinta-feira, 5 de junho de 2008
Fóruns que precisamos conhecer (Sobre Testes)
Em desenvolvimento, dúvidas são frequentes, por isso vou listar alguns lugares (Fóruns sobre teste e utilitários) que podemos encontrar soluções para os mais diversos problemas e mesmo encontrar respostas para nossas novas dúvidas.
EclEMMA: https://sourceforge.net/forum/forum.php?forum_id=614869
EMMA: http://sourceforge.net/forum/forum.php?forum_id=373865
JMock: http://www.jmock.org/mailing-lists.html
JMockit: https://jmockit.dev.java.net/servlets/SummarizeList?listName=users
JUnit: http://www.nabble.com/JUnit-f2687.html
Selenium: http://www.nabble.com/Selenium-f14083.html
XP-Rio (Sobre práticas ágeis, TDD, outras formas de testar software): http://tech.groups.yahoo.com/group/xprio/
EclEMMA: https://sourceforge.net/forum/forum.php?forum_id=614869
EMMA: http://sourceforge.net/forum/forum.php?forum_id=373865
JMock: http://www.jmock.org/mailing-lists.html
JMockit: https://jmockit.dev.java.net/servlets/SummarizeList?listName=users
JUnit: http://www.nabble.com/JUnit-f2687.html
Selenium: http://www.nabble.com/Selenium-f14083.html
XP-Rio (Sobre práticas ágeis, TDD, outras formas de testar software): http://tech.groups.yahoo.com/group/xprio/
Resolvendo o problema do finally no EclEMMA
A algum tempo reclamei do meu colega EclEMMA aqui, acontece que fiz uma pergunta num forum do produto, e mesmo com o meu inglês digno de um índio, obtive uma boa resposta:
"Hi, the yellow line is an artifact of the underlying coverage technology which is based on byte code instrumentation. "Finally" statements only exist in Java source code, the Java compiler creates corresponding control structured in byte code. The finally block is actually executed in two different cases: In case of normal execution and in case of an exception within the try block. You're test code follows only one the these paths, therefor the line is marked yellow (partial execution)."
Muito bacana, dessa forma percebi que tenho que deixar o método disparar a exceção, caso ela aconteça, e tenho que verifica-la no método de teste.
Usando o mesmo exemplo, eis a forma correta de testar e obter o resultado correto com o EclEMMA :
"Hi, the yellow line is an artifact of the underlying coverage technology which is based on byte code instrumentation. "Finally" statements only exist in Java source code, the Java compiler creates corresponding control structured in byte code. The finally block is actually executed in two different cases: In case of normal execution and in case of an exception within the try block. You're test code follows only one the these paths, therefor the line is marked yellow (partial execution)."
Muito bacana, dessa forma percebi que tenho que deixar o método disparar a exceção, caso ela aconteça, e tenho que verifica-la no método de teste.
Usando o mesmo exemplo, eis a forma correta de testar e obter o resultado correto com o EclEMMA :
1: public class StupidClass {e seu teste fica assim:
2:
3: public String falar() throws Exception{
4:
5: try
6: {
7: System.out.println("try: ");
8: System.out.println("oyeah!");
9: }
10: catch (Exception e)
11: {
12: System.out.println("catch");
13: e.printStackTrace();
14: throw e;
15: }
16: finally
17: {
18: System.out.println("ogou!");
19: }
20:
21: return "oh! fezes";
22: }
23: }
0: import static org.junit.Assert.assertEquals;Obrigado ao pessoal do EclEMMA.
1: import static org.junit.Assert.assertNotNull;
2:
3: import org.junit.Before;
4: import org.junit.Test;
5:
6: public class TestStupidClass {
7:
8: StupidClass stupidClass = null;
9:
10: @Before
11: public void antes(){
12: stupidClass = new StupidClass();
13: }
14:
15: @Test
16: public void falarTest() throws Exception{
17:
18: assertEquals("Test 1", stupidClass.falar(), "oh! fezes");
19:
20: Exception exp = null;
21:
22: try{
23: stupidClass.falar();
24: }catch (Exception e) {
25: exp = e;
26: }
27:
28: assertNotNull("Test 2", exp);
29:
30: }
31: }
terça-feira, 13 de maio de 2008
O que o cliente quer, o que você entrega
Essa figura é bem manjada, mas ilustra muito bem o que acontece com o desenvolvimento de software. As metodologias ágeis vieram ao mundo pra tentar reduzir essa distancia entre o que o "cliente quer" e o que você entrega, algumas práticas como Interações Curtas e Testes podem te ajudar a estar sempre mais próximo de atingir os seus objetivos.
segunda-feira, 12 de maio de 2008
Teste de Recuperação
Depois de um email que me foi enviado pela Roberta Laquini, questionando alguns assuntos, procurei saber um pouco sobre Testes de Recuperação, bem, nunca ouvi falar e depois que vi uma fonte sobre o assunto percebi porque isso não é muito comum:
"Avalia o comportamento do sistema após ocorrência de erro, sobrecarga ou condições anormais, como queda de luz e rede. Nestes testes são tipicamente simulados cenários inesperados da mesma forma que ocorrem nos ambientes reais, como a remoção forçada de um cabo de rede, o desligamento repentino da energia, etc."
Imagina aí um cara desenvolvendo e indo debaixo da mesa puxar o cabo de força, mas é isso mesmo. :)
"Avalia o comportamento do sistema após ocorrência de erro, sobrecarga ou condições anormais, como queda de luz e rede. Nestes testes são tipicamente simulados cenários inesperados da mesma forma que ocorrem nos ambientes reais, como a remoção forçada de um cabo de rede, o desligamento repentino da energia, etc."
Imagina aí um cara desenvolvendo e indo debaixo da mesa puxar o cabo de força, mas é isso mesmo. :)
quarta-feira, 7 de maio de 2008
JMockit - RealMethodNotFoundForMockException
Existe um erro bem comum quando usamos JMockit: "Corresponding real methods not found for the following mocks".
Por um erro, meu, de tradução, imaginava que esse erro devia-se a um método da classe real não estar sendo criado na minha classe Mock, quando li com calma hoje pela manhã, percebi que é o contrário, um método implementado na minha classe Mock não existe na classe real.
Esse erro é comum de acontecer quando usamos uma classe de terceiro, normalmente empacotada num jar. No meu caso, tentava mockar a classe WebApplicationContextUtils do Spring e tive que procurar o código fonte no google pra saber a assinatura do método.
Percebi também que se você não criar o método falso para um método da classe real, ele vai invocar o método da classe real.
Por um erro, meu, de tradução, imaginava que esse erro devia-se a um método da classe real não estar sendo criado na minha classe Mock, quando li com calma hoje pela manhã, percebi que é o contrário, um método implementado na minha classe Mock não existe na classe real.
Esse erro é comum de acontecer quando usamos uma classe de terceiro, normalmente empacotada num jar. No meu caso, tentava mockar a classe WebApplicationContextUtils do Spring e tive que procurar o código fonte no google pra saber a assinatura do método.
Percebi também que se você não criar o método falso para um método da classe real, ele vai invocar o método da classe real.
Problema com o CGLIB
O CGLIB foi descoberto quando procurávamos mockar classe concretas , ele é muito útil em muitas situações, principalmente quando usamos classes de outros. Acontece que esta semana descobrimos um probleminha nele: A classe que será mockada precisa do construtor default.
O interessante é que ele executa o construtor default quando o método mock(Class) é invocado. Isso pode ser um problema, no meu caso, a classe é de terceiro e não tem o construtor default, mas acredito que o CGLIB vai continuar sendo a alternativa pra esse tipo de situação.
O interessante é que ele executa o construtor default quando o método mock(Class) é invocado. Isso pode ser um problema, no meu caso, a classe é de terceiro e não tem o construtor default, mas acredito que o CGLIB vai continuar sendo a alternativa pra esse tipo de situação.
segunda-feira, 5 de maio de 2008
Send us code. Get back tests. Free.
Dica de pesquisa.
O Igo deu uma dica de pesquisa bem interessante, como passei o dia tendo problemas com a rede e não consegui testar, envio a dica pra, caso alguém queira e tenha tempo, testar esse cara.
JUnitFactory promete gerar os testes para o seu código legado, com uma API de Mock Objects diferente das mais populares de mercado, pode ser uma boa alternativa pra quem tem interesse em gerar grande quantidade de testes para código já existente.
Aqui uma demonstração de como usar o JUnitFactory.
O Igo deu uma dica de pesquisa bem interessante, como passei o dia tendo problemas com a rede e não consegui testar, envio a dica pra, caso alguém queira e tenha tempo, testar esse cara.
JUnitFactory promete gerar os testes para o seu código legado, com uma API de Mock Objects diferente das mais populares de mercado, pode ser uma boa alternativa pra quem tem interesse em gerar grande quantidade de testes para código já existente.
Aqui uma demonstração de como usar o JUnitFactory.
quinta-feira, 24 de abril de 2008
EclEMMA e seus problemas com o finally
Hoje tive a maior decepção do ano, logo com o EclEMMA, um plugin que tinha como extremamente confiável.
Para ilustrar o problema vou fazer um exemplo bem simples (desculpem os termos usados, mas estou meio irritado com isso):
Pra quem não sabe o que significa o amarelo ou verde ou vermelho, veja aqui.
Agora é buscar outra ferramenta que atenda essa necessidade, quando, e se, achar, posto aqui.
Para ilustrar o problema vou fazer um exemplo bem simples (desculpem os termos usados, mas estou meio irritado com isso):
0:E seu teste:
1: public class StupidClass {
2:
3: public String falar(){
4:
5: try
6: {
7: System.out.println("oyeah!");
8: }
9: finally
10: {
11: System.out.println("ogou!");
12: }
13:
14: return "oh! fezes";
15: }
16:
17: }
0: import static org.junit.Assert.assertEquals;Para a minha infeliz surpresa, o corpo do finally não é considerado coberto, na verdade ele é "amarelado".
1:
2: import org.junit.Before;
3: import org.junit.Test;
4:
5: public class TestStupidClass {
6:
7: StupidClass stupidClass = null;
8:
9: @Before
10: public void antes(){
11: stupidClass = new StupidClass();
12: }
13:
14: @Test
15: public void blablabla(){
16: assertEquals(stupidClass.falar(), "oh! fezes");
17: }
18:
19: }
Pra quem não sabe o que significa o amarelo ou verde ou vermelho, veja aqui.
Agora é buscar outra ferramenta que atenda essa necessidade, quando, e se, achar, posto aqui.
quinta-feira, 17 de abril de 2008
Acessando um método não visível "na marra"
Um dia eu falei que um método privado deveria ser testado por intermédio de outro, já que se não existir um método que o chame, ele nem deve existir. Bem, hoje vi que não é verdade, ou pelo menos encontrei um caso que precisava implementar um método protected em uma classe que seria enviada como parâmetro para outro método.
Aí pensei, "tá na hora de sujar as mãos".
Como é possível que nem todos saibam usar Reflection, vou postar aqui o código para ajudar a quem precisar:
Este é o método:
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(mailSender.getUsername(), mailSender.getPassword());
}
e assim o acesso:
public void testGetPasswordAuthentication() throws Exception{
Method metodo = authenticatorImpl.getClass().getDeclaredMethod( "getPasswordAuthentication", new Class[]{});
metodo.setAccessible(true);
metodo.invoke(authenticatorImpl, new Object[]{});
}
Aí pensei, "tá na hora de sujar as mãos".
Como é possível que nem todos saibam usar Reflection, vou postar aqui o código para ajudar a quem precisar:
Este é o método:
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(mailSender.getUsername(), mailSender.getPassword());
}
e assim o acesso:
public void testGetPasswordAuthentication() throws Exception{
Method metodo = authenticatorImpl.getClass().getDeclaredMethod( "getPasswordAuthentication", new Class[]{});
metodo.setAccessible(true);
metodo.invoke(authenticatorImpl, new Object[]{});
}
Boas práticas - Parte II
Uma boa prática que tenho percebido ajudar muito na análise de testes, é o uso do parâmetro (message) do tipo String que todos os asserts do JUnit suportam.
Desta forma indico que sempre este parâmetro seja utilizado.
Exemplos:
assertEquals("Test 1", true, true);
assertTrue("Test 2", false);
assertNotNull("Test 3", new Object());
e todos os outros asserts se comportam da mesma forma.
O erro aparecerá assim: junit.framework.AssertionFailedError: Test 2
Indicando onde o erro aconteceu.
Numa classe de testes onde você tenha muitos assert é quase impossível, ou no mínimo complicado, encontrar em que assert o teste falhou.
Desta forma indico que sempre este parâmetro seja utilizado.
Exemplos:
assertEquals("Test 1", true, true);
assertTrue("Test 2", false);
assertNotNull("Test 3", new Object());
e todos os outros asserts se comportam da mesma forma.
O erro aparecerá assim: junit.framework.AssertionFailedError: Test 2
Indicando onde o erro aconteceu.
Numa classe de testes onde você tenha muitos assert é quase impossível, ou no mínimo complicado, encontrar em que assert o teste falhou.
quarta-feira, 2 de abril de 2008
BDD - Mais uma técnica de programação?
Behaviour Driven Development é algo que, atualmente, se ouve bastante no meio Ágil, e lendo um artigo do Ivan Sanchez sobre o assunto, vejo que não existem diferenças "em termos de técnica de programação" entre BDD e TDD (Test Driven Development), aparentemente o BDD foi criado para substituir conceitualmente o TDD, pois o termo Test pode ser (e é) facilmente confundindo com Validação e causa uma certa distância entre nós e os humanos normais (não-desenvolvedores).
Segundo o Portal do BDD, esta técnica é a evolução "lógica" do TDD e do Acceptance Test Driven Planning. BDD têm um vocabulário que pode ser utilizado por todos os interessados: empresa, desenvolvedores, testadores, analistas e gerentes. Este vocabulário é tido como o "grande bum" do BDD.
BDD é mais rico que TDD quando pensamos em ferramentas, pois existe um conjunto de frameworks que auxilia na sua implementação, para Java o mais famoso (e o único que eu conheço) é o JBehave.
Vejo BDD como uma idéia bem bacana e que merece ser experimentada, os depoimentos que escuto são sempre positivos.
Segundo o Portal do BDD, esta técnica é a evolução "lógica" do TDD e do Acceptance Test Driven Planning. BDD têm um vocabulário que pode ser utilizado por todos os interessados: empresa, desenvolvedores, testadores, analistas e gerentes. Este vocabulário é tido como o "grande bum" do BDD.
BDD é mais rico que TDD quando pensamos em ferramentas, pois existe um conjunto de frameworks que auxilia na sua implementação, para Java o mais famoso (e o único que eu conheço) é o JBehave.
Vejo BDD como uma idéia bem bacana e que merece ser experimentada, os depoimentos que escuto são sempre positivos.
Desacoplando para melhorar o design e facilitar os testes
Usando TDD, normalmente nos deparamos com situações em que precisamos mudar nossa implementação para atender a algum teste, isso é normal e viável, já que os testes são parte do sistema.
Quando o Paulo Silveira veio a Fortaleza e ministrou um workshop sobre Java, falou-se muito sobre desacoplar, não usar métodos estáticos (sempre que possível) e outras boas práticas como IoC, acontece que venho buscando sempre soluções pra esses casos sem mudar minha implementação, na maioria até consegui, lógico que com maior dificuldade e esforço.
Assim, esta semana discutimos a implementação de alguns testes que estavam muito complicados e ví que o que o Paulo havia falado era bem simples, ao menos da maneira que solucionamos o problema. Vou ilustrar com um exemplo bem simples pra deixar claro como podemos facilitar os testes desacoplando algumas coisas.
Primeiro quero dizer que com um Spring da vida você não vai precisar usar esse artifício, pois com o IoC do Spring você pode fazer isso;
Segundo, é importante ficar atento, porque se existirem muitas dependência, um artifício simples como esse pode não funcionar.
Ao exemplo:
Tenho 3 classes.
Como poderíamos evitar isso? Bem simples:
Passaremos a instância da ClasseUtil como parâmetro do método:
O design fica bem simples e os testes mais fáceis de implementar.
Quando o Paulo Silveira veio a Fortaleza e ministrou um workshop sobre Java, falou-se muito sobre desacoplar, não usar métodos estáticos (sempre que possível) e outras boas práticas como IoC, acontece que venho buscando sempre soluções pra esses casos sem mudar minha implementação, na maioria até consegui, lógico que com maior dificuldade e esforço.
Assim, esta semana discutimos a implementação de alguns testes que estavam muito complicados e ví que o que o Paulo havia falado era bem simples, ao menos da maneira que solucionamos o problema. Vou ilustrar com um exemplo bem simples pra deixar claro como podemos facilitar os testes desacoplando algumas coisas.
Primeiro quero dizer que com um Spring da vida você não vai precisar usar esse artifício, pois com o IoC do Spring você pode fazer isso;
Segundo, é importante ficar atento, porque se existirem muitas dependência, um artifício simples como esse pode não funcionar.
Ao exemplo:
Tenho 3 classes.
0: public class ClasseA {
1: public void metodo1(){
2: ClasseB classeB = new ClasseB();
3: classeB.metodo2();
4: }
5: }
0: public class ClasseB {
1: public void metodo2(){
2: System.out.println(ClasseUtil.m1());
3:
4: ClasseUtil classeUtil = new ClasseUtil();
5: System.out.println(classeUtil.m2());
6: }
7: }
0: public class ClasseUtil {No teste da ClassseB terei algumas dificuldades porque existe uma chamada a um método estático (podemos usar JMockit, porém, dependendo do retorno, torna-se uma tarefa muito complicada) e um instanciamento da ClasseUtil.
1: public static String m1(){
2: return "m1";
3: }
4:
5: public String m2(){
6: return "m2";
7: }
8: }
Como poderíamos evitar isso? Bem simples:
Passaremos a instância da ClasseUtil como parâmetro do método:
0: public class ClasseA {
1: public void metodo1(){
2: ClasseB classeB = new ClasseB();
3: classeB.metodo2(new ClasseUtil());
4: }
5: }
0: public class ClasseB {
1: public void metodo2(ClasseUtil classeUtil){
2: System.out.println(classeUtil.m1());
3: System.out.println(classeUtil.m2());
4: }
5: }
O design fica bem simples e os testes mais fáceis de implementar.
segunda-feira, 17 de março de 2008
Devo mockar métodos de classes "utilitárias"?
Se considerarmos que esses métodos estão fora do escopo testado ou que serão testados isoladamente, a resposta é não, porém, pensando com cuidado, percebo que esses métodos normalmente são recheados de Reflections e Generics para serem o mais genérico possível, e pensando em algumas situações acho que podemos mudar de opinião.
Temos aqui na empresa uma classe CollectionUtil onde criamos métodos para manipular Collections. Nesta classe criamos um método não tão genérico quanto o possível, porém, o suficiente para a nossa utilização, sendo que me deparei com uma coisa:
O método é responsável por receber uma Collection e retornar um Array de ids (todas as nossas entidades tem esse atributo):
Imagine uma situação onde alguém, não importa o porque, não use o atributo "id" e sim "cod" ou um erro de digitação e sai um "ide", ou qualquer outra coisa, pensando nisso, vejo que a melhor saída é: Não mockar o método "utilitário".
Use o bom senso, um método utilitário que envie um email ou que não seja tão genérico assim, pode ser mockado. Outra alternativa é enriquecer o método "utilitário", fazendo verificações se a entidade possui o atributo "id".
Temos aqui na empresa uma classe CollectionUtil onde criamos métodos para manipular Collections. Nesta classe criamos um método não tão genérico quanto o possível, porém, o suficiente para a nossa utilização, sendo que me deparei com uma coisa:
O método é responsável por receber uma Collection e retornar um Array de ids (todas as nossas entidades tem esse atributo):
0: public Long[] convertCollectionToArrayIds(
1: Collectioncoll)
2: {
3: Method mKey = null;
4: Long[] ids = new Long[coll.size()];
5:
6: try
7: {
8: int count = 0;
9: for(T clazz: coll)
10: {
11: mKey = clazz.getClass().
12: getMethod("getId");
13: ids[count] = (Long) mKey.invoke(
14: clazz,new Object[]{});
15: count++;
16: }
17:
18: return ids;
19: }
20: catch(Exception e)
21: {
22: e.printStackTrace();
23: return null;
24: }
25: }
Imagine uma situação onde alguém, não importa o porque, não use o atributo "id" e sim "cod" ou um erro de digitação e sai um "ide", ou qualquer outra coisa, pensando nisso, vejo que a melhor saída é: Não mockar o método "utilitário".
Use o bom senso, um método utilitário que envie um email ou que não seja tão genérico assim, pode ser mockado. Outra alternativa é enriquecer o método "utilitário", fazendo verificações se a entidade possui o atributo "id".
terça-feira, 11 de março de 2008
Mais sobre Cobertura de Código (Code Coverage)
Mais uma vez caí numa discussão sobre garantir ou não 100% de cobertura, na ultima vez que discuti o assunto caí num ponto onde 100% de Cobertura só serve para o marketing da minha empresa, ou setor ou de um produto, de qualquer forma, somente para Propaganda. Acontece que algumas coisas foram mudando minha opinião, e achei que deveria atualizar a opinião do Blog também.
* Sem garantir os 100% de Cobertura, fica complicado verificar se o que deve ser testado, foi testado. O responsável, ou toda a equipe tem que ficar constantemente verificando a cobertura de uma determinada classe ou de um determinado pacote;
* Alterações em um método que não deveria ser testado fazem com que ele precise de teses. Num "getzinho" simples que só retorna uma data, você pode incluir um regra que: se não houver data no objeto, retorne a data de hoje. Se o desenvolvedor esquecer de fazer o teste, dificilmente alguém vai perceber a ausência dele;
* Num momento de refactoring, a confiança nos testes fica abalada. Se você não sabe se todo o sistema esta coberto, como você vai garantir que suas alterações não quebraram o código? Resposta fácil, procurando a classe de testes responsável pelo método que esta sendo refatorado e verificando se existe algum teste para aquele método específico.
Quando pensamos em todos esses pontos, e percebemos outros como o que vi no Blog do Wagner Elias, ótimo texto por sinal, percebemos que garantir a cobertura de código é muito válido, não somente a nível de Propaganda.
Uma alternativa muito boa é automatizar alguns testes, ou gerar o código das situações mais genéricas, prefira sempre a primeira situação, manter código desnecessário é um custo muito alto. Sendo assim, um get ou set estaria sendo coberto por esse teste genérico, até ter sua implementação alterada, neste caso um teste "de verdade" seria criado para ele.
Para ver os outros post sobre o assunto.
* Sem garantir os 100% de Cobertura, fica complicado verificar se o que deve ser testado, foi testado. O responsável, ou toda a equipe tem que ficar constantemente verificando a cobertura de uma determinada classe ou de um determinado pacote;
* Alterações em um método que não deveria ser testado fazem com que ele precise de teses. Num "getzinho" simples que só retorna uma data, você pode incluir um regra que: se não houver data no objeto, retorne a data de hoje. Se o desenvolvedor esquecer de fazer o teste, dificilmente alguém vai perceber a ausência dele;
* Num momento de refactoring, a confiança nos testes fica abalada. Se você não sabe se todo o sistema esta coberto, como você vai garantir que suas alterações não quebraram o código? Resposta fácil, procurando a classe de testes responsável pelo método que esta sendo refatorado e verificando se existe algum teste para aquele método específico.
Quando pensamos em todos esses pontos, e percebemos outros como o que vi no Blog do Wagner Elias, ótimo texto por sinal, percebemos que garantir a cobertura de código é muito válido, não somente a nível de Propaganda.
Uma alternativa muito boa é automatizar alguns testes, ou gerar o código das situações mais genéricas, prefira sempre a primeira situação, manter código desnecessário é um custo muito alto. Sendo assim, um get ou set estaria sendo coberto por esse teste genérico, até ter sua implementação alterada, neste caso um teste "de verdade" seria criado para ele.
Para ver os outros post sobre o assunto.
quinta-feira, 6 de março de 2008
Método mockado retornando uma Exception
Recentemente precisei fazer um teste em um método e que deveria verificar o comportamento do mesmo quando um método invocado por ele retornasse uma Exception específica.
Exemplo do método:
Exemplo do método:
0: public String delete() throws ExceptionNo expect do método é só retornar a exception da seguinte forma:
1: {
2: try
3: {
4: vendaManager.delete(venda.getId());
5: }
6: catch (UnexpectedRollbackException e)
7: {
8: addActionError("Não foi possível cancelar venda.");
9: return Action.INPUT;
10: }
11: return Action.SUCCESS;
12: }
0: vendaManager.expects(once()).method("delete")
1: .with(ANYTHING).will(throwException(
2: new UnexpectedRollbackException("")));
3: assertEquals(action.delete(), "input");
quinta-feira, 28 de fevereiro de 2008
Descobrindo o Selenium
Selenium é a ferramenta mais utilizada para Testes de Integração, ou ao menos a mais popular no meio Java.
Para os nossos exemplos, vamos usar o plugin para o FireFox que facilita muito a tarefa (Uma lista de links importantes esta no final do post, incluindo downloads de plugins e ferramentas).
O Selenium tem o seu próprio testRunner, porém nós vamos utilizar o Selenium-RC para testarmos via código Java, isso possibilita integrar os testes de Selenium com seus Testes Unitários.
Com o plugin do FireFox, iniciamos a usar o Selenium:
Começamos a gravar o processo que queremos testar, no caso um simples login:
Quando completamos o processo que queremos testar, paramos a gravação:
Exportamos o teste feito para código Java:
O código gerado é esse:
O objeto selenium no método acima, nos da informações da página, como um alert, tempo esperado para a página carregar, o titulo, dentre outras coisas.
Para que o teste fique completo, estendemos TestCase, declaramos um atributo selenium do tipo Selenium e criamos o setUp e tearDown:
A classe finalmente deve ficar assim:
Estarte o selenium server: java -jar selenium-server.jar que está no diretório selenium-remote-control-0.9.2\selenium-server-0.9.2 do Selenium-RC.
Agora rode como se fosse um teste comum do JUnit.
Links que podem ajudar:
Selenium (http://selenium-ide.openqa.org/)
Download em (http://selenium-core.openqa.org/download.jsp)
Selenium IDE 4 Firefox (http://release.openqa.org/selenium-ide/0.8.7/selenium-ide-0.8.7.xpi)
Selenium 4 Java (http://selenium-rc.openqa.org/java.html)
Download em (http://selenium-rc.openqa.org/download.jsp)
Para os nossos exemplos, vamos usar o plugin para o FireFox que facilita muito a tarefa (Uma lista de links importantes esta no final do post, incluindo downloads de plugins e ferramentas).
O Selenium tem o seu próprio testRunner, porém nós vamos utilizar o Selenium-RC para testarmos via código Java, isso possibilita integrar os testes de Selenium com seus Testes Unitários.
Com o plugin do FireFox, iniciamos a usar o Selenium:
Começamos a gravar o processo que queremos testar, no caso um simples login:
Quando completamos o processo que queremos testar, paramos a gravação:
Exportamos o teste feito para código Java:
O código gerado é esse:
0: import com.thoughtworks.selenium.*;
1: import java.util.regex.Pattern;
2:
3: public class login extends SeleneseTestCase {
4: public void testLogin() throws Exception {
5: selenium.open("/login.action;" +
6: "jsessionid=21FC72563" +
7: "78F1D482601E23C929635A1");
8: selenium.type("j_password", "1234");
9: selenium.click("submit");
10: selenium.waitForPageToLoad("30000");
11: assertEquals("Área Inacessível",
12: selenium.getAlert());
13: }
14: }
O objeto selenium no método acima, nos da informações da página, como um alert, tempo esperado para a página carregar, o titulo, dentre outras coisas.
Para que o teste fique completo, estendemos TestCase, declaramos um atributo selenium do tipo Selenium e criamos o setUp e tearDown:
0: public void setUp() throws Exception {
1: String url =
2: "http://www.meuslivroseletronicos.com.br";
3: selenium = new DefaultSelenium("localhost",
4: SeleniumServer.getDefaultPort(),
5: "*firefox", url);
6: selenium.start();
7: }
8:
9: protected void tearDown() throws Exception {
10: selenium.stop();
11: }
A classe finalmente deve ficar assim:
0: import junit.framework.TestCase;
1: import org.openqa.selenium.server.SeleniumServer;
2: import com.thoughtworks.selenium.DefaultSelenium;
3: import com.thoughtworks.selenium.Selenium;
4:
5: public class login extends TestCase {
6: private Selenium selenium;
7: public void setUp() throws Exception {
8: String url = "http://www.meuslivroseletronicos" +
9: ".com.br";
10: selenium = new DefaultSelenium("localhost",
11: SeleniumServer.getDefaultPort(), "*firefox",
12: url);
13: selenium.start();
14: }
15:
16: protected void tearDown() throws Exception {
17: selenium.stop();
18: }
19:
20: public void testLoginLivro() throws Exception {
21: selenium.open("/login.action;jsessionid=BEF" +
22: "47FEE6B27A5E7EA20DB20E6F3F4F2");
23: selenium.type("j_username", "admin");
24: selenium.type("j_password", "1234");
25: selenium.click("submit");
26: selenium.waitForPageToLoad("30000");
27: assertEquals("Livro Eletronico",
28: selenium.getTitle());
29: }
30: }
Estarte o selenium server: java -jar selenium-server.jar que está no diretório selenium-remote-control-0.9.2\selenium-server-0.9.2 do Selenium-RC.
Agora rode como se fosse um teste comum do JUnit.
Links que podem ajudar:
Selenium (http://selenium-ide.openqa.org/)
Download em (http://selenium-core.openqa.org/download.jsp)
Selenium IDE 4 Firefox (http://release.openqa.org/selenium-ide/0.8.7/selenium-ide-0.8.7.xpi)
Selenium 4 Java (http://selenium-rc.openqa.org/java.html)
Download em (http://selenium-rc.openqa.org/download.jsp)
Assinar:
Postagens (Atom)