Ambiente de desenvolvimento integrado na nuvem (Cloud IDE)

As vantagens de usar cloud IDE é a capacidade de reproduzir facilmente o ambiente a partir do repositório do git, outra vantagem é não precisar instalar programas no seu computador, também é possível começar o desenvolvimento em uma máquina e continuar exatamente onde você parou em outro computador.

Caso seja necessário desenvolver em computadores diferentes como faço? Cloud IDE. Por exemplo, usar o meu computador quando estou em casa de manhã e continuar o desenvolvimento quando vou pra casa do meu pai a noite.

Os principais recursos necessários

Uma IDE deve fornecer um local único para o trabalho de desenvolvimento. Para isso é preciso recursos principais como: editor de código, gerenciamento de código fonte, execução e visualização do resultado.

Editor de código

É a parte principal de escrita de código então esse recurso deve ter meios de ajudar o desenvolvedor a se adaptar a sua forma de escrita de código, ou o contrário, o desenvolvedor tem que adaptar a como é que a IDE disponibiliza suas facilidades.

Gerenciamento de código fonte

Pra mim o que faz o andamento das funcionalidades é o git. Esse tipo de fluxo de trabalho é bem explicado pelo github: https://guides.github.com/introduction/flow/

Basicamente, cria uma branch, adiciona commits, abre um pull request para discussões, comentários + code review, faz deploy e reintegra a branch na master.

Sendo assim os códigos gerados pela IDE não ficam “presos”, estão sempre a disposição do time para contribuições.

Execução e visualização do resultado

Aqui a IDE deve fornecer uma maneira legal de executar e visualizar aquilo que está sendo trabalho. Para esse caso, eu sempre prefiro conseguir reproduzir o mais próximo do ambiente de produção para evitar erros de incompatibilidade.

Assumindo responsabilidade de infraestrutura

Devido essas facilidades, podemos deixar de lado a relação direta com a infraestrutura, mais um benefício, conseguir trabalhar com “a infra estando por baixo dos panos”.

Em determinados momentos da carreira é importante assumir responsabilidades de infraestrutura, eu recomendo aprender docker e aws, essas coisas que ajudam pra caramba o trabalho do desenvolvedor, está ligado mais a infraestrutura do que ao desenvolvimento e isso vai dar uma bagagem necessária para ser o profissional completo.

Mas se estiver se aprofundando em programação, aprendendo sobre um framework, por que seria necessário assumir essa responsabilidade?

Como eu uso?

Você também pode criar ambiente de desenvolvimento rapidamente eu vou mostrar o passo a passo abaixo. Quero que você também tenha o gatilho mais rápido do oeste para executar aplicações, hehe.

Codeanywhere

1- Acesse https://codeanywhere.com/

codeanywhere.com

2- Clique em Login

3- Clique no ícone do GitHub

codeanywhere.com-login-github

4- Informe o seu login e senha da conta do Github

5- No editor escolha o nome para o container, no meu caso vou escolher “slim-skeleton”

6- Escolher a stack, para PHP digite php7 na busca, click na stack php7 no centos 6.5

editor-php7

7- Clique em CREATE, vai aparecer uma mensagem de progresso ex: “Status: Deploying Container…”

8- Na aba do terminal que está aberta faça o clone do projeto que vai executar, no meu caso “git clone git@github.com:ptcmariano/slim-skeleton-controller.git .”

9- baixar as dependencias do projeto comando “composer install”

10- Clique em Run Project, e irá abrir uma nova aba com o projeto sendo executado

editor-executando-projeto

Está pronto, só continuar o fluxo de desenvolvimento do git, toda stack está na mão com poucos comandos.

Dica: caso precise criar um ambiente em cloud IDE que seja para um fim específico, utilize um email temporário, com a ferramenta https://temp-mail.org/pt/

E agora, recomendo experimentar trabalhar dessa forma. Depois volte nesse post e me diga se valeu a pena ou se você preferiu continuar com o ambiente local. Preciso saber quais seriam os motivos para que eu tivesse que assumir a responsabilidade de infra em ambiente local.

