1987 capítulos
  Título Autor Editora Formato Comprar item avulso Adicionar à Pasta
Medium 9788577807246

27. Padrões de Teste

Beck, Kent Grupo A PDF Criptografado

164

Parte III

Padrões para Desenvolvimento Guiado por Testes

Tente ambas as formas. Veja se você se sente diferente; programe diferente quando tiver dois testes que não funcionam. Responda conforme o caso.

Objeto simulado (Mock Object)

Como você testa um objeto que se baseia em um recurso caro ou complicado? Crie uma versão faz de conta do recurso que responde com constantes.

Há ao menos um material com valor equivalente a um livro sobre Objeto

Simulado1, mas isso servirá como uma introdução. O exemplo clássico é um banco de dados. Bancos de dados levam um longo tempo para iniciarem; são difíceis de manter limpos; e, se estão localizados em um servidor remoto, amarram seus testes a um local físico em uma rede. O banco de dados também é uma fonte fértil de erros no desenvolvimento.

A solução é não usar um banco de dados real na maior parte do tempo. A maioria dos testes é escrita em termos de um objeto que age como um banco de dados, mas está apenas assentado na memória. public void testOrderLookup() {

Ver todos os capítulos
Medium 9788577807246

16. Abstração, Finalmente

Beck, Kent Grupo A PDF Criptografado

98

Parte I

O Exemplo Financeiro

Roseta* para gerações futuras apreciarem nossa genialidade. Pense, ó pense, em nossos leitores.

O teste, nesse caso, é mais longo que o código. O código é o mesmo código em Money. (Será que eu ouvi uma classe abstrata ao longe?)

Sum public Expression plus(Expression addend) { return new Sum(this, addend);

}

$5 + 10 CHF = $10 se a taxa é 2:1

$5 + $5 = $10

Retornar Money de $5 + $5

Bank.reduce( Money )

Reduzir Money com conversões

Reduce(Bank, String)

Sum.plus

Expression.times

Você terminará, aproximadamente, com o mesmo número de linhas no código do teste e no código modelo quando implementar TDD. Para TDD fazer sentido em termos de economia, você precisará ser capaz de escrever o dobro de linhas por dia do que escrevia antes, ou escrever metade daquela quantidade de linhas para a mesma funcionalidade. Terá que medir e ver que efeitos TDD tem por sua própria prática. Certifique-se, no entanto, de considerar tempos de depuração, integração e explicação em suas métricas.

$5 + 10 CHF = $10 se a taxa é 2:1

Ver todos os capítulos
Medium 9788577807246

Apêndice I - Diagramas de Influência

Beck, Kent Grupo A PDF Criptografado

Apêndice

I

Diagramas de Influência

Este livro contém muitos exemplos de diagramas de influência. A ideia dos diagramas de influência é obtida da excelente série Quality Software Manage1 ment de Gerald Weinberg, particularmente do Livro 1: Systems Thinking . O propósito de um diagrama de influência é ver como os elementos de um sistema afetam um ao outro.

Diagramas de influência têm três elementos:

• Atividades, anotadas como uma palavra ou frase curta.

• Conexões positivas, anotadas como uma flecha direcionada entre duas atividades, significando que mais da atividade origem tende a criar mais da atividade destino, ou menos da atividade de origem tende a criar menos da atividade destino.

• Conexões negativas, anotadas como flechas direcionadas entre duas atividades com um círculo sobre ela, significando que mais da atividade de origem tende a criar menos da atividade destino, ou menos da atividade de origem tende a criar mais da atividade destino.

Um monte de palavras para um conceito simples. As Figuras A.1 a A.3 fornecem alguns exemplos.

Ver todos os capítulos
Medium 9788577807246

10. Tempos Interessantes

Beck, Kent Grupo A PDF Criptografado

66

Parte I

O Exemplo Financeiro

Dollar

Money times(int multiplier) { return Money.dollar(amount * multiplier);

}

Não há um jeito óbvio de fazê-las idênticas. Às vezes você tem que voltar atrás para seguir em frente, como quando resolvemos um Cubo de Rubik. O que acontece se nós otimizarmos os métodos fábrica? (Eu sei, eu sei, nós chamamos o método fábrica pela primeira vez apenas um capítulo atrás. Frustrante, não é?)

Franc

Money times(int multiplier) { return new Franc(amount * multiplier, "CHF");

}

Dollar

Money times(int multiplier) { return new Dollar(amount * multiplier, "USD");

}

Em Franc, entretanto, sabemos que a variável de instância de moeda é sempre

“CHF”, então podemos escrever:

Franc

Money times(int multiplier) { return new Franc(amount * multiplier, currency);

}

Isso funciona. O mesmo truque funciona em Dollar:

Dollar

Money times(int multiplier) { return new Dollar(amount * multiplier, currency);

}

Estamos quase lá. Realmente importa se temos um Franc ou um Money? Poderíamos pensar sobre isso cuidadosamente, dado nosso conhecimento sobre o sistema, mas temos código limpo e temos testes que nos dão confiança de que o código limpo funciona. Em vez de usar minutos de raciocínio suspeito, podemos apenas perguntar para o computador fazendo as mudanças e rodando os testes.

Ver todos os capítulos
Medium 9788577807246

8. Fazendo Objetos

Beck, Kent Grupo A PDF Criptografado

56

Parte I

O Exemplo Financeiro

Podemos dar um passo na direção de conciliá-los, fazendo ambos retornarem um

Money:

Franc

Money times(int multiplier) { return new Franc(amount * multiplier);

}

Dollar

Money times(int multiplier) { return new Dollar(amount * multiplier);

}

O próximo passo não é tão óbvio. As duas subclasses de Money não estão fazendo o suficiente para justificarem sua existência, logo, gostaríamos de eliminá-las. Mas, não podemos fazer isso em um grande passo, pois não seria uma demonstração muito eficaz de TDD.

Certo, estaremos um passo mais próximos de eliminarmos as subclasses se houver menos referências diretas às subclasses. Podemos introduzir um método fábrica em Money que retorne um Dollar. Usaremos algo assim: public void testMultiplication() {

Dollar five = Money.dollar(5); assertEquals(new Dollar(10), five.times(2)); assertEquals(new Dollar(15), five.times(3));

}

A implementação cria e retorna um Dollar:

Money static Dollar dollar(int amount) { return new Dollar(amount);

}

Mas, queremos que as referências a Dollars desapareçam, logo precisamos mudar a declaração no teste: public void testMultiplication() {

Ver todos os capítulos
Medium 9788577807246

21. Contagem

Beck, Kent Grupo A PDF Criptografado

Capítulo

21

Contagem

Invoque o método teste

Invoque setUp primeiro

Invoque tearDown depois

Invoque tearDown mesmo se o método teste falhar

Rode múltiplos testes

Informe resultados coletados

String de registro em WasRun

Eu ia incluir uma implementação para assegurar que tearDown() fosse chamada a despeito das exceções durante o método de teste. Contudo, precisamos capturar exceções de forma a fazer o teste funcionar. (Eu sei, eu tentei e recuei.) Se cometermos um engano implementando isso, não seremos capazes de ver o engano, pois as exceções não serão informadas.

Em geral, a ordem da implementação dos testes é importante. Quando eu pego o próximo teste para implementar, eu acho que um teste me ensinará algo e eu tenho confiança que posso fazer funcionar. Se eu deixar aquele teste funcionando, mas ficar emperrado no próximo, então considero o backup de dois passos.

Seria ótimo se o ambiente de programação me ajudasse com isso, funcionando como um checkpoint para o código cada vez que todos os testes rodarem.

Ver todos os capítulos
Medium 9788577807246

4. Privacidade

Beck, Kent Grupo A PDF Criptografado

40

Parte I

O Exemplo Financeiro

product= five.times(3); assertEquals(15, product.amount);

}

Isso parece melhor, então reescrevemos a segunda asserção também: public void testMultiplication() {

Dollar five= new Dollar(5);

Dollar product= five.times(2); assertEquals(new Dollar(10), product); product= five.times(3); assertEquals(new Dollar(15), product);

}

Agora a variável temporária product não está ajudando muito, então podemos otimizá-la: public void testMultiplication() {

Dollar five= new Dollar(5); assertEquals(new Dollar(10), five.times(2)); assertEquals(new Dollar(15), five.times(3));

}

Esse teste nos fala mais claramente, como se fosse uma afirmação de verdade e não uma sequência de operações.

Com essas mudanças para o teste, Dollar é agora a única classe usando sua variável de instância amount, então podemos fazê-la privada:

Dollar private int amount;

$5 + 10 CHF = $10 se a taxa é 2:1

$5 * 2 = $10

Tornar “quantidade” privada

Efeitos colaterais em Dollar?

Arredondamento de dinheiro? equals() hashCode()

Ver todos os capítulos
Medium 9788577807246

1. Dinheiro Multi-Moeda

Beck, Kent Grupo A PDF Criptografado

24

Parte I

O Exemplo Financeiro

De que comportamento precisaremos para produzir o relatório revisado?

Dito de outra forma, qual conjunto de testes, quando passarem, demonstrará a presença de código que estamos confiantes que irá calcular o relatório corretamente?

• Precisamos ser capazes de somar valores em duas moedas diferentes e de converter o resultado, dado um conjunto de taxas de câmbio.

• Precisamos ser capazes de multiplicar um valor (preço por ação) por um número (número de ações) e de receber uma quantia.

Faremos uma lista de tarefas para lembrarmos o que precisamos fazer para mantermos o foco e para dizer quando acabarmos. Quando começarmos a trabalhar em um item, nós o marcaremos em negrito, assim. Quando terminarmos um item, o riscaremos, assim. Quando pensarmos em outro teste para escrever, o adicionaremos à lista.

Como você pode ver, a partir da lista de tarefas na página anterior, trabalharemos, primeiro, na multiplicação. Então, de qual objeto precisamos primeiro?

Uma questão traiçoeira. Nós não começamos com objetos, começamos com testes.

Ver todos os capítulos
Medium 9788577807246

3. Igualdade para Todos

Beck, Kent Grupo A PDF Criptografado

Capítulo

3

Igualdade para Todos

Se eu tenho um inteiro e somo 1 a ele, não espero o inteiro original mudar; espero usar o novo valor. Objetos geralmente não se comportam desse jeito. Se eu tenho um contrato e adiciono um a sua cobertura, então a cobertura do contrato deveria mudar (sim, sim, sujeita a todo tipo de regras de negócio interessantes que não nos interessam aqui).

Podemos usar objetos como valores, como estamos usando nosso Dollar agora. O padrão para isso é Value Object*. Uma das restrições em usar Value Object

é que os valores das variáveis de instância do objeto nunca mudem uma vez que foram criados no construtor.

Há uma grande vantagem em usar Value Object: você não tem que se preocupar com problemas de sinônimos. Digamos que eu tenho um cheque e coloque sua quantia em $5, e então eu crio outra quantia de cheque com os mesmos $5. Alguns dos piores bugs da minha carreira ocorreram quando mudar o valor do primeiro cheque mudou, inadvertidamente, o valor do segundo cheque. Isso são sinônimos.

Ver todos os capítulos
Medium 9788577807246

14. Mudança

Beck, Kent Grupo A PDF Criptografado

88

Parte I

O Exemplo Financeiro

? 2

: 1; return new Money(amount / rate, to);

}

