<?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: Como Testar parte 2 - Mocks</title>
    <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks</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 2 - Mocks" by Emerson Macedo</title>
      <description>&lt;p&gt;Eu criei uma gem demeter que faz o trabalho de forma DRY, sem ter que ficar toda hora criando esse c&#243;digo no bra&#231;o&lt;/p&gt;

&lt;p&gt;&lt;a href="http://codificando.com/2009/11/26/law-of-demeter-simples-em-ruby-com-a-gem-demeter/" rel="nofollow"&gt;http://codificando.com/2009/11/26/law-of-demeter-simples-em-ruby-com-a-gem-demeter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Abra&#231;os.&lt;/p&gt;</description>
      <pubDate>Fri, 27 Nov 2009 01:06:31 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:3917ce8a-a9ef-47a6-866e-3dcbecf94292</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1889</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Daniel Lopes</title>
      <description>&lt;p&gt;hum... ent&#227;o tamb&#233;m concordo. Isso &#233; importante em qualquer linguagem, quanto mais caixa preta forem as coisas melhor.&lt;/p&gt;</description>
      <pubDate>Thu, 04 Dec 2008 16:50:32 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:84948318-d2ed-4d9a-8b98-012ad7060a46</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1624</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Lucas de Castro</title>
      <description>&lt;p&gt;Tapaj&#243;s, queria saber como voc&#234;s testam finders mais complexos, como por exemplo envolvendo v&#225;rios models e muitas 'conditions'. Voc&#234;s usam fixtures nesses casos ou fazem com mocks?&lt;/p&gt;</description>
      <pubDate>Mon, 01 Dec 2008 12:25:52 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:40c3c205-8a2e-4feb-ac2f-23eaca0e5401</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1623</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Tapaj&#243;s</title>
      <description>&lt;p&gt;Daniel, usei o exemplo do ActiveRecord apenas para ilustrar. O problema grave mesmo &#233; o excesso de conhecimento de um objeto ao outro.&lt;/p&gt;

&lt;p&gt;Um abra&#231;o&lt;/p&gt;</description>
      <pubDate>Sun, 30 Nov 2008 21:41:38 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:d3cc6cd8-ef77-4e05-a398-4bc2e3c817d0</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1622</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by daniel lopes</title>
      <description>&lt;p&gt;Muito legal o post, s&#243; n&#227;o concordo muito com uma coisa. Associantion proxies... n&#227;o vejo nada de errado em usar @account.user.name, pelo contr&#225;rio, se eu for colocar um m&#233;todo na classe account para cada atributo que usar vou estar duplicando o tamanho do meu c&#243;digo e tornando muito mais dificil de dar manuten&#231;&#227;o. Eu penso que se vc tem um sistema funcionando e muda os campos do DB em alguma tabela &#233; esperado que voc&#234; tenha erros e vc tem que estar preparado para isso.... acho que seria melhor testar a associa&#231;&#227;o de account com user ao inv&#233;s de implementar m&#233;todos dentro de account.&lt;/p&gt;

&lt;p&gt;Bem, essa &#233; a minha opni&#227;o.&lt;/p&gt;</description>
      <pubDate>Fri, 28 Nov 2008 12:53:07 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:27e6240f-a56a-4bd4-adeb-699816136621</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1621</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Tapaj&#243;s</title>
      <description>&lt;p&gt;Oi Sylvestre,&lt;/p&gt;

&lt;p&gt;Quando se usa mock realmente se cai nesse problema. No caso desse exemplo eu queria tr&#234;s coisas:&lt;/p&gt;

&lt;p&gt;1 - Mostrar o problema do conhecimento.&lt;/p&gt;

&lt;p&gt;2 - Me isolar do banco.&lt;/p&gt;

&lt;p&gt;3 - Criar um exemplo did&#225;tico.&lt;/p&gt;

&lt;p&gt;Nessa s&#233;rie muita coisa ser&#225; feita propositadamente de uma forma justamente para usar o que eu estiver querendo mostrar.&lt;/p&gt;

&lt;p&gt;Com rela&#231;&#227;o ao problema que conversamos mais cedo ainda n&#227;o pensei muito, vou tentar alguma forma e depois te conto.&lt;/p&gt;

&lt;p&gt;Vou olhar o seu post mais tarde e fa&#231;o meus coment&#225;rios.&lt;/p&gt;

&lt;p&gt;Um abra&#231;&#227;o e te vejo na hora extra segunda.&lt;/p&gt;

&lt;p&gt;Um abra&#231;o&lt;/p&gt;</description>
      <pubDate>Fri, 28 Nov 2008 09:33:27 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:add7d3f0-50bf-4779-aa8e-59051d7c6a0a</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1620</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Sylvestre Mergulh&#227;o</title>
      <description>&lt;p&gt;Fala Tapa!&lt;/p&gt;

