46

Aprenda bdd-jogando-dados-ebook

Embed Size (px)

DESCRIPTION

Um livro para você aprender BDD, Ruby, Rspec e Shoes enquanto se diverte.

Citation preview

Page 1: Aprenda bdd-jogando-dados-ebook

1

www.princexml.com
Prince - Personal Edition
This document was created with Prince, a great way of getting web content onto paper.
Page 2: Aprenda bdd-jogando-dados-ebook
Page 3: Aprenda bdd-jogando-dados-ebook

Aprenda BDD Jogando Dados!

SEU COMPORTAMENTO É SE DIVERTIR

Primeira Edição

Page 4: Aprenda bdd-jogando-dados-ebook
Page 5: Aprenda bdd-jogando-dados-ebook

Aprenda BDD Jogando Dados!

SEU COMPORTAMENTO É SE DIVERTIR

Primeira Edição

por Valério Farias de Carvalho

Page 6: Aprenda bdd-jogando-dados-ebook

Por Valério Farias de Carvalho. Disponibilizado como Creative Commons Atribuição 2.5

Primeira edição: Julho de 2010

Valério Fariashttp://www.valeriofarias.comTwitter: @valeriofarias

Origem da imagem da capa: http://www.flickr.com/photos/missturner/

Page 7: Aprenda bdd-jogando-dados-ebook

INTRODUÇÃO

Olá entusiasta do Ruby! Olá entusiasta de BDD! Bem vindo a essa jornada linha a linha, teste por teste,utilizando a técnica do Desenvolvimento Orientado a Comportamento (Behavior Driven Development) em umprojeto muito interessante: Simulação dos dados do jogo War!. Eu escrevi esse pequeno livro para aprenderBDD e RSpec, por isso eu tinha que falar sobre algo simples e de preferência que fosse divertido. Então eupensei: Por que não jogar dados! Porque não jogar dados do jogo War! Então aqui está: Aprenda BDDjogando dados!

A aplicação que nós criaremos juntos usa duas classes: Dice e WarDice. O primeiro capítulo eu começoconstruindo o arquivo RSpec da classe Dice e a classe Dice simultaneamente e passo a passo até todos ostestes tornarem-se verdes.

No segundo capítulo eu continuo o desenvolvimento com a classe WarDice, aquela que fará a simulação decada dado do jogo war!

Finalmente, no terceiro capítulo eu uso as classes criadas em uma divertida aplicação feita em shoes.

A filosofia desse livro é aprender fazendo coisas divertidas. Em uma palavra: Experimentação. Então euespero que você aproveite esse livro simples mas também muito instrutivo.

Os testes usados nesse livro não são uma solução definitiva. Eles são somente possibilidades no meio deoutras. Como eu disse, eu o escrevi para aprender RSpec. Você pode enviar sugestões, clonar o projeto,modificá-lo e codificá-lo de outra forma. Quem sabe não faremos juntos a versão 2.0 desse livro :).

Aprenda BDD Jogando Dados!

7

Page 8: Aprenda bdd-jogando-dados-ebook

O código fonte completo da aplicação você pode encontrar no seguinte endereço: https://github.com/valeriofarias/shoes-war-dice/tree

Agora, apenas leia, codifique, teste, compartilhe e se divirta!

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

8

Page 9: Aprenda bdd-jogando-dados-ebook

Capítulo 1

Fazendo a classe Dice usando BDD

INICIANDO

Antes de começarmos a programar vamos instalar os pacotes necessários. Primeiro instale o rspec:

gem install rspec

Se você quiser mais usabilidade, instale o pacote zentest:

gem install ZenTest

Agora instale o pacote autotest-notification. Essa gem configura o autotest (ZenTest) para enviar mensagenspara softwares como o Growl, LibNotify, and Snarl, mostrando uma janela com os resultados. Leia o arquivoREADME do projeto para saber como instalar: http://github.com/carlosbrando/autotest-notification/. Comessas três gems, nossa jornada se torna divertida!

Capítulo 1: Fazendo a classe Dice usando BDD

9

Page 10: Aprenda bdd-jogando-dados-ebook

Para completar a bagagem, visite a página do Shoes e leia como instalar em seu sistema operacional. Sim,Shoes é muito divertido! Nós finalizaremos nossa brincadeira com ele. Acesse http://github.com/shoes/shoes/downloads ou http://github.com/shoes/shoes. Para aprender como usar e executar o Shoes leia oebook Nobody knows Shoes disponível em http://github.com/shoes/shoes/downloads.

