Freshen é um framework BDD para Python, que utiliza a Gherkin syntax do cucumber. Em outras palavras, é uma versão do Cucumber para Python, que se integra ao Nose, uma ferramenta para testes em Python. Com ele, é possível escrever funcionalidades de sistemas numa linguagem muito mais próxima da humana (mas muito mesmo xD). Eis um exemplo de especificação funcionalidade:
[cc lang=”text”]Funcionalidade: Adição Para evitar erros bobos Como um péssimo matemático Eu quero saber como somar dois números
Cenário: Adicionar dois números Dado que eu digitei 50 na calculadora E que eu digitei 70 na calculadora Quando eu aperto o botão de soma Então o resultado na calculadora deve ser 120[/cc]
Existem apenas algumas palavras mágicas como Funcionalidade, Cenário, Dado, E, Quando e Então. Todo o resto é nosso conhecido português. Desta forma, seria possível criar várias funcionalidades, e para cada uma dessas funcionalidades, um cenário, e garantir o funcionamento disso de forma bem simples :)
O Cucumber foi desenvolvido para o Ruby, e a definição dos passos acima, em Ruby, seria o seguinte código:
[cc lang=”ruby”]Before do @calc = Calculadora.new end
After do end
Dado /que eu digitei (\d+) na calculadora/ do |n| @calc.push n.to_i end
Quando ‘eu aperto o botão de soma’ do @result = @calc.soma end
Então /o resultado na calculadora deve ser (\d*)/ do |result| @result.should == result.to_i end [/cc]
O Freshen traz um estilo muito semelhante, só que usando Python. O código Ruby acima, “traduzido” em Python, fica desta forma:
[cc lang=”python”] @Before def criar_calculadora(sc): scc.calculadora = Calculadora()
@Given(“que eu digitei (\d+) na calculadora”) def digitar_numero(numero): scc.calculadora.add_numero(int(numero))
@When(“eu aperto o botão de soma”) def somar(): scc.resultado = scc.calculadora.somar()
@Then(“o resultado na calculadora deve ser (\d+)”) def visualizar_resultado(resultado): scc.resultado |should_be| int(resultado) [/cc]
Observação: Aquele “should_be” é provido pela should-dsl.
Bom, isso é o básico do básico do básico. O post tem como intuito demonstrar a integração do Freshen com o Pylons. O Pylons já tem uma integração natural com o Nose, fornecendo um plugin para que o Nose possa executar testes sobre o Pylons, e o Freshen também é um plugin do Nose, assim, fica fácil integrar as duas ferramentas a nível de “comando”, mas e a nível de código, como fica?
Se você não tem muito conhecimento em Pylons, recomendo ler o post Construindo um CRUD com frameworks Python, Parte I: Pylons e também o guia de referência do framework. Assumindo então que já existe o conhecimento necessário, e que o projeto do Pylons já está criado (batizei o projeto de post_pylons_freshen), é hora de iniciar a integração. Colando o exemplo do post de BDD no Django, vamos criar a funcionalidade Cadastro de aluno, com o cenário Cadastro de aluno sem nome. O “código” da nossa funcionalidade é o seguinte:
Funcionalidade: Cadastro de Aluno
Como um secretário
Eu quero cadastrar um aluno informando nome e data de nascimento
Para que ele possa ser matriculado em disciplinas
Cenário: Cadastro de aluno sem nome
Dado que estou cadastrando um aluno
Quando não digito o nome
Então o cadastro não pode ser completado
O cenário está meio estranho, mas podemos melhorar futuramente. O importante é compreender que da pra fazer muita coisa definindo nossas funcionalidades em português :) Executando o Nose na nossa aplicação, somos informados que temos passos indefinidos em nossos cenários:
Cadastro de Aluno: Cadastro de aluno sem nome … UNDEFINED: “que estou cadastrando um aluno” # post_pylons_freshen/features/cadastro_aluno.feature:7 ——————– >> begin captured logging << ——————– pylons.configuration: DEBUG: Initializing configuration, package: ‘post_pylons_freshen’ pylons.configuration: DEBUG: Loaded None template engine as the default template renderer routes.middleware: DEBUG: Initialized with method overriding = True, and path info altering = True ——————— >> end captured logging << ———————
Ran 1 test in 0.192s
OK (UNDEFINED=1)[/cc]
O próximo passo então é definir os passos do nosso Cenário, através do arquivo steps.py. É neste arquivo que faremos a “conexão” entre o Pylons e o Freshen, utilizando também os recursos do Nose para executar testes no nosso model :)
[cc lang=”python”]@Before def instanciar_aluno(sc): scc.aluno = Aluno()
@Given(“que estou cadastrando um aluno”) def cadastrando_aluno(): pass
@When(“não digito o nome”) def set_none_in_nome(): scc.aluno.nome = None
@Then(“o cadastro não pode ser completado”) def falhar_cadastro(): Session.add(scc.aluno) assert_raises(IntegrityError, Session.commit)[/cc]
E aí está tudo definido :) Após esta definição, devemos criar nosso model Aluno, com o atributo nome NOT NULL. Como o foco aqui era a criação da funcionalidade e a definição dos passos de nosso cenário, você pode conferir o projeto completo no Github: http://github.com/fsouza/post-freshen-pylons.
O exemplo inicial da Calculadora também está disponível no Github: http://github.com/fsouza/exemplo-freshen-calculadora.