Estou repassando o vídeo abaixo da palavra de advertência acerca das eleições de 2010 dada pelo Pr. Paschoal Piragine Jr, da Primeira Igreja Batista de Curitiba/PR.
Normalmente não me pronuncio acerca de questões eleitorais, mas diante do momento que estamos vivendo, não diremos, nas palavras do Pr. Piragine, “em quem votar, mas em quem não votar”…
Sempre que consigo algum tempo tento escrever alguma coisa no blog e desta vez quero mostrar como é fácil o desenvolvimento em BDD no Sinatra usando Cucumber e Webrat. Para quem numca ouviu falar nesses caras vamos as apresentações.
Quem é esse Sinatra?
Para quem não saber Sinatra é uma linguagem de domínio específico (DSL – Domain Specific Language) para a criação rápida de aplicações web escritas em ruby. Ele mantém uma característica mínima definida, deixando livre o desenvolvedor para utilizar as ferramentas que melhor lhe servir em sua aplicação.
BDD? Cucumber? BDD ou Behavior Driven Development(Desenvolvimento Guiado por Comportamento) é uma técnica de desenvolvimento Ágil que encoraja colaboração entre desenvolvedores, setores de qualidade e pessoas não-técnicas ou de negócios num projeto de software. O foco em BDD é a linguagem e interações usadas no processo de desenvolvimento de software.
O Cucumber foi criado para permitir que você execute a documentação de funcionalidades de uma aplicação, escritas em texto puro (também conhecidas como “estórias”). Com o Cucumber, isto é uma especificação executável que você pode discutir com seu cliente e então usá-la para verificar o comportamento correto dos testes. Por trás dos bastidores, você faz isto funcionar criando “steps”, que são expressões regulares que executam código em Ruby.
Webrat Webrat é uma ferramenta fantástica que permite escrever rapidamente testes de aceitação expressivos e robustos para uma aplicação web Ruby. Ele nos fornece entre outras coisas:
Simulador de browser de alto nível;
Suporta vários frameworks web Ruby;
Suporta os mais populares frameworks de teste;
Fornece uma API para verificar o HTML gerado usando CSS, XPath, etc.
Depois de feita as devidas apresentações vamos colocar a mão na massa. O primeiro passo é criar o diretório de nosso projeto.
1
2
$ mkdir sinatra-cucumber
$ cd sinatra-cucumber
Vamos acessar a pasta do projeto que acabamos de criar e executar os comandos abaixo para criar a pasta onde iremos definir nossas features.
1
2
$ mkdir features
$ touch features/ola.feature
Obs.: Para quem não conhece o comando touch apenas criou um arquivo vazio.
No arquivo ola.feature escreva o seguinte código:
1
2
3
4
5
6
7
8
9
# language: pt
Funcionalidade: Ver páginas
Como um usuário qualquer
Eu quero acessar as páginas do sistema
Para ter acesso a seu conteúdo
Cenário: Página principal
Dado que acabei de acessar o sistema
Então Eu devo ver o texto "Olá, pessoal!"
Vamos executar o cucumber e ver o que acontece.
1
$ cucumber features/ola.feature
Como era de esperar o teste não passou. Vamos em seguida criar os testes para nossa funcionalidade mais antes iremos criar uma tarefa rake para otimizar a chamada do Cucumber.
1
$ touch Rakefile
O código para nossa tarefa rake que será executada com o comando “rake features” é o seguinte:
No arquivo ola_steps.rb teremos o seguinte código:
1
2
3
4
5
6
7
Dado /^que acabei de acessar o sistema$/ do
visit("/")
end
Entao /^Eu devo ver o texto "(.+)"$/ do |texto|
response_body.should =~ Regexp.new(Regexp.escape(texto))
end
Estes dois passos simples fazem uma solicitação a url do nosso aplicativo pelo Webrat e verifica se a resposta contém o texto que estamos procurando.
Abaixo segue as configurações que fazem a integração realmente acontecer. Vamos configurar o ambiente do Cucumber para usar o Webrat.
require'spec/expectations'require'rack/test'require'webrat'
Webrat.configuredo|config|
config.mode = :rackendclass MyWorld
include Rack::Test::Methods
includeWebrat::MethodsincludeWebrat::MatchersWebrat::Methods.delegate_to_session:response_code, :response_bodydef app
Sinatra::Applicationendend
World do
MyWorld.newendrequireFile.dirname(__FILE__)+'/../../ola'
Agora que temos nosso cenário montado podemos escrever nossa aplicação web com essas simples linhas abaixo:
1
$ touch ola.rb
1
2
3
4
5
6
require'rubygems'require'sinatra'
get '/'do"Olá, pessoal!"end
Agora vamos executar mais uma vez o Cucumber e ver os testes passando para ficarmos felizes.
1
$ rake features
Bom pessoal, o objetivo foi cumprido e espero que tenha ficado claro como é fácil desenvolver em Sinatra usando BDD com Cucumber e Webrat. Sei que o exemplo foi bem simples e abaixo segue o código fonte do projeto e alguma referências para você conhecer mais do assunto.
Ruby Tracker é um projeto de apoio à comunidade liderado por Jacob Swanner que tem como objetivo monitorar e informar as dependências entre gems em projetos Ruby.
O Ruby Tracker funciona examinando o projeto e verificando quais gems são utilizadas e principalmente quais versões. Quando for preciso autalizar qualquer gem você será informado quais outras também devem ser atualizadas e para qual versão.
Segue abaixo um vídeo demonstrando melhor o uso da ferramenta:
assert(boolean, message)
assert(person.name == “John”, “Name was expected to be John.”)
assert(item.errors.invalid?(:price))
assert_equal(expected, actual, message)
assert_equal(person.name, “John”, “Name was expected to be John.”)
assert_equal(“can’t be empty”, product.errors.on(:price))
assert_not_equal(expected, actual, message)
assert_not_equal(person.name, “Mary”, “Name was Mary and it should not be.”)
assert_not_equal(“is not a number”, product.errors.on(:price))
assert_raise(Exception, message) { block… }
assert_raise(ZeroDivisionError, “Cannot divide by zero!”) { 100 / 0 }
assert_raise(ActiveRecord::RecordNotFound) { Product.find(bad_id) }
assert_nothing_raised(Exception, message) { block… }
assert_nothing_raised(ZeroDivisionError) { 100 / [0,1].max }
assert_nothing_raised(ActiveRecord::RecordNotFound) { Product.find(good_id) }
assert_nil(object, message)
assert_nil( product, “Expected product to be nil.” )
assert_nil( Wine.find(:first, :conditions => ‘id = 1000’) )
assert_not_nil(object, message)
assert_not_nil( product, “Product should not be nil.” )
assert_not_nil( Wine.find(:first, :conditions => ‘id = 1’) )
assert_valid(activerecord_object)
same as: assert(object.valid?)
assert_valid(@person)
assert_valid( Wine.find(1) )
flunk(message)
always fails immediately; same as: assert(false, message)
flunk(“Quantity should not be greater than 100”) if quantity > 100
flunk(“Either user or account should be valid”) unless user.valid? || account.valid?
Advanced Assertions
assert_match(pattern, string, message)
assert_match(/^\d,\d{3},\d{3}$/, “1,000,000”, “Should match this format.”)
assert_no_match(pattern, string, message)
assert_no_match(/\d{3},\d{2}$/, “1,000,000”, “Should not match this format.”)
assert_in_delta(expected_float, actual_float, delta, message)
assert_in_delta(100.0, price, 20.0, “Price should be between 80.00 and 120.00”)
assert_in_delta(2, length, 1, “Length should be 1-3 feet.”)
assert_instance_of( klass, object, message )
assert_instance_of( User, person, “person should be an instance of User” )
assert_kind_of( klass, object, message )
assert_kind_of( User, person, “person should be a kind of User” )
assert_kind_of( Class, User, “User should be a kind of Class” )
assert_respond_to( object, symbol, message )
Instances only respond to instance methods, classes only respond to class methods
assert_respond_to( person, :full_name, “No response to full_name” )
assert_respond_to( User, :custom_find, “No response to custom_find” )
assert_throws(expected_symbol, message) { block… }
assert_throws(:done, “Array should be empty”) { throw :done if [].empty? }
assert_nothing_thrown(message) { block… }
assert_nothing_thrown(“Array should not be empty”) { throw :done if [1].empty? }
Rare Assertions & DEFAULT EROR MESSA GES
assert_same( expected, actual, message)
same as: assert_equal(expected, actual)
assert_same( person.name, “John”)
assert_not_same( expected, actual, message)
same as: assert_not_equal(expected, actual)
assert_not_same( person.name, “Mary”)
assert_operator( object1, operator, object2, message )
same as: assert( object1.operator(object2) )
assert_operator( 1000, :<, 2000, “Expected 1000 to be less than 2000” )
assert_operator( user, ld_enough?, Time.now(), “User should be old enough”)
assert_send([receiver, symbol, arg1, arg2], message)
same as: assert( receiver.message(arg1, arg2) )
assert_send([product, :decrement_inventory, qty], “Decrement should succeed”)
@@default_error_messages = {:inclusion=> “is not included in the list”,
:exclusion=> “is reserved”,
:invalid=> “is invalid”,
:confirmation=> “doesn’t match confirmation”,
:accepted=> “must be accepted”,
:empty=> “can’t be empty”,
:blank=> “can’t be blank”,
:too_long=> “is too long (maximum is %d characters)”,
:too_short=> “is too short (minimum is %d characters)”,
:wrong_length=> “is the wrong length (should be %d characters)”,
:taken=> “has already been taken”,
:not_a_number=> “is not a number”,
:greater_than=> “must be greater than %d”,
:greater_than_or_equal_to=> “must be greater than or equal to %d”,
:equal_to=> “must be equal to %d”,
:less_than=> “must be less than %d”,
:less_than_or_equal_to=> “must be less than or equal to %d”,
:odd=> “must be odd”,
:even=> “must be even”
}
Me enconte por ai...