Agora vamos começar nossa viagem teste a teste no mundo BDD!

CRIANDO O ARQUIVO RSPEC

Primeiro crie a pasta dice e dentro dela crie as pastas lib e spec. Agora crie o arquivo dice_spec.rb dentro dapasta spec. Bem! O que está esperando! Vamos escrever o primeiro requisito nesse arquivo. Agora começaa diversão!

requirerequire "rubygems"requirerequire "spec"

requirerequire "lib/dice"

describe Dice dodoit "deve ter números entre 1 e 6"

endend

Para executar esse teste abra o terminal, entre na pasta dice, depois digite o comando de teste:

cd dicespec spec//dice_spec

Em nosso exemplo com autotest, apenas digite o seguinte comando:

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

10

Page 11: Aprenda bdd-jogando-dados-ebook

autospec

Agora o teste executa automaticamente toda vez que salvarmos o arquivo. Voltando para nosso exemplo. Asaída do primeiro teste quebra porque precisamos criar a classe Dice no arquivo lib/dice.rb.

spec ----autospec spec//dice_spec.rbrb.//spec//dice_spec.rbrb:6: uninitialized constant Dice (NameError)

ESCREVA A CLASSE DICE

Para resolver o erro inicial, apenas crie a classe Dice:

classclass Diceendend

A saída mostra o requisito pendente:

Pending:

Dice deve ter números entre 1 e 6 (Not Yet Implemented).//spec//dice_spec.rbrb:7

Finished inin 0.04099 seconds

1 example, 0 failures, 1 pending

OS NÚMEROS NO DADO

O primeiro requisito é delimitar a quantidade de números do dado: 1 a 6:

Capítulo 1: Fazendo a classe Dice usando BDD

11

Page 12: Aprenda bdd-jogando-dados-ebook

describe Dice dodoit "deve ter números entre 1 e 6" dodo

dice == DiceDice.newnew(1..6).shouldshould includeinclude( dice.playplay )

endendendend

Saída:

F