Agora, de repente, Money conhece taxas de câmbio. Eca! O Bank deveria ser o

único lugar em que nos preocupamos com taxas de câmbio. Teremos que passar o

Bank como um parâmetro para Expression.reduce(). (Vê? Nós sabíamos que precisaríamos disso e estávamos certos. Nas palavras do avô em A Princesa Prometida,

“Você é muito esperto...”) Primeiro, o chamador:

Bank

Money reduce(Expression source, String to) { return source.reduce(this, to);

}

Então os implementadores:

Expression

Money reduce(Bank bank, String to);

Sum public Money reduce(Bank bank, String to) { int amount= augend.amount + addend.amount; return new Money(amount, to);

}

Money public Money reduce(Bank bank, String to) { int rate = (currency.equals("CHF") && to.equals("USD"))

? 2

: 1; return new Money(amount / rate, to);

}

Os métodos têm que ser públicos, pois métodos na interface têm que ser públicos

(por alguma razão excelente, estou certo).

Agora conseguimos calcular a taxa no Bank:

Ver todos os capítulos
Medium 9788577807246

Apêndice II - Fibonacci

Beck, Kent Grupo A PDF Criptografado

232

Apêndice II

Há muitos jeitos que eu poderia usar para fazer isso rodar. Escolherei tratar