Abraços e codifiquemos!

Anúncios

Microframework PHP definição

Micro frameworks em minha observação

Vou descrever um pouco sobre o que tenho aprendido sobre framework PHP e esclarecer um pouco da diferença entre micro frameworks e fullstack frameworks. A comunidade PHP tem se dedicado em desenvolver essas duas frentes para facilitar a criação de novas aplicações, cada uma com suas vantagens. Acredito que podemos chegar a uma definição e esclarecer quando usar.

Proposta de definição

Um microframework é um termo usado para se referir a um framework de aplicação web minimalista. É contrastado com fullstack frameworks também chamados de frameworks entreprise, onde há uma coleção completa de recursos a disposição. Para um micro framework os recursos adicionais são considerados como dependências da aplicação, deixando mais enxuto o mecanismo mínimo de funcionamento.

Um micro framework facilita o recebimento de uma requisição HTTP, roteando a requisição para uma controller apropriada, despachando uma controller e retornando uma resposta HTTP. Além desse recurso talvez alguns adicionais, como log, porém a ideia que recursos sejam dependências do projeto a ser implementado. Como pode ser ilustrado na imagem abaixo:

ex de arquitetura de um microframework
exemplo de arquitetura de um microframework

Um fullstack framework é comum ter um conjunto de recursos para uma aplicação web completa, como:

  • Autenticação, contas, papéis, login, etc.
  • Acesso a base de dados, principalmente através de um mapeamento objeto relacional
  • Validação de entradas e sanitização de input
  • Views e template engine
  • Abstração de sistemas de arquivos

Também pode ser adicionado recursos por dependências em projetos fullstack, porém para uma aplicação web os recursos já existem em boa parte. Como é ilustado na imagem abaixo:

arquitetura de fullstack framework
exemplo de arquitetura de um fullstack framework

Projetos de microframeworks em PHP

Abaixo listei os quais eu consegui captar, eu particularmente uso o Slim framework e tenho um skeleton para usa-lo com o paradgima de Orientação a Objetos PHP em https://github.com/ptcmariano/slim-skeleton-controller

Movimento de microserviços

A proposta desse artigo não é se aprofundar a discussão para o movimento de microserviços, porém acredito que esses projetos de microframeworks vem para facilitar a implementação da estratégia de microserviços. Deve haver relações entre esses movimentos, se alguém tiver referências por favor comentem para que a comunidade tenham melhor entendimento dessa relação.

Conclusão, prós e contras

Podemos observar a diferença principal é que os microframeworks usam dependências para criar as aplicações web, isso traz a vantagem de maior flexibilidade para variar quais dependências você vai escolher para cada projeto, porém há necessidade de administração maios das dependências. Para a escolha de um fullstack framework, tem a vantagem de haver recursos disponíveis para a sua aplicação, tendo também a facilidade de uma única documentação explicar como tratar todos recursos disponíveis, com a desvantagem de carregar recursos que talvez não sejam necessários para o projeto que você irá usar e menor flexibilidade para alterar os recursos que serão usados (não é comum altera-los já que estão disponíveis). Bom com essas informações é possível melhorar a escolha de cada projeto, sendo que pode ser enriquecido a decisão com seus próprios critérios para escolha de um ou outro tipo de framework. Desejo que a próxima vez que tomar a decisão de usar um framework leve em consideração, além é claro de sua experiência, qual é a necessidade do projeto, vai usar poucos recursos? vai experimentar um recurso novo? Com isso você terá a oportunidade de expandir a suas habilidades e a aplicação web ter novos recursos quem sabe até disponibilizar novas oportunidades de negócios.

Se achar que algum ponto pode ser melhor no artigo, por favor faça comentários a respeito. Pretendo aumentar a discussão sobre isso para entendermos melhor essas frentes.

Referências

Acessados em 17/04/2017