1)NoMethodError inin 'Dice deve ter números entre 1 e 6'undefined method `play' for #<Dice:0xb7b6986c>./spec/dice_spec.rb:9:

Finished in 0.073369 seconds

1 example, 1 failure

CRIANDO O MÉTODO PLAY ARQUIVO DICE.RB

Para começar a resolver o problema anterior vamos escrever o método play:

classclass Dicedefdef playplayendend

endend

Saída: Ainda vai falhar pois o método play no exemplo anterior retorna nulo.

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

12

Page 13: Aprenda bdd-jogando-dados-ebook

F

1)'Dice deve ter números entre 1 e 6' FAILEDexpected 1..6 to includeinclude nilnil.//spec//dice_spec.rbrb:9:

Finished inin 0.031104 seconds

1 example, 1 failure

NÚMERO FORA DO CONJUNTO 1-6

Por experimentação vamos colocar um número fora do conjunto 1-6 para ver o que acontece:

classclass Dicedefdef playplay

10endend

endend

Output: continua falhando, pois está fora do conjunto.

F

1)'Dice deve ter números entre 1 e 6' FAILEDexpected 1..6 to includeinclude 10.//spec//dice_spec.rbrb:9:

Finished inin 0.03021 seconds

Capítulo 1: Fazendo a classe Dice usando BDD

13

Page 14: Aprenda bdd-jogando-dados-ebook

1 example, 1 failure

NÚMERO DENTRO DO CONJUNTO 1-6

Agora, vamos colocar um número entre 1-6 e finalmente a classe Dice passa no teste.

classclass Dicedefdef playplay

6endend

endend

Output:

.

Finished inin 0.027648 seconds

1 example, 0 failures

NÚMEROS ALEATÓRIOS

Por enquanto está Ok. Agora vamos trabalhar no requisito dos números aleatórios. Eu vou colocar algunsoutros requisitos que lembrei, mas não muitos. Vou colocar também a letra x no começo da declaração'it'para o rspec ignorá-la. Isso é apenas um truque ;).

requirerequire "rubygems"requirerequire "spec"

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

14

Page 15: Aprenda bdd-jogando-dados-ebook

requirerequire "lib/dice"

describe Dice dodoit "deve ter números entre 1 e 6" dodo

dice == DiceDice.newnew(1..6).shouldshould includeinclude( dice.playplay )

endend

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "deve armazenar número da última jogada."xit "deve jogar o dado no momento que o objeto for inicializado"

endend

Output:

.F

1)'Dice deve mostrar números de forma aleatória' FAILEDexpected falsefalse, got truetrue.//spec//dice_spec.rbrb:18:

Finished inin 0.093321 seconds

Capítulo 1: Fazendo a classe Dice usando BDD

15

Page 16: Aprenda bdd-jogando-dados-ebook

2 examples, 1 failure

GERANDO NÚMERO ALEATÓRIO E REFATORANDO

Agora temos quer gerar números aleatórios e também temos que refatorar o primeiro requisito que delimita asaída entre os números 1 a 6. Esse requisito deve ser testado com uma variedade de números ao invés desomente um como está agora. Para brincar um pouco com Ruby também eu vou modificar o código dorequire para diminuir o número de linhas.

classclass Dicedefdef playplay

randrand(6)endend

endend

%w{ rubygems spec lib/dice }.eacheach{|lib| requirerequire lib }

describe Dice dodoit "deve ter somente números entre 1 e 6" dodo

dice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.should_notshould_not be_nilgroup.should_notshould_not be_emptygroup.should_notshould_not includeinclude('-') # Números negativos não são permitidosgroup.should_notshould_not includeinclude('0')group.should_notshould_not includeinclude('7')group.should_notshould_not includeinclude('8')group.should_notshould_not includeinclude('9')

endend

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

16

Page 17: Aprenda bdd-jogando-dados-ebook

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "deve armazenar número da última jogada."xit "deve jogar o dado no momento que o objeto for inicializado"

endend

Output: rand(6) gera também o número zero, portanto o teste falha.

F.

1)'Dice deve ter somente números entre 1 e 6' FAILEDexpected "40251322225105400021423125552351044325205220224304451434252545153314510153043001251012005523244142243512333204035042444130240534042050050324205500223120330524430331015422350203015044053545205524012055023101003331405204353205410102441530220034031430225503404511243223354504315335402445045511" notnot to includeinclude "0".//spec//dice_spec.rbrb:10:

Finished inin 0.046589 seconds

2 examples, 1 failure

Capítulo 1: Fazendo a classe Dice usando BDD

17

Page 18: Aprenda bdd-jogando-dados-ebook

RESOLVENDO A FALHA DO NÚMERO ALEATÓRIO

Finalmente vamos ajeitar o comando para rand(6) + 1. Dessa forma limita a saída entre 1 e 6. Vamosaproveitar para refatorar o primeiro requisito para usar expressões regulares. Agora o teste passa :).

classclass Dicedefdef playplay

randrand(6) ++ 1endend

endend

%w{ rubygems spec lib/dice }.eacheach {|lib| requirerequire lib}

describe Dice dodoit "deve ter somente números entre 1 e 6" dodo

dice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "deve armazenar número da última jogada."

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

18

Page 19: Aprenda bdd-jogando-dados-ebook

xit "deve jogar o dado no momento que o objeto for inicializado"endend

ARMAZENANDO O NÚMERO DO DADO

Vamos trabalhar no próximo requisito: "deve armazenar número da última jogada.". Eu criarei o métodoshow_number e colocarei uma constante para o teste passar.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodo

it "deve ter somente números entre 1 e 6" dododice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

it "deve armazenar número da última jogada." dododice == DiceDice.newnewdice.playplay

Capítulo 1: Fazendo a classe Dice usando BDD

19

Page 20: Aprenda bdd-jogando-dados-ebook

dice.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)endend

xit "deve jogar o dado no momento que o objeto for inicializado"endend

classclass Dicedefdef playplay

randrand(6) ++ 1endend

defdef show_numbershow_number3

endendendend

TROCANDO CONSTANTE POR VARIÁVEL

Essa é uma regra importante no BDD. Agora você pode mudar a constante por uma variável de instância naclasse dados. O teste continua passando.

classclass Dicedefdef playplay

@number == randrand(6) ++ 1endend

defdef show_numbershow_number@number

endendendend

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

20

Page 21: Aprenda bdd-jogando-dados-ebook

JOGAR O DADO NA INICIALIZAÇÃO DO OBJETO

Agora eu quero que o dado seja jogado quando ele for inicializado. O próximo teste quebrará.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodo

it "deve ter somente números entre 1 e 6" dododice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

it "deve armazenar número da última jogada." dododice == DiceDice.newnewdice.playplaydice.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

it "deve jogar o dado na inicialização do objeto." dodoDiceDice.newnew.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

Capítulo 1: Fazendo a classe Dice usando BDD

21

Page 22: Aprenda bdd-jogando-dados-ebook

endend

endend

Output:

...F

1)'Dice deve jogar o dado na inicialização do objeto.' FAILEDexpected "" to match /^[1-6]*[1-6]$/.//spec//dice_spec.rbrb:29:

Finished inin 0.12371 seconds

4 examples, 1 failure

USANDO O MÉTODO INITIALIZEINITIALIZE

Agora eu finalizo a classe Dice colocando o método initialize com uma mensagem para o método play. Oteste passa.

classclass Dicedefdef initializeinitialize

playendend

defdef playplay@number == randrand(6) ++ 1

endend

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

22

Page 23: Aprenda bdd-jogando-dados-ebook

defdef show_numbershow_number@number

endendendend

VAMOS REFATORAR

Agora eu posso fazer uma pequena refatoração no arquivo dice_spec.rb. Eu colocarei um blocobefore(:each) para simplificar o código. Farei também uma refatoração no terceiro requisito: "devearmazenar número da última jogada". Agora ele funciona com uma grande quantidade de números. A lógicaé se o último número é armazenado então dois grupos de 100 números são diferentes entre si.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodobeforebefore(::eacheach) dodo

@dice == DiceDice.newnewendend

it "deve ter somente números entre 1 e 6" dodogroup == (1..1_000).collectcollect{ @dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Três grupos de 1000 números aleatórios devem ser diferentes um do outroit "deve mostrar números de forma aleatória" dodo

group1 == (1..1_000).collectcollect{ @dice.playplay }group2 == (1..1_000).collectcollect{ @dice.playplay }group3 == (1..1_000).collectcollect{ @dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false

Capítulo 1: Fazendo a classe Dice usando BDD

23

Page 24: Aprenda bdd-jogando-dados-ebook

(group2 ==== group3).shouldshould be_falseendend

it "deve armazenar número da última jogada." dodogroup1 == (1..100).collectcollect dodo

@[email protected]_numbershow_number

endendgroup2 == (1..100).collectcollect dodo

@[email protected]_numbershow_number

endend(group1 ==== group2).shouldshould be_false

endend

it "deve jogar o dado na inicialização do objeto." [email protected]_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

endend

BRINCANDO COM A CLASSE DICE

Agora que a classe foi finalizada vamos brincar com ela no irb!

>>>> requirerequire 'dice'=> truetrue>>>> dice == DiceDice.newnew=> #<Dice:0xb7a64188 @number=5>>>>> dice.show_numbershow_number=> 5

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

24

Page 25: Aprenda bdd-jogando-dados-ebook

>>>> dice.classclass=> Dice

Jogando o dado somente uma vez e deixando ele de lado:

>>>> DiceDice.newnew.show_numbershow_number=> 2

Jogar o dado várias vezes:

>>>> 20.timestimes{ print dice.playplay }21636456135236136236=> 20

Três dados:

>>>> yellowdice == [DiceDice.newnew, DiceDice.newnew, DiceDice.newnew]=> [#<Dice:0xb7a3e3d4 @number=3>, #<Dice:0xb7a3e3ac @number=5>, #<Dice:0xb7a3e384 @number=5>]>>>> yellowdice.eacheach{ |dice| puts dice.show_numbershow_number }355=> [#<Dice:0xb7a3e3d4 @number=3>, #<Dice:0xb7a3e3ac @number=5>, #<Dice:0xb7a3e384 @number=5>]

Jogar novamente o mesmo dado

>>>> yellowdice.eacheach{ |dice| puts dice.playplay }525=> [#<Dice:0xb7a3e3d4 @number=5>, #<Dice:0xb7a3e3ac @number=2>, #<Dice:0xb7a3e384 @number=5>]

Qual o maior valor entre os três dados da última jogada?

Capítulo 1: Fazendo a classe Dice usando BDD

25

Page 26: Aprenda bdd-jogando-dados-ebook

>>>> puts yellowdice.collectcollect{ |item| item.show_numbershow_number }.maxmax5=> nilnil

E qual o menor valor?

>>>> puts yellowdice.collectcollect{ |item| item.show_numbershow_number }.minmin2=> nilnil

Espere um pouco. No último exemplo eu usei 3 dados?!? Ahaaa! Isso parece com os dados do jogo War.Nós criaremos os dados do jogo war no próximo capítulo.

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

26

Page 27: Aprenda bdd-jogando-dados-ebook

Capítulo 2

Reproduzindo os dados do jogo War

DESCRIÇÃO DO JOGO WAR

Finalmente terminei a classe Dice! Mas eu quero algo mais excitante! Eu quero reproduzir em uma classeRuby os dados do jogo War. Isso mesmo! Aqueles 6 dados. 3 vermelhos e 3 amarelos que representamataque e defesa respectivamente.

Eu usarei dois arrays: reddice e yellowdice para armazenar os valores dos dados. Lembrei agora que o usodos dados depende do número de exércitos do atacante e do defensor. Mas nesse experimento eu pensareisomente na manipulação dos dados no jogo. Eles podem ser manuseados com 1, 2 ou 3 dados vermelhoscontra 1, 2 ou 3 dados amarelos. Eu terei que comparar o maior valor dos dados vermelhos com o maiorvalor entre os dados amarelos e assim sucessivamente com os demais dados (do maior para o menor valor).Em caso de empate o amarelo vence. O vermelho só vence quando o número for maior que o amarelo.

Capítulo 2: Reproduzindo os dados do jogo War

27

Page 28: Aprenda bdd-jogando-dados-ebook

Bem, eu acho que é só. Vamos agir agora!

TESTANDO COMPARAÇÃO DE ARRAYS

Nos primeiros testes que eu fiz em meu computador, tive problemas com comparação de arrays. Paralelo aisso me lembrei que é imprescindível o uso de comparação de arrays na classe WarDice. Vamos incluir umteste de comparação de arrays no arquivo dice_spec.rb e verificar o comportamento:

describe Array dodoit "deve ser comparável" dodo

([8,9,10] >> [1,2,3]).shouldshould be_trueendend

endend

Output:

....F

1)NoMethodError inin 'Array deve ser comparável'undefined method `>' for [8, 9, 10]:Array./spec/dice_spec.rb:43:

