Francisco Souza

Open source, Python, Django, Java, agile e outras coisas mais

Integrando Freshen Com Pylons

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.