&lt;p&gt;N&#227;o discordo completamente do uso de mocks em testes unit&#225;rios, mas nesse caso espec&#237;fico que voc&#234; descreveu existe um outro problema.&lt;/p&gt;

&lt;p&gt;Caso voc&#234; mude o atributo &lt;em&gt;name&lt;/em&gt; para &lt;em&gt;full_name&lt;/em&gt; voc&#234; ter&#225; que corrigir todos os lugares em que voc&#234; fez mock do m&#233;todo &lt;em&gt;name&lt;/em&gt;. O que &#233; um problema, pode acontecer de testes passarem por ter mockado, mas ao rodar de verdade o c&#243;digo de crash.&lt;/p&gt;

&lt;p&gt;&#201; claro que quem usa mocks tem que estar ciente disso. O importante &#233; ter um teste em algum lugar que ir&#225; quebrar informando o problema.&lt;/p&gt;

&lt;p&gt;Escrevi um &lt;a href="http://mergulhao.info/2008/11/28/como-voc-s-fazem-o-describe-das-suas-specs" rel="nofollow"&gt;post&lt;/a&gt; no meu &lt;a href="http://mergulhao.info" rel="nofollow"&gt;blog&lt;/a&gt; questinando sobre como utilizar o &lt;em&gt;describe&lt;/em&gt; do rspec. D&#234; uma passada l&#225;.&lt;/p&gt;

&lt;p&gt;PS: conseguiu resolver aquele problema que voc&#234; me perguntou mais cedo?&lt;/p&gt;

&lt;p&gt;Um abra&#231;o!&lt;/p&gt;</description>
      <pubDate>Fri, 28 Nov 2008 04:06:15 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:b48c5268-0803-42ae-97a3-b28dc67890e8</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1619</link>
    </item>
    <item>
      <title>"Como Testar parte 2 - Mocks" by Levy</title>
      <description>&lt;p&gt;Ol&#225; Tapaj&#243;s, excelente s&#233;rie sobre o tema!&lt;/p&gt;

&lt;p&gt;Sobre a explica&#231;&#227;o acima sobre o m&#233;todo "stub!", acho que fica mais claro dizer que:&lt;/p&gt;

&lt;p&gt;Ao usar:&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;... estamos simplesmente programando a inst&#226;ncia @account para ter uma resposta "enlatada". Ou seja, ao chamar @account.user ela sempre vai responder com o retorno que colocamos, no caso o @user_mock. Em outras palavras, estamos substituindo o retorno de um m&#233;todo.&lt;/p&gt;

&lt;p&gt;Quando isto seria &#250;til? Por exemplo, se precisamos testar um envio de emails, e obviamente n&#227;o queremos enviar um email de verdade, poder&#237;amos fazer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
Mailer.stub!(:send_mail).and_return(true)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ou seja, toda vez que se chamar Mailer.send_mail de dentro da cl&#225;usula "it" em quest&#227;o, sempre ser&#225; retornado "true". O email n&#227;o &#233; enviado. Existem formas melhores de testar, como contar quantos emails foram enviados ou checar o conte&#250;do e recipientes, mas essa &#233; a id&#233;ia b&#225;sica.&lt;/p&gt;

&lt;p&gt;J&#225; ao usar o:&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;... estamos criando um "mock", que na realidade &#233; um objeto onde existe uma programa&#231;&#227;o de expectativas.&lt;/p&gt;

&lt;p&gt;Lembrando que existem 4 tipos de objetos "dubl&#234;": dummy, fake, stubs e mocks. Os 'stubs' d&#227;o respostas "programadas" e os mocks s&#227;o objetos que possuem um mecanismo de expectativa. Voc&#234; diz o que um mock deve esperar, e ao final do teste caso isto n&#227;o tenha acontecido, ele d&#225; o erro e voc&#234; sabe que algo falhou em seus testes.&lt;/p&gt;

&lt;p&gt;Um artigo que fala sobre estes tipos de objetos "dubl&#234;" est&#225; em &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html" rel="nofollow"&gt;Mocks Aren't Stubs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Acho que &#233; isso... Estou come&#231;ando no estudo de testes, ent&#227;o se falei algo errado por favor me corrijam, pessoal :)&lt;/p&gt;

&lt;p&gt;Como disse o Lucas H&#250;ngaro, vamos discutir mais o tema de testes! &#201; necess&#225;rio! :)&lt;/p&gt;

&lt;p&gt;Abra&#231;o!
Levy&lt;/p&gt;</description>
      <pubDate>Mon, 24 Nov 2008 09:56:58 -0200</pubDate>
      <guid isPermaLink="false">urn:uuid:dd4d1b54-9b21-42af-ab9c-26e4092699de</guid>
      <link>http://blog.improveit.com.br/articles/2008/11/16/como-testar-parte-2-mocks#comment-1617</link>
    </item>
  </channel>
</rss>