Finished in 0.039464 seconds

5 examples, 1 failure

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

28

Page 29: Aprenda bdd-jogando-dados-ebook

INCLUINDO O MÓDULO COMPARABLECOMPARABLE

Para solucionar o problema anterior, apenas inclua o módulo comparable no arquivo dice.rb. Eu farei issousando uma forma simplificada para brincar com as possibilidades do Ruby :).

classclass Array; includeinclude Comparable; endend

Agora o teste passa.

NÚMERO DE DADOS

Agora vou trabalhar no primeiro requisito: permite a utilização de 1, 2 or 3 dados, para o ataque oudefesa.. Na classe WarDice eu vou colocar de propósito um número diferente de 1, 2, 3 para gerar um erro.

describe Wardice dodoit "permite a utilização de 1, 2 or 3 dados, para o ataque ou defesa." dodo

wardice == WardiceWardice.newnew(0, 3)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

endend

it "compara os valores do maior para o menor e salva em um array"endend

classclass Wardiceattr_readerattr_reader ::redred, ::yellowyellow

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow

Capítulo 2: Reproduzindo os dados do jogo War

29

Page 30: Aprenda bdd-jogando-dados-ebook

endendendend

Output:

'Wardice permite a utilização de 1, 2 or 3 dados, para o ataque ou defesa.' FAILEDexpected "0" to match /^[1-3]$/.//spec//dice_spec.rbrb:50:

Finished inin 0.032203 seconds

8 examples, 1 failure, 2 pending

RESOLVENDO O PROBLEMA DOS NÚMEROS

Lógica: Se a classe for inicializada com números diferentes da lista 1-3 então as variáveis serão preenchidascom um número aleatório variando de 1 a 3. Eu farei também uma pequena refatoração no arquivo specpara que ele fique preparado para a situação em que a quantidade de dados for correta. Você pode ver asolução abaixo:

classclass WarDiceattr_readerattr_reader ::redred, ::yellowyellow

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

endendendend

describe WarDice dodo

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

30

Page 31: Aprenda bdd-jogando-dados-ebook

it "permite a utilização de 1, 2 or 3 dados, para o ataque ou defesa." dodowardice == WardiceWardice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WardiceWardice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

it "compara os valores do maior para o menor e salva em um array"endend

ARRAY EM ORDEM DECRESCENTE

Esse requisito é muito importante, mas só lembrei dele agora: wardice deve fornecer os resultados dosdados vermelho e amarelo em arrays em ordem decrescente. Você pode ver a solução abaixo:

describe WarDice dodoit "permite a utilização de 1, 2 or 3 dados, para o ataque ou defesa." dodo

wardice == WarDiceWarDice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WarDiceWarDice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

it "deve fornecer os resultados dos dados vermelho e amarelo em arrays em ordem decrescente" dodowardice == WardiceWardice.newnew(3, 3)wardice.reddicereddice.is_a?is_a?(Array).shouldshould be_true