0 como um caso especial: int fib(int n) { if (n == 0) return 0; return 1;

}

A duplicação no caso de teste está começando a me incomodar, e ela só vai ficar pior conforme adicionarmos novos casos. Podemos fatorar a estrutura comum das asserções dirigindo o teste a partir de uma tabela de entrada e valores esperados. public void testFibonacci() { int cases[][]= {{0,0},{1,1}}; for (int i= 0; i < cases.length; i++) assertEquals(cases[i][1], fib(cases[i][0]));

}

Agora adicionar o próximo caso requer seis teclas pressionadas e nenhuma linha adicional: public void testFibonacci() { int cases[][]= {{0,0},{1,1},{2,1}}; for (int i= 0; i < cases.length; i++) assertEquals(cases[i][1], fib(cases[i][0]));

}

Desconcertantemente, o teste funciona. Acontece que nossa constante 1 está certa para esse caso também. No próximo teste: public void testFibonacci() { int cases[][]= {{0,0},{1,1},{2,1},{3,2}}; for (int i= 0; i < cases.length; i++) assertEquals(cases[i][1], fib(cases[i][0]));

Ver todos os capítulos
Medium 9788577807246

30. Padrões de Projeto

Beck, Kent Grupo A PDF Criptografado

Capítulo

30

Padrões de Projeto

Uma das principais ideias de padrões é que, embora possa parecer como se estivéssemos todo o tempo resolvendo problemas completamente diferentes, a maioria dos problemas que resolvemos é gerada pelas ferramentas que usamos e não

1 por problemas externos próximos . Por causa disso, podemos esperar encontrar (e realmente vamos encontrar) problemas comuns com soluções comuns, mesmo em meio a uma incrível diversidade de contextos externos de solução de problemas.

Aplicar objetos para organizar a computação é um dos melhores exemplos de subproblemas comuns internamente gerados, sendo resolvidos de formas previsíveis e comuns. O enorme sucesso de padrões de projeto é um atestado das coisas em comum vistas por programadores na orientação a objetos. O sucesso

2 do livro Padrões de Projeto , contudo, asfixiou qualquer diversidade na expressão desses padrões. O livro parece ter um viés sutil em direção ao projeto como uma fase. Isso certamente não faz menção à refatoração como uma atividade de projeto. Projeto em TDD requer um olhar ligeiramente diferente sobre os padrões de projeto.

Ver todos os capítulos
Medium 9788577807246

22. Lidando com Falha

Beck, Kent Grupo A PDF Criptografado

130

Parte II

O Exemplo xUnit

self.runCount= 0 self.errorCount= 0 def testFailed(self): self.errorCount= self.errorCount + 1

Com a contagem correta (a qual suponho que poderíamos ter testado, se estivéssemos dando passos muito, muito pequeninos – mas eu não vou me incomodar, pois o café fez efeito agora), podemos mostrar corretamente:

TestResult def summary(self): return "%d run, %d failed" % (self.runCount, self.failureCount)

Agora esperamos que se chamarmos testFailed() corretamente, teremos a resposta esperada. Quando a chamamos? Quando capturarmos uma exceção no método de teste:

TestCase def run(self): result= TestResult() result.testStarted() self.setUp() try: method = getattr(self, self.name) method() except: result.testFailed() self.tearDown() return result

Há uma sutileza escondida nesse método. Pela forma como foi escrito, se um desastre acontecer durante setUp(), então a exceção não será pega. Isso pode não ser o que queremos – queremos que nossos testes executem independentemente uns dos outros. Contudo, precisamos de outro teste antes que possamos mudar o código.

Ver todos os capítulos
Medium 9788577807246

17. Retrospectiva Financeira

Beck, Kent Grupo A PDF Criptografado

102

Parte I

O Exemplo Financeiro

cia, os testes podem ser mais irregulares e o projeto mais feio sem interferir em sua confiabilidade.

Quando faço todas as tarefas óbvias, gosto de rodar um analisador de código* como Small-Lint para Smalltalk. Muitas das sugestões que surgem eu já conheço ou não concordo. Todavia, analisadores automáticos não esquecem, logo, se não deleto uma implementação obsoleta, não tenho estresse. O analisador vai indicá-la.

Outra questão “qual é o próximo passo?” é: “De que testes adicionais eu preciso?” Às vezes, você pensa em um teste que “não deveria” funcionar, e ele funciona. Então você precisa descobrir o porquê. Às vezes, um teste que não deveria funcionar realmente não funciona, e você pode gravá-lo como uma limitação conhecida ou como trabalho a ser feito depois.

Finalmente, quando a lista está vazia é uma boa hora para rever o projeto. As palavras e os conceitos jogam juntos? Há duplicação que é difícil de eliminar dado o atual projeto? (Duplicação duradoura é um sintoma de projeto latente.)

Ver todos os capítulos
Medium 9788577807246

7. Maçãs e Laranjas

Beck, Kent Grupo A PDF Criptografado

54

Parte I

O Exemplo Financeiro

que não estamos comparando Dollars com Francs. Podemos fazer isso agora comparando a classes dos dois objetos – dois Moneys são iguais apenas se suas quantidades e classes são iguais.

Money public boolean equals(Object object) {

Money money = (Money) object; return amount == money.amount

&& getClass().equals(money.getClass());

}

Usar as classes dessa forma no código modelo é um pouco mau cheiroso*.

Gostaríamos de usar um critério que fizesse sentido no domínio das finanças, não no domínio de objetos Java. Mas, não temos atualmente nada como uma moeda, e isso não parece ser razão suficiente para introduzir uma, então isso terá que ficar assim por enquanto.

$5 + 10 CHF = $10 se a taxa é 2:1

$5 * 2 = $10

Tornar “quantidade” privada

Efeitos colaterais em Dollar?

Arredondamento de dinheiro? equals() hashCode()

Igualdade de null

Igualdade de objeto

5 CHF * 2 = 10 CHF

Duplicação de Dólar/Franco

Igualdade comum

Multiplicação comum

Comparar Francos com Dólares

Moeda?

Agora, realmente precisamos nos livrar do código comum de times() para que possamos obter uma aritmética de moeda mista. Antes de fazê-lo, contudo, podemos revisar nossas grandes realizações desse capítulo:

Ver todos os capítulos

Carregar mais