https://belitsoft.com/laravel-development-services/full-stack-framework-or-microframework-laravel-or-lumen

https://en.wikipedia.org/wiki/Microframework

Minha Jornada com PHP

Recentemente me deparei que estou envolvido até as tampas com PHP. Comecei aqui mesmo em 2010 e nem sabia, a plataforma wordpress ainda não tinha me trazido para desenvolvimento, era só uma forma de compartilhar os meus estudos.

Agora em 2017, nos últimos só tenho utilizado PHP, por que? Simples, ele é produtivo. Ah beleza, se usar Rails ou Django tem gente que consegue criar aplicações com facilidade. Mas eu ter trabalhado 3 anos e meio na Catho, sai pra montar um negócio (em outro post explico, eu se quiser me mande uma msg), fez com que eu conhecesse essa incrivel plataforma, gratuita, popular e vamos dizer “simpática”. Veja só é um elefante! (elephantine mascot of the PHP project) rsrs.

elephpant-running-78x48

Bom pra continuar essa saga, comprei um livro, criando aplicações web com wordpress.

Pretendo criar artigos sobre WP aqui! e continuar a descrição dos meus estudos nesse blog. Depois de um hiato de alguns meses.

Abraços.

Paulo Tiago Castanho Mariano

 

Metodos Ageis e seus mitos

Um pequeno “draft” do que Mauricio Aniche (Caelum) apresentou de Métodos ágeis: o que é folclore e o que é real?

Em uma rápida pesquisa nos participantes, a maioria levatou a mão ao perguntar que utiliza metodologia ágil (cerca de 70 de 100).

Aniche declarou que  a ciência (em seu mestrado e doutorado) o deixou mais frio, deixou a paixão do agile e se concentrou apenas em resultados.

Indicou leitura de Leprechauss of software engineering >> acredito que seja: https://leanpub.com/leprechauns

O principal da palestra foi que os testes unitários levam um tempo para se tornar hábito.

Conforme a equipe ganha maturidade ai vai procurando outras ferramentas para auxiliar as entregas de software funcionando.

 

Aprendendo TDD (ou Desenvolvimento Orientado a Testes) Python

Reblogado de: http://blog.thiagobelem.net/aprendendo-tdd-ou-desenvolvimento-orientado-a-testes/

Mas afinal, que diabos é TDD?

Segundo a Wikipédia, TDD é:

Test Driven Development (TDD) ou em português Desenvolvimento dirigido guiado por testes é uma técnica de desenvolvimento de software que baseia em um ciclo curto de repetições:

Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade. Então, é produzido código que possa ser validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis.

Kent Beck, considerado o criador ou o ‘descobridor’ da técnica, declarou em 2003 que TDD encoraja designs de código simples e inspira confiança.

E qual a vantagem disso?

Pra mim, TDD é uma forma de você garantir que, através de pequenos passos, você vai chegar à um “todo-completo”, que é uma aplicação que funciona baseado apenas nas especificações que foram definidas, e lá na frente quando você for mudar algo, é só rodar os testes novamente para garantir que tudo continue funcionando.

Mas por via das dúvidas, pesquisem mais sobre o assunto, tem um mundo por trás disso tudo.

“Talk is cheap! Shut up and show me the code!”

(eu amo essa frase)

Vamos direto ao ponto e vamos ver um pequeno exemplo de como criar uma função baseada em TDD… e vamos começar pelo exemplo mais simples e usado por todos: o famoso Fizz Buzz, que segundo a nossa amiga Wikipédia é:

Bizz buzz (also known as fizz buzz, or simply buzz) is a group word game frequently encountered as a drinking game. Players take turns to count incrementally, replacing any number divisible by three with the word “bizz”, and any number divisible by five with the word “buzz”.

Tradução deste que vos fala:

Bizz buzz (também conhecido com fizz buzz, ou apenas buzz) é uma brincadeira em grupo, comummente jogada como desculpa pra encher a cara. Os jogadores jogam em turnos incrementais, onde cada um fala um número substituindo números divisíveis por três pela palavra “fizz“, e números divisíveis por cinco pela palavra “buzz“.

Sendo que você precisa falar “fizz buzz” quando o número for múltiplo de três e de cinco.

Básicamente o resultado final do jogo seria algo assim:

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, …

Então vamos criar uma função FizzBuzz que receba um parâmetro N e retorne o número, fizz, buzz ou fizz buzz dependendo do número que ela recebeu. :)

Antes de qualquer coisa, vou fazer algo que aprendi com esse livro e recomendo muito: vamos montar uma lista de especificações que precisaremos testar/implementar.

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”

Essas não são todas as regras do jogo, mas já é um bom começo… Cada um desses itens vai significar um teste e – possivelmente – uma melhoria no código da função.

Ao longo desse artigo irei copiar essa lista diversas vezes, marcando com negrito os itens que iremos atacar, e riscando os itens que forem concluídos.

Mas peraí… e a linguagem?

Verdade… temos que decidir uma linguagem, e como eu falo MUITO de PHP aqui no blog vamos fazer esse FizzBuzz em Python, que já vem com um framework de testes embutido na linguagem, pra quem quiser brincar de testes unitários no PHP recomendo muito o PHPUnit.

Baby steps, ou “Passos de bebê”

Uma das maiores características do desenvolvimento orientado à testes é que você sempre tente dar passos menores que suas pernas, não significa que você não possa dar uma corrida se o projeto exigir, mas sempre avance com pequenos passos, nada de escrever 100 linhas de código sem testar (com testes)… e eu vou tentar seguir essa metodologia aqui.

Mãos à obra!

No TDD você SEMPRE começa pelo teste, então vamos começar criando nosso arquivo de testes:

#!/usr/bin/env python

import unittest

if __name__ == '__main__':
unittest.main()

Fizemos três coisas no nosso arquivo fizzbuzz_test.py:

  1. Primeiro temos #!/usr/bin/env python que permite que executemos o arquivo sem ser através do executável do Python, mas isso é opcional.
  2. Depois importamos a biblioteca nativa de testes unitários do Python
  3. E por fim usamos uma condição que – resumidamente – permite que o arquivo seja executado pelo terminal já rodando os testes

Quando a gente rodar esse arquivo com o comando ./fizzbuzz_test.py ou o comando python fizzbuzz_test.py vamos ter o seguinte output:

$ python fizzbuzz_test.py

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK
view raw
gistfile1.sh
This Gist brought to you by GitHub.

Então sabemos que tudo está funcionando… prontos para o primeiro teste?

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”

Nosso primeiro teste, da forma mais simples e reduzida possível, ficaria assim:

#!/usr/bin/env python

import unittest

class TestFizzBuzz(unittest.TestCase):

def test_FizzBuzz(self):
self.assertEqual(FizzBuzz(1), 1)

if __name__ == '__main__':
unittest.main()

Criamos uma classe TestFizzBuzz, que é um caso de teste (contém vários testes) para testar a classe/funcionalidade FizzBuzz.

Definimos nosso primeiro teste (test_FizzBuzz), onde fizemos uma asserção (verificação):

O resultado de FizzBuzz(1) é IGUAL a 1?

A asserção de igualdade é justamente o método assertEqual… 🙂

Prontos pra rodar o teste? Vamos ver o que acontece…