Capítulo 2: Reproduzindo os dados do jogo War

31

Page 32: Aprenda bdd-jogando-dados-ebook

wardice.yellowdiceyellowdice.is_a?is_a?(Array).shouldshould be_true

wardice.reddicereddice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.reddicereddicewardice.yellowdiceyellowdice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.yellowdiceyellowdice

endend

it "compara os valores do maior para o menor e salva em um array"

endend

classclass wardiceattr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

endendendend

Output:

'wardice deve fornecer os resultados dos dados vermelho e amarelo em arrays em ordem decrescente' FAILEDexpected: [[5], [2], [4]],

got: [[5], [4], [2]] (using ====).//spec//dice_spec.rbrb:63:

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

32

Page 33: Aprenda bdd-jogando-dados-ebook

Finished inin 0.035218 seconds

9 examples, 1 failure, 2 pending

SOLUCIONANDO O PROBLEMA DO ARRAY EM ORDEM DECRESCENTEclassclass WarDice

attr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

@reddice.sort!sort!{|x,y| y <=><=> x }@yellowdice.sort!sort!{|x,y| y <=><=> x }

endendendend

Agora o teste passa.

Capítulo 2: Reproduzindo os dados do jogo War

33

Page 34: Aprenda bdd-jogando-dados-ebook

COMPARANDO OS VALORES

Vamos trabalhar no último requisito: compara os valores do maior para o menor e salva em um array.

describe Wardice dodoit "permite a utilização de 1, 2 or 3 dados, para o ataque ou defesa." dodo

wardice == WardiceWardice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WardiceWardice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

it "deve fornecer os resultados dos dados vermelho e amarelo em arrays em ordem decrescente" dodowardice == WardiceWardice.newnew(3, 3)wardice.reddicereddice.is_a?is_a?(Array).shouldshould be_truewardice.yellowdiceyellowdice.is_a?is_a?(Array).shouldshould be_true

wardice.reddicereddice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.reddicereddicewardice.yellowdiceyellowdice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.yellowdiceyellowdice

endend

it "compara os valores do maior para o menor e salva em um array" dodowardice == WardiceWardice.newnew(3, 2)wardice.reddicereddice.firstfirst.shouldshould >> wardice.reddicereddice.lastlastwardice.attackattack

wardice.resultresult[0].shouldshould ==== "Red Win" ifif wardice.reddicereddice[0] >> wardice.yellowdiceyellowdice[0]wardice.resultresult[0].shouldshould ==== "Yellow Win" ifif wardice.reddicereddice[0] <=<= wardice.yellowdiceyellowdice[0]

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

34

Page 35: Aprenda bdd-jogando-dados-ebook

endendendend

A classe WarDice com os testes passando por completo:

classclass Wardiceattr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice, ::resultresult

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []@result == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

@reddice.sort!sort!{|x, y| y <=><=> x }@yellowdice.sort!sort!{|x, y| y <=><=> x }

endend

defdef [email protected]_with_indexeach_with_index dodo |item, index|

nextnext ifif @yellowdice[index].nil?nil?reddice == itemyellowdice == @yellowdice[index]

ifif reddice >> yellowdice@result <<<< "Vermelho Venceu!"

elseelse

Capítulo 2: Reproduzindo os dados do jogo War

35

Page 36: Aprenda bdd-jogando-dados-ebook

@result <<<< "Amarelo Venceu!"endend

endendendend

endend

BRINCANDO COM A CLASSE WARDICE

Abra o irb e digite a sequência abaixo:

>>>> requirerequire 'dice'=> truetrue

