<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Desenvolvimento &#193;gil - Blog da Improve It: Tag rspec_on_rails</title>
    <link>http://blog.improveit.com.br/articles/tag/rspec_on_rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
    <item>
      <title>Como Testar parte 2 - Mocks</title>
      <description>&lt;blockquote class="excerpt"&gt;
  &lt;p&gt;Tem gente "chutando" Demeter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Minha id&#233;ia era escrever um post dessa s&#233;rie por semana, mas infelizmente uma tendinite tem me atacado e est&#225; meio complicado ficar escrevendo muito. Por isso mesmo esse segundo artigo ser&#225; bem compacto. Todos os c&#243;digos citados nesse post fazem parte de um projeto How Test que est&#225; dispon&#237;vel em: http://github.com/tapajos/how-test&lt;/p&gt;

&lt;p&gt;Nesse post a minha id&#233;ia &#233; mostrar de forma bem simples como funciona um &lt;a href="http://en.wikipedia.org/wiki/Mock_Object"&gt;mock object&lt;/a&gt; no Rspec e no Test::Unit(usando o mocha). &lt;/p&gt;

&lt;p&gt;Para exemplificar eu vou fazer uma cr&#237;tica a uma constru&#231;&#227;o que eu tenho visto muito em diversos projetos e que viola a &lt;a href="http://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Lei de Demeter&lt;/a&gt;(Principle of Least Knowledge). Ou como dizia, o meu amigo, Bernardo, "Tem gente chutando Demeter!". &lt;/p&gt;

&lt;h3&gt;Vamos a um exemplo dessa viola&#231;&#227;o:&lt;/h3&gt;

&lt;p&gt;Supondo que voc&#234; tenha um modelo Account que se relaciona ao modelo User.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Account &amp;lt; ActiveRecord::Base
  belongs_to :user
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Freq&#252;entemente eu vejo constru&#231;&#245;es do tipo:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@account.user.name
@account.user.mail
@account.user.rg.number
@account.user.rg.state
@account.user.rg.city
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;O grande problema &#233; que nesse tipo de constru&#231;&#227;o voc&#234; est&#225; "conhecendo" coisas demais e certamente vai pagar por isso num futuro breve, quando voc&#234; precisar fazer um refactoring e tiver que mudar em v&#225;rios lugares. Imagina se o User deixa de ter um "name" e passa a ter um "full_name".&lt;/p&gt;

&lt;p&gt;Para resolver esse tipo de problema basta voc&#234; concentrar esse "conhecimento" no seu modelo Account da seguinte forma:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Account &amp;lt; ActiveRecord::Base
  belongs_to :user
  def user_name
    user.name
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OBS: Vou me concentrar apenas no problema com o nome e n&#227;o vou me preocupar em validar se o relacionamento foi estabelecido.&lt;/p&gt;

&lt;p&gt;Bem, finalizada a cr&#237;tica a uma falha de design OO vamos ao objetivo desse post, mostrar como *EU* testaria esse problema.&lt;/p&gt;

&lt;p&gt;Como eu falei no post anterior, n&#227;o gosto muito de usar fixtures para testes unit&#225;rios e por isso mesmo vou apelar aos &lt;a href="http://en.wikipedia.org/wiki/Mock_Object"&gt;mock objects&lt;/a&gt;. Se voc&#234; n&#227;o est&#225; muito familiarizado com mocks sugiro que pare por aqui e leia um pouco mais sobre isso. Uma refer&#234;ncia r&#225;pida pode ser o &lt;a href="http://en.wikipedia.org/wiki/Mock_Object"&gt;wikipedia&lt;/a&gt; mas realmente sugiro que v&#225; mais adiante.&lt;/p&gt;