$ python fizzbuzz_test.py
E
======================================================================
ERROR: test_FizzBuzz (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 8, in test_FizzBuzz
    self.assertEqual(FizzBuzz(1), 1)
NameError: global name 'FizzBuzz' is not defined

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

UHU! Nosso primeiro erro! (sim, no TDD os testes não passando significam progresso).. mas não é o erro que eu estava esperando! :(

O problema é que nós ainda não definimos FizzBuzz, por isso o Python xiou dizendo “global name ‘FizzBuzz’ is not defined“.

Vamos criar nosso arquivo fizzbuzz.py com o seguinte conteúdo:

#!/usr/bin/env python

def FizzBuzz():
pass
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Criamos a estrutura da nossa função FizzBuzz que ainda não faz nada.

O “pass” no Python é usado quando uma função/método ainda não tem conteúdo.. e como não temos chaves pra dizer onde ela começa e termina, precisamos de uma instrução que faça exatamente nada.

Vamos voltar ao nosso teste e importar essa função para que ela possa ser usada nos nossos testes, depois vamos rodar os testes novamente e ver se aquela mensagem de erro mudou.

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_FizzBuzz(self):
self.assertEqual(FizzBuzz(1), 1)

if __name__ == '__main__':
unittest.main()

A linha “from fizzbuzz import FizzBuzz” significa “Importe a classe/função FizzBuzz do arquivo ou módulo chamado fizzbuzz“… isso mesmo, no Python podemos importar apenas parte de um arquivo! :)

E o resultado da execução dos testes é…

$ python fizzbuzz_test.py
E
======================================================================
ERROR: test_FizzBuzz (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 9, in test_FizzBuzz
    self.assertEqual(FizzBuzz(1), 1)
TypeError: FizzBuzz() takes no arguments (1 given)

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

E estamos chegando lá… agora o Python reclamou que – segundo sua definição – a função FizzBuzz não recebe parâmetros.. que é justamente o primeiro item da nossa lista, então vamos fazer isso acontecer.

#!/usr/bin/env python

def FizzBuzz(numero):
pass
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Isso.. tudo beeeeem de vagar, lembre-se dos passos de bebê!

Agora rodamos os testes novamente e…

$ python fizzbuzz_test.py
F
======================================================================
FAIL: test_FizzBuzz (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 9, in test_FizzBuzz
    self.assertEqual(FizzBuzz(1), 1)
AssertionError: None != 1

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

Conseguimos! O primeiro item da lista foi resolvido! :D

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”

Mas agora temos outro problema… que é justamente a nossa asserção de “FizzBuzz(1) é 1″ falhando.

Fazendo um teste passar

Agora vem a parte (pra mim) mais importante do TDD:

Sempre que você escrever um teste e ele quebrar, pergunte-se: “Qual o menor passo, a menor mudança no código, que eu posso fazer pra esse teste passar?” Não importa se esse passo é elegante, segue padrões de projeto ou está simplesmente enganando o código… A primeira vez que você faz o teste passar tem a ver com velocidade e simplicidade, boas práticas fica pro momento da refatoração, com todos os testes passando.

A menor mudança que a gente pode fazer pra esse código funcionar, sem pensar nos outros casos de FizzBuzz(n) que ainda não estão testados é:

#!/usr/bin/env python

def FizzBuzz(numero):
return 1
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Você pode querer me matar depois dessa, mas não estou brincando.. isso é sério, é assim que a coisa funciona! :)

E os testes?

$ python fizzbuzz_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

view raw
gistfile1.sh
This Gist brought to you by GitHub.

Satisfação! Finalmente, nosso primeiro teste passou!!!

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”

Decidindo o próximo teste

Quando você estiver decidindo o próximo teste, tente seguir as seguintes dicas:

  • Escreva um teste que você SABE não vai passar.. Não adianta ficar testando coisas que já estão passando, né?
  • Mas se você não tem certeza, escreva o teste e veja o que acontece… Testes nunca são demais.
  • Escreva um teste que você ACHA que pode ser resolvido de forma simples, nada de testar o programa todo de uma vez.. o ideal é que você tenha apenas um teste quebrando em cada “rodada”
  • Teste com valores plausíveis e facilmente compreensíveis… testar soma (1, 2) == 3 é muito melhor do que testar soma (12312512312, 31653341265312) = ????, entendeu onde quero chegar?

Então vamos seguir a lista e testar FizzBuzz(2) que deveria retornar 2, e provavelmente não vai passar por causa do nosso roubo (return 1).

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_FizzBuzz_de1(self):
self.assertEqual(FizzBuzz(1), 1)

def test_FizzBuzz_de2(self):
self.assertEqual(FizzBuzz(2), 2)

if __name__ == '__main__':
unittest.main()

Eu poderia fazer a nova asserção dentro do mesmo teste, mas preferi trocar o nome dele e criar um segundo teste, assim as coisas ficam mais claras e você pode ver cada teste falhando separadamente.

$ python fizzbuzz_test.py
.F
======================================================================
FAIL: test_FizzBuzz_de2 (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 12, in test_FizzBuzz_de2
    self.assertEqual(FizzBuzz(2), 2)
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

E com esse teste, concluímos que ao roubar (mesmo valendo pra quele momento) acabamos cuspindo pra cima, e agora o cuspe caiu na nossa cabeça… 2 (esperado) é diferente de 1 (resultado).

Mais uma vez, hora de se perguntar: ”Qual o menor passo, a menor mudança no código, que eu posso fazer pra esse teste passar?“.. e se a função retornar o número que recebeu?

#!/usr/bin/env python

def FizzBuzz(numero):
return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Feita a mudança, rodamos os testes e…

$ python fizzbuzz_test.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

view raw
gistfile1.sh
This Gist brought to you by GitHub.

Excelente! Nosso segundo teste está passando e, quase sem perceber, refatoramos o código para algo realmente dentro das regras do problema :)

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”

Qual o nosso próximo teste? Vamos seguir a lista e ver no que vai dar… Se entendemos bem o que fizemos até agora, FizzBuzz(3) vai retornar 3 e não “fizz” como manda a especificação do problema (múltiplos de três retornam “fizz”).

Como voltamos na especificação do problema, vamos adicionar mais um item à nossa lista:

  • FizzBuzz(9) retorna “fizz”

Agora vamos ao teste do FizzBuzz(3) retorna “fizz”:

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_FizzBuzz_de1(self):
self.assertEqual(FizzBuzz(1), 1)

def test_FizzBuzz_de2(self):
self.assertEqual(FizzBuzz(2), 2)

def test_FizzBuzz_de3_retorna_fizz(self):
self.assertEqual(FizzBuzz(3), "fizz")

if __name__ == '__main__':
unittest.main()

E o resultado dos testes, como esperado, falhou:

$ python fizzbuzz_test.py
..F
======================================================================
FAIL: test_FizzBuzz_de3_retorna_fizz (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 15, in test_FizzBuzz_de3_retorna_fizz
    self.assertEqual(FizzBuzz(3), "fizz")
AssertionError: 3 != 'fizz'

----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (failures=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

Minha função FizzBuzz ainda não está preparada para retornar fizz, o teste quebrou e nós progredimos em direção a solução de mais um problema… viu como é legal?

Eu sei que você está querendo começar a correr e verificar se numero é múltiplo de três, mas não temos testes pra isso ainda.. temos apenas um teste onde FizzBuzz(3) deveria retornar “fizz”… é esse pequeno passo que vamos dar. TDD também tem a ver com ansiedade, e você precisa aprender a controlar a sua.

#!/usr/bin/env python

def FizzBuzz(numero):
if (numero == 3):
return "fizz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

E os testes passaram! :D

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(9) retorna “fizz”

Eu poderia seguir a lista e partir para o FizzBuzz(4) mas ele provavelmente vai passar, mas ao mesmo tempo, entre a dúvida e o teste, fique com o teste:

def test_FizzBuzz_de4(self):
self.assertEqual(FizzBuzz(4), 4)
view raw
gistfile1.py
This Gist brought to you by GitHub.

Ok.. os testes continuam passando, então esse teste não colaborou em nada para o problema.. Vamos dar um pulo e testar nosso caso mais recente:

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_FizzBuzz_de1(self):
self.assertEqual(FizzBuzz(1), 1)

def test_FizzBuzz_de2(self):
self.assertEqual(FizzBuzz(2), 2)

def test_FizzBuzz_de3_retorna_fizz(self):
self.assertEqual(FizzBuzz(3), "fizz")

def test_FizzBuzz_de4(self):
self.assertEqual(FizzBuzz(4), 4)

def test_FizzBuzz_de9_retorna_fizz(self):
self.assertEqual(FizzBuzz(9), "fizz")

if __name__ == '__main__':
unittest.main()

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(9) retorna “fizz”

Ok.. os testes quebraram.. mas como vamos resolver o problema?

Sabemos que nosso código ainda tem um “roubo”, podemos fazer outro roubo ou partir para uma refatoração que resolva o FizzBuzz(3) e FizzBuzz(9), como o problema é ridiculamente simples e esse artigo está ficando longo demais, vamos pra segunda opção:

#!/usr/bin/env python

def FizzBuzz(numero):
if not numero % 3:
return "fizz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

E os testes passaram! :)

Vejam que foi uma refatoração bem simples, ao invés de verificar se o número é igual a três, eu verifiquei se não sobrou resto da sua divisão por três, ou seja: se ele é múltiplo de três.

Uma coisa que está me incomodando um pouco são todos os nomes testes, acho que podemos refatorar o teste a grupar casos semelhantes:

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_numeroSimples(self):
self.assertEqual(FizzBuzz(1), 1)
self.assertEqual(FizzBuzz(2), 2)
self.assertEqual(FizzBuzz(4), 4)

def test_numeroMultiploDeTres(self):
self.assertEqual(FizzBuzz(3), "fizz")
self.assertEqual(FizzBuzz(9), "fizz")

if __name__ == '__main__':
unittest.main()

Agora sim! Bem melhor, e continuamos testando a mesma coisa… só que com menos testes e mais asserções por teste.

Nossa lista está quase acabando…

  • FizzBuzz recebe um número
  • FizzBuzz(1) retorna 1
  • FizzBuzz(2) retorna 2
  • FizzBuzz(3) retorna “fizz”
  • FizzBuzz(4) retorna 4
  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(9) retorna “fizz”

Falta apenas o FizzBuzz(5) mas acho que é hora de revisar o problema e adicionar mais alguns itens à lista:

  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(10) retorna “buzz”
  • FizzBuzz(15) retorna “fizzbuzz”
  • FizzBuzz(30) retorna “fizzbuzz”

Claro que eu já sei como o problema funciona, mas podemos levantar esses exemplos só de olhar pra descrição do problema lá em cima..

Então vamos partir pro primeiro item da lista: FizzBuzz(5) retorna “buzz”

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_numeroSimples(self):
self.assertEqual(FizzBuzz(1), 1)
self.assertEqual(FizzBuzz(2), 2)
self.assertEqual(FizzBuzz(4), 4)

def test_numeroMultiploDeTres(self):
self.assertEqual(FizzBuzz(3), "fizz")
self.assertEqual(FizzBuzz(9), "fizz")

def test_numeroMultiploDeCinco(self):
self.assertEqual(FizzBuzz(5), "buzz")

if __name__ == '__main__':
unittest.main()

Os testes voltam a quebrar, apenas o último teste falhou pois 5 != “buzz”.

Como ainda não explicitamos a regra do “multiplo de 5″ através de testes, o correto aqui é fazer com que apenas essse teste passe (e não testes futuros):

#!/usr/bin/env python

def FizzBuzz(numero):
if not numero % 3:
return "fizz"

if numero == 5:
return "buzz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

E todos os testes passaram! :D

Agora vamos colocar mais um múltiplo de cinco, como por exemplo FizzBuzz(10) e o teste vai quebrar:

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_numeroSimples(self):
self.assertEqual(FizzBuzz(1), 1)
self.assertEqual(FizzBuzz(2), 2)
self.assertEqual(FizzBuzz(4), 4)

def test_numeroMultiploDeTres(self):
self.assertEqual(FizzBuzz(3), "fizz")
self.assertEqual(FizzBuzz(9), "fizz")

def test_numeroMultiploDeCinco(self):
self.assertEqual(FizzBuzz(5), "buzz")
self.assertEqual(FizzBuzz(10), "buzz")

if __name__ == '__main__':
unittest.main()

Vamos então parar de roubar e fazer o que fizemos com com os múltiplos de três:

#!/usr/bin/env python

def FizzBuzz(numero):
if not numero % 3:
return "fizz"

if not numero % 5:
return "buzz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

E todos os três testes (com as 7 asserções) passaram novamente.

  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(10) retorna “buzz”
  • FizzBuzz(15) retorna “fizzbuzz”
  • FizzBuzz(30) retorna “fizzbuzz”

Agora vamos atacar a última parte do problema, múltiplos de três e de cinco.

Nada de escrever código, primeiro o teste:

#!/usr/bin/env python

import unittest
from fizzbuzz import FizzBuzz

class TestFizzBuzz(unittest.TestCase):

def test_numeroSimples(self):
self.assertEqual(FizzBuzz(1), 1)
self.assertEqual(FizzBuzz(2), 2)
self.assertEqual(FizzBuzz(4), 4)

def test_Fizz(self):
self.assertEqual(FizzBuzz(3), "fizz")
self.assertEqual(FizzBuzz(9), "fizz")

def test_Buzz(self):
self.assertEqual(FizzBuzz(5), "buzz")
self.assertEqual(FizzBuzz(10), "buzz")

def test_FizzBuzz(self):
self.assertEqual(FizzBuzz(15), "fizzbuzz")

if __name__ == '__main__':
unittest.main()

(Aproveitei também pra mudar o nome dos testes)

Vejam vocês… o teste falhou.. mas diferente do que vimos até agora, não temos “15 != fizzbuzz” mas sim “fizz != fizzbuzz” pois 15 é multiplo de 3, correto?

$ python fizzbuzz_test.py
..F.
======================================================================
FAIL: test_FizzBuzz (__main__.TestFizzBuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 22, in test_FizzBuzz
    self.assertEqual(FizzBuzz(15), "fizzbuzz")
AssertionError: 'fizz' != 'fizzbuzz'

----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=1)

view raw
gistfile1.sh
This Gist brought to you by GitHub.

Primeiro roubamos (o que o Kent Beck chama de “implementação óbvia”):

#!/usr/bin/env python

def FizzBuzz(numero):
if numero == 15:
return "fizzbuzz"

if not numero % 3:
return "fizz"

if not numero % 5:
return "buzz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

E todos os testes voltam a passar… Agora adicionamos mais um teste da nossa lista:

  • FizzBuzz(5) retorna “buzz”
  • FizzBuzz(10) retorna “buzz”
  • FizzBuzz(15) retorna “fizzbuzz”
  • FizzBuzz(30) retorna “fizzbuzz”

E fazemos uma implementação simples, que resolva o problema:

#!/usr/bin/env python

def FizzBuzz(numero):
if not numero % 15:
return "fizzbuzz"

if not numero % 3:
return "fizz"

if not numero % 5:
return "buzz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Agora, que todos os testes estão passando e o problema está resolvido, podemos refatorar nossa função pra algo mais elegante:

#!/usr/bin/env python

def MultiploDeTres(numero):
return not numero % 3

def MultiploDeCinco(numero):
return not numero % 5

def FizzBuzz(numero):
if MultiploDeTres(numero) and MultiploDeCinco(numero):
return "fizzbuzz"

if MultiploDeTres(numero):
return "fizz"

if MultiploDeCinco(numero):
return "buzz"

return numero
view raw
fizzbuzz.py
This Gist brought to you by GitHub.

Não que essa solução seja a mais elegante e mais eficiente em Python, mas acredito que ela deixe a lógica mais clara.

Enfim… TDD é isso!

Espero que vocês tenham gostado. :D