Inicialize a classe WarDice. O primeiro parâmetro é o número de dados vermelhos e o segundo é o númerode dados amarelos:

>>>> wardice == WarDiceWarDice.newnew(2, 3)=> #<WarDice:0xb7b0b410 @yellowdice=[[6], [1], [1]], @reddice=[[3], [1]], @dice=#<Dice:0xb7b0ae0c@number==6>>, yellow3, result["Yellow Win", "Yellow Win"], red2

Mostra o números nos dados amarelos e vermelhos:

>>>> puts wardice.reddicereddice31=> nilnil>>>> puts wardice.yellowdiceyellowdice611=> nilnil

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

36

Page 37: Aprenda bdd-jogando-dados-ebook

Mostra o resultado:

>>>> puts wardice.resultresultYellow WinYellow Win=> nilnil

A saída completa em um único comando:

>>>> war.reddicereddice.each_with_indexeach_with_index{ |item, index| puts "Red:#{item} Yellow:#{war.yellowdiceyellowdice[index]}- #{war.resultresult[index]}"}Red:3 Yellow:6 -- Yellow WinRed:1 Yellow:1 -- Yellow Win=> [[3], [1]]

Agora já temos as ferramentas para brincar. Então no próximo capítulo nós usaremos essas classes parafazer uma bela aplicação gráfica usando Shoes.

Capítulo 2: Reproduzindo os dados do jogo War

37

Page 38: Aprenda bdd-jogando-dados-ebook

Capítulo 3

Jogando dados com Shoes

UM PEQUENO TESTE COM A CLASSE DICE E SHOES

Nesse capítulo vamos criar uma interface gráfica usando Shoes e a classe WarDice, mas primeiro vamosfazer um simples teste com a classe Dice. Você deve criar o arquivo diceshoes.rb, então cole o códigoabaixo dentro dele e salve dentro do mesmo diretório do arquivo dice.rb. Finalmente, execute ele com oShoes para ver o resultado.

Observe que eu apenas inclui o arquivo dice.rb com um require no começo do código abaixo:

requirerequire 'dice'ShoesShoes.appapp ::titletitle => "Teste com a classe Dice", ::widthwidth => 500, ::heightheight => 500 dodo

background aliceblue

para "Bem Vindo! Esse é um exemplo de uso da classe Dice.", ::weightweight => "bold"

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

38

Page 39: Aprenda bdd-jogando-dados-ebook

dice == DiceDice.newnew

# Imprime na tela os números dos dados jogados com cores aleatórias1_000.timestimes dodo

r, g, b == rand, rand, randpara dice.playplay, ::strokestroke => rgbrgb(r****3, g****3, b****3), ::sizesize => "large"

endendendend

Saída:

Capítulo 3: Jogando dados com Shoes

39

Page 40: Aprenda bdd-jogando-dados-ebook

O CÓDIGO SHOES DO APLICATIVO QUE USA A CLASSE WARDICE

Finalmente a última aplicação. copie o código seguinte em um novo arquivo e salve com o nomewardiceshoes.rb na pasta lib. Eu fiz alguns comentários através do código para facilitar a compreensão.Faça um bom experimento.

requirerequire 'dice'

ShoesShoes.appapp ::titletitle => "Dados do Jogo War", ::widthwidth => 500, ::heightheight => 500 dodobackground gradientgradient( black, teal )

# Listbox com a quantidade de dados vermelhos: 1, 2 or 3para "Red", ::strokestroke => tomato@numberreddice == list_box ::itemsitems => ["1", "2", "3"],

::widthwidth => 70, ::choosechoose => "3" dodo |list|endend

para " X ", ::strokestroke => snow

# Listbox com a quantidade de dados amarelos: 1, 2 or 3@numberyellowdice == list_box ::itemsitems => ["1", "2", "3"],

::widthwidth => 70, ::choosechoose => "3" dodo |list|endendpara "Yellow", ::strokestroke => yellow

# Define uma posição aleatória@a == @b == @c == []

(40..200).stepstep(10){ |x| @a <<<< x }(230..450).stepstep(10){ |y| @b <<<< y }(80..450).stepstep(10){ ||z|| @c <<<< z }

# Variáveis que vão armazenar os valores do objeto wardice.

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

40

Page 41: Aprenda bdd-jogando-dados-ebook

@reddice == @yellowdice == @resulttext == []