&lt;p&gt;Testando usando RSpec(usando o framework de mock padr&#227;o):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;before(:each) do
  @account = Account.new    
  @user_mock = mock_model(User)
  @account.stub!(:user).and_return(@user_mock)
end

describe ".user_name" do

  it "should delegate to user.name" do
    @user_mock.should_receive(:name).and_return("Tapaj&#243;s")
    @account.user_name.should == "Tapaj&#243;s"
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Testando usando Test::Unit com mocha:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;setup :create_model

def test_user_name
  @user_mock.expects(:name).returns("Tapaj&#243;s")
  assert_equal "Tapaj&#243;s", @account.user_name
end

private

  def create_model
    @account = Account.new
    @user_mock = mock("User")
    @account.stubs(:user).returns(@user_mock)
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Explicando...&lt;/h3&gt;

&lt;p&gt;Em ambos os casos eu preciso que a minha account simule o relacionamento com User e para isso eu vou retornar um mock object. Isso &#233; feito pelas linhas:&lt;/p&gt;

&lt;p&gt;RSpec:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@account.stub!(:user).and_return(@user_mock)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Test::Unit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@account.stubs(:user).returns(@user_mock)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ap&#243;s o setup ou o before, temos um modelo @account onde o @account.user retorna um mock.&lt;/p&gt;

&lt;p&gt;Feito isso eu preciso configurar o meu mock, isto &#233;, informar que ele receber&#225; uma mensagem name (chamada do m&#233;todo .name) e essa retornar&#225; o meu nome(sim, sou egoc&#234;ntrico). Isso &#233; feito pelos m&#233;todos "should_receive" e "expects" conforme as linhas abaixo:&lt;/p&gt;

&lt;p&gt;RSpec:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@user_mock.should_receive(:name).and_return("Tapaj&#243;s")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Test::Unit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@user_mock.expects(:name).returns("Tapaj&#243;s")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Depois que nossos mocks foram devidamente configurados podemos, finalmente, fazer nossa verifica&#231;&#227;o do retorno, isto &#233;, simplesmente chamar nossos m&#233;todos conferir o retorno. Pronto teste feito com sucesso, sem precisar recorrer a banco de dados nem configurar fixtures.&lt;/p&gt;

&lt;p&gt;Nesse momento deve ter surgido uma d&#250;vida: "Porque uma hora voc&#234; usa stub! e outra um should_receive?"&lt;/p&gt;

&lt;p&gt;A resposta &#233; bem simples, o stub! n&#227;o faz um verify no final enquanto a outra chamada sim. Na pratica isso significa:&lt;/p&gt;

&lt;h3&gt;Stub! ou stubs&lt;/h3&gt;

&lt;p&gt;Quando eu uso stub!(ou um Stubs) eu estou configurando o meu modelo account para responder pelo m&#233;todo user retornando o @mock_user por&#233;m n&#227;o me interessa quebrar esse teste caso voc&#234; n&#227;o chame esse m&#233;todo.&lt;/p&gt;

&lt;h3&gt;Should_receive ou expects&lt;/h3&gt;

&lt;p&gt;Quando eu uso should_receive(ou um expects) eu estou configurando o meu mock user para responder pelo m&#233;todo name por&#233;m caso esse m&#233;todo n&#227;o seja chamado eu devo quebrar meu teste, pois isso seria um comportamento indesej&#225;vel.&lt;/p&gt;

&lt;h2&gt;Porque usar mocks?&lt;/h2&gt;

&lt;p&gt;Ao contr&#225;rio do que muita gente pensa o uso de mocks n&#227;o &#233; um bicho de 7 cabe&#231;as, &#233; bem simples. Na verdade testar &#233; algo simples, desde que voc&#234; tenha dom&#237;nio do ferramental e os mocks s&#227;o realmente &#250;teis em diversos casos.&lt;/p&gt;

&lt;p&gt;Imagina que o seu sistema precise fazer consultas a uma api publica do Yahoo e para isso voc&#234; tenha criado uma classe de consultas. Voc&#234; n&#227;o vai querer(nem o Yahoo vai gostar) ir l&#225; no servidor toda vez que voc&#234; rodar os seus testes. Isso tornaria os seus testes lentos e imposs&#237;vel roda-los offline. Nesse caso voc&#234; resolve seu problema "mockando" essa classe.&lt;/p&gt;</description>
      <pubDate>Sun, 16 Nov 2008 18:22:13 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:bf5b906d-9cec-4301-8cd1-f6480e1d406b</guid>
      <author>Marcos Tapaj&#243;s</author>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks</link>
      <category>rails</category>
      <category>rspec</category>
      <category>rspec_on_rails</category>
      <category>ruby</category>
      <category>test</category>
      <category>Testes</category>
      <category>unit</category>
    </item>
    <item>
      <title>Como Testar parte 1 - Models</title>
      <description>&lt;p&gt;Quando comecei a estudar &lt;a href="http://www.improveit.com.br/xp"&gt;Extreme Programming&lt;/a&gt; descobri que n&#227;o &#233; poss&#237;vel fazer nenhum software de qualidade sem uma excelente base de testes. Desde ent&#227;o tenho me dedicado muito ao estudo das mais diversas ferramentas e t&#233;cnicas para elaborar bons de testes.&lt;/p&gt;

&lt;p&gt;O assunto testes &#233; bastante pol&#234;mico e n&#227;o pretendo (nesse post) tentar convencer ningu&#233;m da import&#226;ncia deles. Se voc&#234; n&#227;o faz testes e/ou discorda de qualquer uma das minhas afirma&#231;&#245;es deixo algumas perguntas para voc&#234; refletir.&lt;/p&gt;

&lt;p&gt;1 - Quantos bugs fixes voc&#234; fez esse ano?&lt;/p&gt;

&lt;p&gt;2 - Quantos tickets abertos existem no seu bug tracker?&lt;/p&gt;

&lt;p&gt;3 - Quantas vezes voc&#234; fez um deploy de uma nova vers&#227;o em uma sexta feira de tarde e saiu mais cedo do trabalho?&lt;/p&gt;

&lt;p&gt;4 - Quantas vezes voc&#234; "virou a noite" esse ano?&lt;/p&gt;

&lt;p&gt;Acabei me tornando um evangelizador de testes por&#233;m nunca fiz nada muito pr&#225;tico para passar o conhecimento que eu adquiri para a comunidade. S&#243; que agora vou me redimir dessa falha iniciando uma s&#233;rie de posts onde vou expor um problema e como EU testaria usando Test::Unit e Rspec. N&#227;o vou falar de Shoulda pois n&#227;o gosto dele. :-)&lt;/p&gt;

&lt;p&gt;A id&#233;ia de escrever essa s&#233;rie de posts sobre testes surgiu logo ap&#243;s a grava&#231;&#227;o do terceiro epis&#243;dio do &lt;a href="http://railsbox.org/2008/9/9/railsbox-podcast-3"&gt;RailsBox&lt;/a&gt; e gostaria de agradecer ao Ozeias e ao Davis Cabral por terem me motivado.&lt;/p&gt;

&lt;p&gt;"Back to the cold cow..."&lt;/p&gt;

&lt;p&gt;O ActiveRecord simplifica muito nossos modelos por&#233;m tenho observado que em v&#225;rios projetos os desenvolvedores deixam de testar corretamente os seus modelos usando a  alega&#231;&#227;o que n&#227;o v&#227;o testar alguma coisa que o Rails j&#225; testou. Esse &#233; um argumento valido em alguns casos pois voc&#234; est&#225; apenas delegando responsabilidades mas voc&#234; sempre deve testar se a responsabilidade foi realmente delegada.&lt;/p&gt;

&lt;p&gt;Um exemplo cl&#225;ssico s&#227;o as valida&#231;&#245;es. Teoricamente voc&#234; n&#227;o precisaria testar como elas s&#227;o implementadas mas deve testar se elas realmente existem pois se algu&#233;m remove-las seus testes v&#227;o continuar passando mas sua aplica&#231;&#227;o estar&#225; quebrada e/ou permitindo inconsist&#234;ncias de banco de dados.&lt;/p&gt;

&lt;p&gt;Nesse post vou mostrar como testar alguns comportamento do ActiveRecord usando como base o modelo User. Todos os c&#243;digos citados nesse post fazem parte de um projeto How Test que est&#225; dispon&#237;vel em: http://github.com/tapajos/how-test&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base
  validates_presence_of :name
  validates_format_of :mail, 
                      :with =&amp;gt; /([-.\w^@]+@(?:[-\w]+.)+[A-Za-z]{2,4})+/i,
                      :on =&amp;gt; :create,
                      :allow_nil =&amp;gt; true
  has_many :accounts
  named_scope :actives, :conditions =&amp;gt; ["active = ?", true]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Uma boa estrat&#233;gia para orientar o desenvolvimento dos teste &#233; elaborar algumas perguntas que dar&#227;o origem aos seus cen&#225;rios de testes.&lt;/p&gt;

&lt;h3&gt;Valida&#231;&#227;o do nome&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Posso cadastrar um usu&#225;rio sem nome? N&#227;o&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Testando usando Test:Unit:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;def test_if_check_presence_of_name
  assert !@user.valid?, "Should be invalid"
  assert_equal "can't be blank", @user.errors[:name]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Na primeira linha desse teste o assert recebe um segundo par&#226;metro que por ser opcional n&#227;o &#233; muito comentado mas merece uma aten&#231;&#227;o especial. Esse argumento nada mais &#233; do que a mensagem que ser&#225; exibida quando o teste quebrar. Quando voc&#234; omite esse par&#226;metro o teste quebra exibindo a mensagem 'false is not true' que n&#227;o ajuda muito a entender o que est&#225; acontecendo.&lt;/p&gt;

&lt;h4&gt;Testando usando RSpec:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;it "should reject if name is not given" do
  @user.should have(1).error_on(:name)
  @user.errors[:name].should == "can't be blank"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Valida&#231;&#227;o do e-mail.&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Posso criar um registro com um e-mail inv&#225;lido? N&#227;o&lt;/li&gt;
&lt;li&gt;Posso atualizar um regitro com um e-mail inv&#225;lido? Sim&lt;/li&gt;
&lt;li&gt;Posso criar um registro com um e-mail em branco? Sim&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Testando usando Test:Unit:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;VALIDS_MAIL = %w(foo@bar.com foo@bar.com.br foo@globo.com foo@i_hate_the_microsoft.com foo@i_love_my_mac.com)
INVALIDS_MAIL = %w(foobar.com foo@bar i_hate_the_microsoft.com i_love_my_mac.com)

def test_if_reject_invalid_format_os_mail_on_create
  INVALIDS_MAIL.each do |mail|
    @user.mail = mail
    assert !@user.valid?, "Should be invalid when mail is #{mail}"
    assert_equal "is invalid", @user.errors[:mail]
  end
end

def test_if_not_reject_when_mail_is_nil
  @user.name = "Tapaj&#243;s"
  assert @user.valid?, "Should be valid"
end

def test_if_not_check_format_of_mail_on_update
  @user.name = "Tapaj&#243;s"
  assert @user.save, "Should save"
  @user.mail = "an invalid mail"
  assert @user.valid?, "Should be valid"
end

def test_if_accept_a_valid_mail
  VALIDS_MAIL.each do |mail|
    @user.name = "Tapaj&#243;s"
    @user.mail = mail
    assert @user.valid?, "Should be valid when mail is #{mail}"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Testando usando RSpec:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;INVALIDS_MAIL.each do |mail|
  it "should reject because #{mail} is an invalid mail" do
    @user.mail = mail
    @user.should have(1).error_on(:mail)
    @user.errors[:mail].should == "is invalid"
  end
end

VALIDS_MAIL.each do |mail|
  it "should be valid when mail is #{mail}" do
    @user.mail = mail
    @user.should_not have(1).error_on(:mail)
  end
end

it "should not reject if mail is not given" do
  @user.name = "Tapaj&#243;s"
  @user.should be_valid
end

it "should not check mail format on update" do
    @user.name = "Tapaj&#243;s"
    @user.save.should be_true
    @user.mail = "an invalid mail"
    @user.should be_valid
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Testando o relacionamento com Account.&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Um usu&#225;rio pode ter mais de uma conta? Sim&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Testando usando Test::Unit:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;def test_has_many_accounts
  association = User.reflect_on_association(:accounts)
  assert association, "Association with account is not found"
  assert_equal :has_many, association.macro
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Testando usando RSpec:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;it "should has many accounts" do
  association = User.reflect_on_association(:accounts)
  association.should_not be_nil
  association.macro.should == :has_many
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Testando o User.actives&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Posso listar usu&#225;rios inativos? N&#227;o&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Testando usando Test::Unit:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;def test_if_actives_use_the_correct_conditions
  assert_equal({:conditions=&amp;gt;["active = ?", true]}, User.actives.proxy_options)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Testando usando RSpec:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;it "should find for all users that status of active is true" do
  User.actives.proxy_options.should == {:conditions=&amp;gt;["active = ?", true]}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Para esse post ficar mais simples e curto n&#227;o me preocupei em validar se o tamanho m&#225;ximo dos campos est&#225; coerente com o tamanho m&#225;ximo permitido pelo tipo no banco de dados. Essa &#233; uma valida&#231;&#227;o EXTREMAMENTE importante que n&#227;o deve ser esquecida!&lt;/p&gt;

&lt;p&gt;No pr&#243;ximo post dessa s&#233;rie falarei um pouco sobre a &lt;a href="http://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Lei de Demeter&lt;/a&gt;, como respeita-la e testar alguns m&#233;todos usando &lt;a href="http://en.wikipedia.org/wiki/Mock_Object"&gt;Mock Objects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O que voc&#234; achou desse artigo? O que voc&#234; gostaria de saber sobre testes de ActiveRecord que eu n&#227;o falei aqui?&lt;/p&gt;

&lt;p&gt;Aguardo o feedback de voc&#234;s.&lt;/p&gt;</description>
      <pubDate>Sun, 26 Oct 2008 02:51:35 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:aa3262eb-fd9b-4304-9bb1-f6ef0d945ee2</guid>
      <author>Marcos Tapaj&#243;s</author>
      <link>http://blog.improveit.com.br/articles/2008/10/26/como-testar-parte-1-models</link>
      <category>rspec</category>
      <category>test</category>
      <category>unit</category>
      <category>Testes</category>
      <category>ruby</category>
      <category>rails</category>
      <category>rspec_on_rails</category>
    </item>
  </channel>
</rss>
