BDD com Shoulda: Menos código, testes mais legíveis
Para quem ainda não conhece, BDD (Behavior Driven Development) é uma prática de processos ágeis que vem mudando a maneira como os desenvolvedores escrevem testes automatizados para seus sistemas. Testes escritos sobre este conceito são mais fáceis de entender e podem ser utilizados como ferramenta de modelagem de um sistema, já que eles ajudam pessoas com pouco conhecimento técnico à entender funcionamento do software. Além disso, testes simples de entender também são simples de manter.
Essas vantagens me fizeram pensar na idéia de migrar os testes do meu projeto Rails, que tinha seus testes unitários escritos sobre o framework Test/Unit, o padrão utilizados pelo Rails para TDD (Test Driven Development). Para realizar a mudança era necessário aprender a utilizar outro framework, no caso o RSpec.
Foi então que, acompanhando a blogosfera de Railers, através de um post do Carlos Brando, conheci o Shoulda. O Shoulda é um framework recente e que eu considero como um passo intermediário entre o Test/Unit e o RSpec. Ele traz um conjunto de classes e métodos que melhoram o Test/Unit padrão, permitindo o uso de uma sintaxe parecida com a do RSpec. Assim é possível se aproximar de BDD sem ter muito impacto na forma de programar os testes.
Vou exemplificar aqui o migração dos testes de um model simples do Sismiko, a classe City que possui apenas os atributos “name” e “state_id”, representando o nome da cidade e o id do estado, respectivamente.
Observe o código utilizando o modo padrão:
class CityTest < Test::Unit::TestCase
fixtures :cities
def test_name_required #testa se o nome da cidade é um campo requerido
check_required(:name)
end
def test_state_required #testa se o campo state_id da cidade é um campo requerido
check_required(:state_id)
end
def test_max_name #testa se o tamanho máximo do campo nome é de 100 caracteres
city = create(:name => "a" * 101)
assert city.errors.invalid?(:name),"name must have a maximum of 100 chars"
assert_not_nil city.errors.on(:name)
assert !city.valid?, "city shouldn't be created"
end
private
def create(options={}) #cria uma cidade padrão
City.create({
:name => "Maringá",
:state_id => 1
}.merge(options))
end
def check_required(att) #verifica se o campo passado em att é requerido pelo objeto
city = create(att => nil)
assert city.errors.invalid?(att)
assert_not_nil city.errors.on(att)
assert !city.valid?, "city shouldn't be created"
end
end
No código acima vemos três testes sendo feitos e mais dois métodos auxiliares, para criar um objeto com valores padrão em seus atributos e outro para verificar se um atributo é requerido. Quanto as testes, o primeiro e o segundo testes verificam se os campos “name” e “state_id” são obrigatórios ao criar um objeto de cidade. O terceiro verifica se o campo “name” aceita no máximo 100 caracteres.
Vejamos agora o código necessário para fazer os mesmo testes, usando a declaração de contextos pregada pelo BDD para deixar os testes mais legíveis, já utilizando o Shoulda:
class CityTest < Test::Unit::TestCase
fixtures :cities
context "A City instance" do
setup do
@city = create
end
should "have a required name" do
check_required(:name)
end
should "have a required state" do
check_required(:state_id)
end
context "with a large name" do #uso de contexto aninhado
setup do
@city.name = "a" * 101
@city.save
end
should "validate max size of name" do
assert city.errors.invalid?(:name)
end
end
end
private
def create(options={}) #cria uma cidade padrão
City.create({
:name => "Maringá",
:state_id => 1
}.merge(options))
end
def check_required(att) #verifica se o campo passado em att é requerido pelo objeto
city = create(att => nil)
assert city.errors.invalid?(att)
assert_not_nil city.errors.on(att)
assert !city.valid?, "city shouldn't be created"
end
end
Com certeza ficou mais fácil de entender e também de localizar o erro. Por exemplo, caso o teste de atributo “name” requerido falhe, recebemos a seguinte mensagem:
test: A City instance should have a required name
O uso do novo framework melhorou a legibilidade dos testes, mas podemos melhorar ainda mais. Fazendo uso de helpers acrescentados pelo Shoulda, é possível reduzir o código para algo assim:
CityTest < Test::Unit::TestCase fixtures :cities should_require_attributes :name, :state_id #1 should_ensure_length_in_range :name, (0...100) #2 end
Sem dúvida, muito mais simples. A linha comentada com #1 substitui dois testes escritos anteriormente, validando os campos “name” e “state_id” como requeridos. Em #2 verifica-se se o atributo “name” aceita valores com tamanho entre 0 e 100, rejeitando valores maiores.
Ficou impressionado? Instale o Shoulda e faça suas experiências, o projeto está no github:
script/plugin install git://github.com/thoughtbot/shoulda.git
Veja também:
O Lucas Hungaro fez um screencast muito bacana com uma introdução ao Shoulda.
Existem vários outros helpers além dos apresentados aqui. A documentação de todos eles pode ser encontrada na documentação do projeto.
Não ha comentários
Leave a reply