button "Attack", ::widthwidth => 80 dodo

# Limpa a [email protected]{ |d| d.removeremove }@resulttext.eacheach{ |a| a.removeremove }

# O objeto wardice is inicializadowardice == WarDiceWarDice.newnew( @numberreddice.texttext.to_ito_i, @numberyellowdice.texttext.to_ito_i )@reddice == wardice.reddicereddice@yellowdice == wardice.yellowdiceyellowdice@result == wardice.resultresult

# Cada dado é desenhado em uma posição aleató[email protected]{ |item| drawdraw( @a[randrand(@a.lengthlength)], @c[randrand(@c.lengthlength)], item.to_sto_s.to_ito_i, 1, truetrue ) }@yellowdice.eacheach{ |item| drawdraw( @b[randrand(@b.lengthlength)], @c[randrand(@c.lengthlength)], item.to_sto_s.to_ito_i, 2, truetrue )}

endend

button "Verify", ::widthwidth => 80 dodo

# Limpa a [email protected]{ |d| d.removeremove }@resulttext.eacheach{ |a| a.removeremove }

# posição inicial dos dadosleftyellow == 250leftred == 150topred == topyellow == 100

# Cada dado é desenhado em uma posição [email protected] dodo |item|

drawdraw( leftred, topred, item.to_sto_s.to_ito_i, 1, falsefalse )

Capítulo 3: Jogando dados com Shoes

41

Page 42: Aprenda bdd-jogando-dados-ebook

topred +=+= 100endend

@yellowdice.eacheach dodo |item|drawdraw( leftyellow, topyellow, item.to_sto_s.to_ito_i, 2, falsefalse )topyellow +=+= 100

endend

# Posição inicial para a lista com o resultadoleftresult == 300topresult == 80

# Os resultados são impressos na tela em uma posição [email protected]_with_indexeach_with_index dodo |item, index|

@resulttext[index] == para item.to_sto_s, ::strokestroke => snow, ::toptop => topresult, ::leftleft => leftresulttopresult +=+= 100

endend

endend

# O método draw foi baseado no projeto Pretty Dice, escrito por Ed Heil@dice == []

defdef drawdraw( left, top, number, color, rotate )

imagewidth == 60imageheight == 60

i == imageimage( imagewidth, imageheight,::toptop => top -- imagewidth // 2,::leftleft => left -- imagewidth // 2,::shadowshadow => 10, ::centercenter => truetrue ) dodo

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

42

Page 43: Aprenda bdd-jogando-dados-ebook

ifif color ==== 1strokecolor == redfillrectanglecolor == tomatofilldotscolor == darkred

elseelsestrokecolor == yellowfillrectanglecolor == lightgoldenrodyellowfilldotscolor == chocolate

endend

sw == 1strokewidth swstroke strokecolor

fill fillrectanglecolor

inset == 2inset2 == 8

rectrect( inset, inset, imagewidth--inset--sw, imageheight--inset--sw, 10 )

fill filldotscolor

ovalradius == 10low == inset2high == imagewidth -- inset2 -- ovalradiusmid == ( imagewidth -- ovalradius ) // 2

ovaloval( mid, mid, ovalradius ) ifif number %% 2 ==== 1

ifif number >> 1ovaloval( low, low, ovalradius )ovaloval( high, high, ovalradius )

endend

Capítulo 3: Jogando dados com Shoes

43

Page 44: Aprenda bdd-jogando-dados-ebook

ifif number >> 3ovaloval( low, high, ovalradius )ovaloval( high, low, ovalradius )

endend

ifif number >> 5ovaloval( mid, low, ovalradius )ovaloval( mid, high, ovalradius )

endend

endend # fim do bloco `image`

i.rotaterotate( randrand( 359 ) ) ifif rotate

@dice <<<< i

endend

endend

BRINCANDO COM O APLICATIVO

Abaixo você pode ver o aplicativo em ação. Você deve escolher o número de dados vermelhos e amarelosno respectivo listbox. Depois clique no botão ataque para jogar os dados. Agora é só se divertir!

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

44

Page 45: Aprenda bdd-jogando-dados-ebook

Agora clique no botão verificar para saber quem venceu!

Capítulo 3: Jogando dados com Shoes

45

Page 46: Aprenda bdd-jogando-dados-ebook

Aprenda BDD Jogando Dados! - Seu comportamento é se divertir

46