{"id":7,"date":"2006-05-07T23:11:27","date_gmt":"2006-05-07T22:11:27","guid":{"rendered":"http:\/\/www.dtsato.com\/blog\/default\/Agile\/XP\/?permalink=TDD-em-Combate-Parte-2.html"},"modified":"2006-05-07T23:11:27","modified_gmt":"2006-05-07T22:11:27","slug":"tdd-em-combate-parte-2","status":"publish","type":"post","link":"https:\/\/www.dtsato.com\/blog\/2006\/05\/07\/tdd-em-combate-parte-2\/","title":{"rendered":"TDD em Combate &#8211; Parte 2"},"content":{"rendered":"<p>\u00c9 hora de codificar um pouco: mostraremos o passo-a-passo de como programar no ritmo de <strong style=\"font-weight: bold;\">TDD<\/strong>, desenvolvendo as regras de ataque do jogo <strong style=\"font-weight: bold;\">Combate<\/strong>. Vamos identificar tamb\u00e9m a preocupa\u00e7\u00e3o constante com o Design Simples, e como ele se manifesta quando desenvolvemos os testes antes do c\u00f3digo.<\/p>\n<h3 class=\"heading-1\">Pensando no Design<\/h3>\n<p>No <span class=\"nobr\"><a href=\"http:\/\/www.dtsato.com\/blog\/default\/Agile\/XP\/?permalink=TDD-em-Combate-Parte-1.html&amp;smm=y\">\u00faltimo post<\/a><\/span>, conhecemos as pe\u00e7as que comp\u00f5em o jogo e as respectivas regras de ataque\/defesa. Como a id\u00e9ia de hoje \u00e9 implementarmos essas regras de ataque, precisamos pensar um pouco sobre como modelar nossas classes (lembrem-se, \u00e9 permitido pensar sobre o design antes de come\u00e7ar a implementa\u00e7\u00e3o. O que XP tenta evitar \u00e9 o chamado <span class=\"nobr\"><a href=\"http:\/\/www.c2.com\/cgi\/wiki?BigDesignUpFront\">BDUF<\/a><\/span> &#8211; <em style=\"font-style: italic;\">Big Design Up-Front<\/em> &#8211; onde o design do sistema inteiro \u00e9 definido e escrito num documento, com diversas p\u00e1ginas e diagramas de classe que ser\u00e1 lido por outra pessoa na hora da implementa\u00e7\u00e3o). O <span class=\"nobr\"><a href=\"http:\/\/www.dtsato.com\/blog\/default\/Agile\/XP\/?permalink=Design-Incremental-em-XP.wiki&amp;smm=y\">Design Incremental<\/a><\/span> de XP faz com que voc\u00ea se preocupe somente com a m\u00ednima quantidade de design necess\u00e1ria para atender suas necessidades atuais. No nosso caso, precisamos apenas pensar em onde implementar as regras de ataque.<\/p>\n<p>Um ataque pode ter 3 desfechos diferentes: a pe\u00e7a que atacou vence e ocupa o lugar da pe\u00e7a de defesa (que \u00e9 removida do tabuleiro); a pe\u00e7a que atacou perde e \u00e9 removida do tabuleiro ou ambas as pe\u00e7as s\u00e3o removidas do tabuleiro no caso de um empate. Minha primeira id\u00e9ia era definir um m\u00e9todo de ataque que retornasse a pe\u00e7a perdedora, por\u00e9m no caso do empate, eu precisaria retornar as duas pe\u00e7as, exigindo uma assinatura que devolvesse uma lista de pe\u00e7as ou um par de pe\u00e7as, o que n\u00e3o achei uma boa id\u00e9ia. Tive ent\u00e3o outra id\u00e9ia para implementa\u00e7\u00e3o: fazer com que o m\u00e9todo de ataque avisasse cada pe\u00e7a perdedora, numa esp\u00e9cie de m\u00e9todo <em style=\"font-style: italic;\">callback<\/em> sem retorno. Por\u00e9m, como um objeto Pe\u00e7a faria para se remover do tabuleiro ao descobrir que perdeu? Ele precisaria conhecer o Tabuleiro ou avisar o Tabuleiro, criando depend\u00eancia bi-direcional entre as classes, o que tamb\u00e9m n\u00e3o parece ser uma boa id\u00e9ia (nem estamos pensando no design do Tabuleiro ainda, s\u00f3 queremos implementar as regras de ataque).<\/p>\n<p>A solu\u00e7\u00e3o sugerida, depois de algumas discuss\u00f5es com o <em style=\"font-style: italic;\">RBP<\/em>, foi implementar um m\u00e9todo de compara\u00e7\u00e3o (no estilo de um cmp() em Python ou compareTo() em Java) que devolve 0 se houve empate, um n\u00famero positivo se a pe\u00e7a que est\u00e1 atacando venceu ou um n\u00famero negativo se a pe\u00e7a atacante perdeu. S\u00f3 faltava escolher onde esse m\u00e9todo seria definido. Minha primeira id\u00e9ia foi coloc\u00e1-lo numa classe Stratego que representa o jogo, por\u00e9m depois de alguns minutos de codifica\u00e7\u00e3o, percebi que o melhor lugar para definir esse m\u00e9todo seria na pr\u00f3pria pe\u00e7a. Uma pe\u00e7a sabe como se portar quando ataca um inimigo. A \u00faltima decis\u00e3o de design foi definir um <em style=\"font-style: italic;\">rank<\/em> num\u00e9rico para cada pe\u00e7a, para representar a hierarquia utilizada durante os ataques. Com a tabela seguinte em m\u00e3os, pudemos recome\u00e7ar a implementa\u00e7\u00e3o, no ritmo de <strong style=\"font-weight: bold;\">TDD<\/strong>:<\/p>\n<table class=\"wiki-table\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<th><em style=\"font-style: italic;\">Rank<\/em><\/th>\n<th>\u00a0<\/th>\n<th>Pe\u00e7a<\/th>\n<th>\u00a0<\/th>\n<th><em style=\"font-style: italic;\">Rank<\/em><\/th>\n<th>\u00a0<\/th>\n<th>Pe\u00e7a<\/th>\n<th>\u00a0<\/th>\n<th><em style=\"font-style: italic;\">Rank<\/em><\/th>\n<th>\u00a0<\/th>\n<th>Pe\u00e7a<\/th>\n<\/tr>\n<tr class=\"table-odd\">\n<td><strong style=\"font-weight: bold;\">0<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Bandeira<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">4<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Sargento<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">8<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Coronel<\/em><\/td>\n<\/tr>\n<tr class=\"table-even\">\n<td><strong style=\"font-weight: bold;\">1<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Espi\u00e3o<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">5<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Tenente<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">9<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">General<\/em><\/td>\n<\/tr>\n<tr class=\"table-odd\">\n<td><strong style=\"font-weight: bold;\">2<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Soldado<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">6<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Capit\u00e3o<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">10<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Marechal<\/em><\/td>\n<\/tr>\n<tr class=\"table-even\">\n<td><strong style=\"font-weight: bold;\">3<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Cabo-Armeiro<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">7<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Major<\/em><\/td>\n<td>\u00a0<\/td>\n<td><strong style=\"font-weight: bold;\">11<\/strong><\/td>\n<td>\u00a0<\/td>\n<td><em style=\"font-style: italic;\">Bomba<\/em><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"heading-1\">Criando as pe\u00e7as<\/h3>\n<p>O primeiro passo \u00e9 sempre escrever um teste que falha. Como queremos criar as pe\u00e7as acima, defini um conjunto de testes que verifica se as pe\u00e7as est\u00e3o sendo criadas com o <em style=\"font-style: italic;\">rank<\/em> esperado, num arquivo chamado StrategoTest.py (que armazenar\u00e1 os testes):<\/p>\n<pre lang=\"python\"># StrategoTest.py\nimport unittest\nimport unittestgui\nfrom Stratego import *\n\nclass CreatePieceTest(unittest.TestCase):\n    def testCreateSoldier(self):\n        assert Piece(\"soldier\").rank == 2\n\nsuite = unittest.makeSuite(CreatePieceTest)\n\nif __name__ == \"__main__\":\n    unittestgui.main(\"StrategoTest.suite\")<\/pre>\n<p>Esse c\u00f3digo n\u00e3o compila e n\u00e3o consegue ser executado. Percebam como o teste pede pela codifica\u00e7\u00e3o: definindo um construtor para pe\u00e7a que recebe uma string, definindo uma propriedade rank para a pe\u00e7a e definindo o m\u00f3dulo onde a classe ser\u00e1 definida. Com isso, precisamos do seguinte trecho de c\u00f3digo para termos nossa primeira barra verde (definido no arquivo Stratego.py):<\/p>\n<pre lang=\"python\"># Stratego.py\n\nclass Piece:\n    def __init__(self, name):\n        self.rank = 2<\/pre>\n<p>Essa \u00e9 a implementa\u00e7\u00e3o mais simples que faz o nosso teste passar. Percebam como o <strong style=\"font-weight: bold;\">TDD<\/strong> nos auxilia a evitar generaliza\u00e7\u00f5es precoces no c\u00f3digo. Mesmo sabendo que esse c\u00f3digo n\u00e3o atende a todas as necessidades, precisamos nos disciplinar e focar no m\u00ednimo necess\u00e1rio. O terceiro passo seria a refatora\u00e7\u00e3o, mas ainda n\u00e3o temos nenhum &#8220;mal-cheiro&#8221; no c\u00f3digo, ent\u00e3o podemos partir para o pr\u00f3ximo teste:<\/p>\n<pre lang=\"python\" highlight=\"6,7\"># StrategoTest.py\n\nclass CreatePieceTest(unittest.TestCase):\n    def testCreateSoldier(self):\n        assert Piece(\"soldier\").rank == 2\n    def testCreateMiner(self):\n        assert Piece(\"miner\").rank == 3<\/pre>\n<p>Ao executar a suite de testes, nos deparamos mais uma vez com uma barra vermelha. \u00c9 hora de implementarmos um pouco mais para torn\u00e1-la verde:<\/p>\n<pre lang=\"python\">\n# Stratego.py\nclass Piece:\n    def __init__(self, name):\n        if name == \"soldier\":\n            self.rank = 2\n        else:\n            self.rank = 3\n<\/pre>\n<p>Alguns podem dizer que o c\u00f3digo acima j\u00e1 est\u00e1 come\u00e7ando a cheirar mal, por\u00e9m ainda n\u00e3o vejo tanta repeti\u00e7\u00e3o, ent\u00e3o resolvo escrever o pr\u00f3ximo teste que falha:<\/p>\n<pre lang=\"python\" highlight=\"8,9\">\n# StrategoTest.py\n\nclass CreatePieceTest(unittest.TestCase):\n    def testCreateSoldier(self):\n        assert Piece(\"soldier\").rank == 2\n    def testCreateMiner(self):\n        assert Piece(\"miner\").rank == 3\n    def testCreateSergeant(self):\n        assert Piece(\"sergeant\").rank == 4\n<\/pre>\n<p>Para fazer com que os testes fiquem verdes novamente, preciso codificar mais um pouco:<\/p>\n<pre lang=\"python\">\n# Stratego.py\n\nclass Piece:\n    def __init__(self, name):\n        if name == \"soldier\":\n            self.rank = 2\n        elif name == \"miner\":\n            self.rank = 3\n        else:\n            self.rank = 4\n<\/pre>\n<p>Agora estou numa barra verde, por\u00e9m o c\u00f3digo n\u00e3o parece muito bonito. Fica evidente que a cada novo teste, uma nova ramifica\u00e7\u00e3o elif precisar\u00e1 ser adicionada ao c\u00f3digo do construtor. \u00c9 hora de refatorar (a regra \u00e9: <strong style=\"font-weight: bold;\">&#8220;sempre refatore no verde&#8221;<\/strong>), deixar o c\u00f3digo mais limpo e simples sem alterar seu comportamento, ou seja, sem quebrar nenhum dos testes existentes:<\/p>\n<pre lang=\"python\">\n# Stratego.py\n\nclass Piece:\n    __pieces = {\"soldier\":2, \"miner\":3, \"sergeant\":4}\n    def __init__(self, name):\n        self.rank = self.__pieces[name]\n<\/pre>\n<p>Definindo um dicion\u00e1rio com as poss\u00edveis pe\u00e7as, o c\u00f3digo do construtor fica mais simples, pois se transforma numa simples consulta ao dicion\u00e1rio. Seguindo o mesmo ritmo para construir as outras pe\u00e7as e definindo uma exce\u00e7\u00e3o para a tentativa de cria\u00e7\u00e3o de uma pe\u00e7a inv\u00e1lida, o <strong style=\"font-weight: bold;\">Desenvolvimento Orientado a Testes<\/strong> nos leva ao seguinte c\u00f3digo final:<\/p>\n<pre lang=\"python\">\n# StrategoTest.py\n\nimport unittest\nimport unittestgui\nfrom Stratego import *\nclass CreatePieceTest(unittest.TestCase):\n    def testCreateSoldier(self):\n        assert Piece(\"soldier\").rank == 2\n    def testCreateMiner(self):\n        assert Piece(\"miner\").rank == 3\n    def testCreateSergeant(self):\n        assert Piece(\"sergeant\").rank == 4\n    def testCreateLieutenant(self):\n        assert Piece(\"lieutenant\").rank == 5\n    def testCreateCaptain(self):\n        assert Piece(\"captain\").rank == 6\n    def testCreateMajor(self):\n        assert Piece(\"major\").rank == 7\n    def testCreateColonel(self):\n        assert Piece(\"colonel\").rank == 8\n    def testCreateGeneral(self):\n        assert Piece(\"general\").rank == 9\n    def testCreateMarshal(self):\n        assert Piece(\"marshal\").rank == 10\n    def testCreateSpy(self):\n        assert Piece(\"spy\").rank == 1\n    def testCreateBomb(self):\n        assert Piece(\"bomb\").rank == 11\n    def testCreateFlag(self):\n        assert Piece(\"flag\").rank == 0\n    def testCreateInvalidPiece(self):\n        try:\n            Piece(\"invalid\")\n        except InvalidPiece:\n            pass\n        else:\n            fail(\"expected InvalidPiece exception\")\n\nsuite = unittest.makeSuite(CreatePieceTest)\n\nif __name__ == \"__main__\":\n    unittestgui.main(\"StrategoTest.suite\")\n\n# Stratego.py\n\nclass Piece:\n    __pieces = {\"flag\":0, \"spy\":1, \"soldier\":2, \"miner\":3, \"sergeant\":4,\n                \"lieutenant\":5, \"captain\":6, \"major\":7, \"colonel\":8,\n                \"general\":9, \"marshal\":10, \"bomb\":11}\n    def __init__(self, name):\n        try:\n            self.rank = self.__pieces[name]\n        except KeyError:\n            raise InvalidPiece()\n\nclass InvalidPiece(Exception):\n    pass\n<\/pre>\n<h3 class=\"heading-1\">Implementando o m\u00e9todo de ataque<\/h3>\n<p>Agora que temos as pe\u00e7as criadas, podemos implementar as regras de ataque. Apenas para relembrar, as pe\u00e7as com maior <em style=\"font-style: italic;\">rank<\/em> vencem as de menor <em style=\"font-style: italic;\">rank<\/em>. Algumas exce\u00e7\u00f5es \u00e0 regra s\u00e3o:<br \/>\n\u00a0<\/p>\n<ul class=\"star\">\n<li>O espi\u00e3o vence o marechal quando o ataca<\/li>\n<li>As bombas e a bandeira n\u00e3o podem atacar<\/li>\n<li>Qualquer pe\u00e7a que atacar a bomba perde, exceto o cabo-armeiro<\/li>\n<\/ul>\n<p>Para n\u00e3o perder o costume, come\u00e7amos com um teste que falha:<\/p>\n<pre lang=\"python\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n<\/pre>\n<p>Rapidamente, fazemos o teste passar:<\/p>\n<pre lang=\"python\" highlight=\"5,6\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        return 1\n    # (...) Exceptions\n<\/pre>\n<p>Novamente, apesar desse c\u00f3digo n\u00e3o ser o que desejamos, \u00e9 o suficiente para fazer o teste passar e para permitir que possamos implementar o pr\u00f3ximo teste:<\/p>\n<pre lang=\"python\" highlight=\"6,7\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n<\/pre>\n<p>Mais uma vez, implementamos o mais simples para fazer o teste passar:<\/p>\n<pre lang=\"python\" highlight=\"5,6\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        return self.rank - defender.rank\n    # (...) Exceptions\n<\/pre>\n<p>O teste para verificar uma situa\u00e7\u00e3o de empate j\u00e1 funciona sem nenhuma mudan\u00e7a na implementa\u00e7\u00e3o, assim como o teste para confirmar que o marechal vence o espi\u00e3o quando o ataca. Isso pode acontecer \u00e0s vezes, permitindo que passemos para o pr\u00f3ximo teste que deixar\u00e1 a barra de testes vermelha. Isso acontece com o teste que verifica se o espi\u00e3o vence um ataque contra o marechal:<\/p>\n<pre lang=\"python\" highlight=\"8,9,10,11,12,13\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n    def testTie(self):\n        assert Piece(\"major\").attack(Piece(\"major\")) == 0\n    def testMarshalWinsSpy(self):\n        assert Piece(\"marshal\").attack(Piece(\"spy\")) > 0\n    def testSpyWinsMarshal(self):\n        assert Piece(\"spy\").attack(Piece(\"marshal\")) > 0\n<\/pre>\n<p>Uma simples valida\u00e7\u00e3o do caso excepcional, faz com que os testes funcionem novamente:<\/p>\n<pre lang=\"python\" highlight=\"5,6,7,8,9\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        if self.rank == 1 and defender.rank == 10:\n            return 1\n        else:\n            return self.rank - defender.rank\n    # (...) Exceptions\n<\/pre>\n<p>O pr\u00f3ximo caso excepcional acontece quando o cabo-armeiro ataca uma bomba:<\/p>\n<pre lang=\"python\" highlight=\"14,15\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n    def testTie(self):\n        assert Piece(\"major\").attack(Piece(\"major\")) == 0\n    def testSpyWinsMarshal(self):\n        assert Piece(\"spy\").attack(Piece(\"marshal\")) > 0\n    def testMarshalWinsSpy(self):\n        assert Piece(\"marshal\").attack(Piece(\"spy\")) > 0\n    def testMinerWinsBomb(self):\n        assert Piece(\"miner\").attack(Piece(\"bomb\")) > 0\n<\/pre>\n<p>O necess\u00e1rio para faz\u00ea-lo passar \u00e9 adicionar mais uma valida\u00e7\u00e3o para o novo caso excepcional:<\/p>\n<pre lang=\"python\" highlight=\"5,6,7,8,9\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        if (self.rank == 1 and defender.rank == 10) or (self.rank == 3 and defender.rank == 11):\n            return 1\n        else:\n            return self.rank - defender.rank\n    # (...) Exceptions\n<\/pre>\n<p>Com a barra verde, podemos refatorar o c\u00f3digo. Nesse caso, podemos eliminar a duplica\u00e7\u00e3o na verifica\u00e7\u00e3o dos casos excepcionais criando uma lista dos pares que vencem quando atacam (apesar de possu\u00edrem um <em style=\"font-style: italic;\">rank<\/em> menor). O c\u00f3digo refatorado fica assim:<\/p>\n<pre lang=\"python\" highlight=\"7,10\">\n# Stratego.py\n\nclass Piece:\n    __pieces = {\"flag\":0, \"spy\":1, \"soldier\":2, \"miner\":3, \"sergeant\":4,\n                \"lieutenant\":5, \"captain\":6, \"major\":7, \"colonel\":8,\n                \"general\":9, \"marshal\":10, \"bomb\":11}\n    __winsAttacking = [(1,10), (3,11)]\n    # (...) Constructor\n    def attack(self, defender):\n        if (self.rank,defender.rank) in self.__winsAttacking:\n            return 1\n        else:\n            return self.rank - defender.rank\n    # (...) Exceptions\n<\/pre>\n<p>Para terminarmos nossa implementa\u00e7\u00e3o, s\u00f3 falta lidar os casos de ataque inv\u00e1lido. Primeiro vamos escrever um teste para garantir que uma bomba n\u00e3o pode ter iniciativa de ataque:<\/p>\n<pre lang=\"python\" highlight=\"16,17,18,19,20,21,22\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n    def testTie(self):\n        assert Piece(\"major\").attack(Piece(\"major\")) == 0\n    def testSpyWinsMarshal(self):\n        assert Piece(\"spy\").attack(Piece(\"marshal\")) > 0\n    def testMarshalWinsSpy(self):\n        assert Piece(\"marshal\").attack(Piece(\"spy\")) > 0\n    def testMinerWinsBomb(self):\n        assert Piece(\"miner\").attack(Piece(\"bomb\")) > 0\n    def testBombCannotAtack(self):\n        try:\n            Piece(\"bomb\").attack(Piece(\"miner\"))\n        except InvalidAttack:\n            pass\n        else:\n            fail(\"expected InvalidAttack exception\")\n<\/pre>\n<p>E o c\u00f3digo para fazer o teste passar \u00e9 uma nova valida\u00e7\u00e3o no m\u00e9todo attack():<\/p>\n<pre lang=\"python\" highlight=\"6,7\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        if self.rank == 11:\n            raise InvalidAttack()\n        if (self.rank,defender.rank) in self.__winsAttacking:\n            return 1\n        else:\n            return self.rank - defender.rank\n    # (...) Exceptions\n\nclass InvalidAttack(Exception):\n    pass\n<\/pre>\n<p>O \u00faltimo teste \u00e9 para evitar que uma bandeira tenha iniciativa de ataque:<\/p>\n<pre lang=\"python\" highlight=\"23,24,25,26,27,28,29\">\n# StrategoTest.py\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n    def testTie(self):\n        assert Piece(\"major\").attack(Piece(\"major\")) == 0\n    def testSpyWinsMarshal(self):\n        assert Piece(\"spy\").attack(Piece(\"marshal\")) > 0\n    def testMarshalWinsSpy(self):\n        assert Piece(\"marshal\").attack(Piece(\"spy\")) > 0\n    def testMinerWinsBomb(self):\n        assert Piece(\"miner\").attack(Piece(\"bomb\")) > 0\n    def testBombCannotAtack(self):\n        try:\n            Piece(\"bomb\").attack(Piece(\"miner\"))\n        except InvalidAttack:\n            pass\n        else:\n            fail(\"expected InvalidAttack exception\")\n    def testFlagCannotAtack(self):\n        try:\n            Piece(\"flag\").attack(Piece(\"lieutenant\"))\n        except InvalidAttack:\n            pass\n        else:\n            fail(\"expected InvalidAttack exception\")\n<\/pre>\n<p>Podemos adicionar uma nova valida\u00e7\u00e3o para evitar o ataque de uma bandeira:<\/p>\n<pre lang=\"python\" highlight=\"6,7\">\n# Stratego.py\n\nclass Piece:\n    # (...) Constructor\n    def attack(self, defender):\n        if self.rank == 11 or self.rank == 0:\n            raise InvalidAttack()\n        if (self.rank,defender.rank) in self.__winsAttacking:\n            return 1\n        else:\n            return self.rank - defender.rank\n    # (...) Exceptions\n<\/pre>\n<p>Para finalizar, podemos aproveitar a barra verde para remover uma \u00faltima duplica\u00e7\u00e3o, na valida\u00e7\u00e3o das pe\u00e7as que n\u00e3o podem atacar. Ap\u00f3s a refatora\u00e7\u00e3o, o c\u00f3digo completo e final fica assim:<\/p>\n<pre lang=\"python\" highlight=\"80,89,90\">\n# StrategoTest.py\n\nimport unittest\nimport unittestgui\nfrom Stratego import *\nclass CreatePieceTest(unittest.TestCase):\n    def testCreateSoldier(self):\n        assert Piece(\"soldier\").rank == 2\n    def testCreateMiner(self):\n        assert Piece(\"miner\").rank == 3\n    def testCreateSergeant(self):\n        assert Piece(\"sergeant\").rank == 4\n    def testCreateLieutenant(self):\n        assert Piece(\"lieutenant\").rank == 5\n    def testCreateCaptain(self):\n        assert Piece(\"captain\").rank == 6\n    def testCreateMajor(self):\n        assert Piece(\"major\").rank == 7\n    def testCreateColonel(self):\n        assert Piece(\"colonel\").rank == 8\n    def testCreateGeneral(self):\n        assert Piece(\"general\").rank == 9\n    def testCreateMarshal(self):\n        assert Piece(\"marshal\").rank == 10\n    def testCreateSpy(self):\n        assert Piece(\"spy\").rank == 1\n    def testCreateBomb(self):\n        assert Piece(\"bomb\").rank == 11\n    def testCreateFlag(self):\n        assert Piece(\"flag\").rank == 0\n    def testCreateInvalidPiece(self):\n        try:\n            Piece(\"invalid\")\n        except InvalidPiece:\n            pass\n        else:\n            fail(\"expected InvalidPiece exception\")\n\nclass AttackTest(unittest.TestCase):\n    def testHigherRankWins(self):\n        assert Piece(\"sergeant\").attack(Piece(\"soldier\")) > 0\n    def testLowerRankLoses(self):\n        assert Piece(\"miner\").attack(Piece(\"colonel\")) < 0\n    def testTie(self):\n        assert Piece(\"major\").attack(Piece(\"major\")) == 0\n    def testSpyWinsMarshal(self):\n        assert Piece(\"spy\").attack(Piece(\"marshal\")) > 0\n    def testMarshalWinsSpy(self):\n        assert Piece(\"marshal\").attack(Piece(\"spy\")) > 0\n    def testMinerWinsBomb(self):\n        assert Piece(\"miner\").attack(Piece(\"bomb\")) > 0\n    def testBombCannotAtack(self):\n        try:\n            Piece(\"bomb\").attack(Piece(\"miner\"))\n        except InvalidAttack:\n            pass\n        else:\n            fail(\"expected InvalidAttack exception\")\n    def testFlagCannotAtack(self):\n        try:\n            Piece(\"flag\").attack(Piece(\"lieutenant\"))\n        except InvalidAttack:\n            pass\n        else:\n            fail(\"expected InvalidAttack exception\")\n\nsuite = unittest.TestSuite((unittest.makeSuite(CreatePieceTest),\n                            unittest.makeSuite(AttackTest)))\n\nif __name__ == \"__main__\":\n    unittestgui.main(\"StrategoTest.suite\")\n\n# Stratego.py\n\nclass Piece:\n    __pieces = {\"flag\":0, \"spy\":1, \"soldier\":2, \"miner\":3, \"sergeant\":4,\n                \"lieutenant\":5, \"captain\":6, \"major\":7, \"colonel\":8,\n                \"general\":9, \"marshal\":10, \"bomb\":11}\n    __winsAttacking = [(1,10), (3,11)]\n    __cannotAttack = [0,11]\n\n    def __init__(self, name):\n        try:\n            self.rank = self.__pieces[name]\n        except KeyError:\n            raise InvalidPiece()\n\n    def attack(self, defender):\n        if self.rank in self.__cannotAttack:\n            raise InvalidAttack()\n        if (self.rank,defender.rank) in self.__winsAttacking:\n            return 1\n        else:\n            return self.rank - defender.rank\n\nclass InvalidPiece(Exception):\n    pass\n\nclass InvalidAttack(Exception):\n    pass\n<\/pre>\n<h3 class=\"heading-1\">Conclus\u00f5es e pr\u00f3ximos passos<\/h3>\n<p>Terminamos o segundo post com 70 linhas de c\u00f3digo de teste e 26 linhas de c\u00f3digo de produ\u00e7\u00e3o. Como voc\u00eas puderam notar, o ritmo imposto pelo <strong style=\"font-weight: bold;\">TDD<\/strong> \u00e9 baseado em passos pequenos e resultou num c\u00f3digo simples e comunicativo. Por enquanto, temos apenas uma classe para representar uma pe\u00e7a do jogo. No pr\u00f3ximo post implementaremos as regras de movimenta\u00e7\u00e3o no tabuleiro, n\u00e3o percam!<\/p>\n<p><em style=\"font-style: italic;\">Atualiza\u00e7\u00e3o 03-Out-06<\/em>: Conforme sugest\u00f5es, estou disponibilizando para download o c\u00f3digo fonte final dos <span class=\"nobr\"><a href=\"http:\/\/www.dtsato.com\/resources\/default\/code_sample\/tdd_2\/StrategoTest.py\">testes<\/a><\/span> e das <span class=\"nobr\"><a href=\"http:\/\/www.dtsato.com\/resources\/default\/code_sample\/tdd_2\/Stratego.py\">classes de produ\u00e7\u00e3o<\/a><\/span> para os interessados n\u00e3o precisarem copiar\/colar\/formatar tudo novamente.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00c9 hora de codificar um pouco: mostraremos o passo-a-passo de como programar no ritmo de TDD, desenvolvendo as regras de ataque do jogo Combate. Vamos identificar tamb\u00e9m a preocupa\u00e7\u00e3o constante com o Design Simples, e como ele se manifesta quando desenvolvemos os testes antes do c\u00f3digo. Pensando no Design No \u00faltimo post, conhecemos as pe\u00e7as [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[58,80,82],"class_list":["post-7","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-python","tag-stratego","tag-tdd"],"_links":{"self":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts\/7","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/comments?post=7"}],"version-history":[{"count":0,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/posts\/7\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/media?parent=7"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/categories?post=7"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dtsato.com\/blog\/wp-json\/wp\/v2\/tags?post=7"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}