Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Site updated at 2013-01-09 16:05:05 UTC

  • Loading branch information...
commit 85101dad3fed08340578d3acf9fb054bbf5718a3 1 parent d5a1223
@luisalima authored
Showing with 2,103 additions and 525 deletions.
  1. +184 −30 atom.xml
  2. +363 −0 blog/2013/01/09/how-i-test-part-iv/index.html
  3. +19 −1 blog/archives/index.html
  4. +1 −1  blog/categories/advice/atom.xml
  5. +1 −1  blog/categories/analytics/atom.xml
  6. +1 −1  blog/categories/apps/atom.xml
  7. +1 −1  blog/categories/blog/atom.xml
  8. +1 −1  blog/categories/business/atom.xml
  9. +1 −1  blog/categories/code/atom.xml
  10. +1 −1  blog/categories/console/atom.xml
  11. +199 −0 blog/categories/controllers/atom.xml
  12. +160 −0 blog/categories/controllers/index.html
  13. +1 −1  blog/categories/cool-gems/atom.xml
  14. +1 −1  blog/categories/cool/atom.xml
  15. +184 −1 blog/categories/factory-girl/atom.xml
  16. +18 −0 blog/categories/factory-girl/index.html
  17. +1 −1  blog/categories/future/atom.xml
  18. +1 −1  blog/categories/gpio/atom.xml
  19. +1 −1  blog/categories/grooveshark/atom.xml
  20. +1 −1  blog/categories/inspiring/atom.xml
  21. +1 −1  blog/categories/interesting/atom.xml
  22. +1 −1  blog/categories/intriguing/atom.xml
  23. +1 −1  blog/categories/js/atom.xml
  24. +1 −1  blog/categories/learning/atom.xml
  25. +1 −1  blog/categories/links/atom.xml
  26. +1 −1  blog/categories/log/atom.xml
  27. +1 −1  blog/categories/message/atom.xml
  28. +1 −1  blog/categories/mobile/atom.xml
  29. +1 −1  blog/categories/nosql/atom.xml
  30. +1 −1  blog/categories/octopress/atom.xml
  31. +1 −1  blog/categories/ot/atom.xml
  32. +1 −1  blog/categories/pi/atom.xml
  33. +1 −1  blog/categories/rails-tips/atom.xml
  34. +184 −146 blog/categories/rails/atom.xml
  35. +18 −0 blog/categories/rails/index.html
  36. +1 −1  blog/categories/raspberry/atom.xml
  37. +1 −1  blog/categories/raspberrypi/atom.xml
  38. +184 −49 blog/categories/rspec/atom.xml
  39. +18 −0 blog/categories/rspec/index.html
  40. +1 −1  blog/categories/sql/atom.xml
  41. +1 −1  blog/categories/ssh/atom.xml
  42. +1 −1  blog/categories/talks/atom.xml
  43. +1 −1  blog/categories/tdd/atom.xml
  44. +1 −1  blog/categories/ted/atom.xml
  45. +184 −146 blog/categories/testing/atom.xml
  46. +18 −0 blog/categories/testing/index.html
  47. +1 −1  blog/categories/thin/atom.xml
  48. +1 −1  blog/categories/verbose/atom.xml
  49. +1 −1  blog/categories/vnc/atom.xml
  50. +1 −1  blog/categories/web/atom.xml
  51. +1 −1  blog/categories/webdev/atom.xml
  52. +1 −1  blog/categories/webrick/atom.xml
  53. +49 −61 blog/page/2/index.html
  54. +61 −1 blog/page/3/index.html
  55. +215 −49 index.html
  56. +6 −2 sitemap.xml
View
214 atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
@@ -14,6 +14,189 @@
<entry>
+ <title type="html"><![CDATA[How I test part IV -- testing controllers in rails]]></title>
+ <link href="http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/"/>
+ <updated>2013-01-09T11:21:00+00:00</updated>
+ <id>http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv</id>
+ <content type="html"><![CDATA[<p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers &#8211; rather, they would say &#8220;test models and do integration testing, and controllers will be covered by those tests&#8221;. But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">&#8220;Yes, you should write controller tests!&#8221;</a>, controllers should be tested. I&#8217;ll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It&#8217;s very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it&#8217;s because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don&#8217;t see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said&#8230; What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don&#8217;t want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don&#8217;t behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn&#8217;t set your valid_attributes properly &#8211; including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let&#8217;s go through each of these in order&#8230;</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won&#8217;t work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your &#8220;my_model&#8221; Factory, you&#8217;ll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally&#8230; if your model belongs_to another model, let&#8217;s say that it looks like this (can you tell I&#8217;m hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you&#8217;ll have a strawberry_id in your model&#8217;s attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization &#8211; you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here&#8217;s <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don&#8217;t forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>
+
+
+<p>The errors that are given are not clear at all in this case, so make sure to check that you didn&#8217;t forget to set up the valid session and the current_user!</p>
+
+<h2>Nested resources</h2>
+
+<p>If my_model is a nested resource, you need to adapt your controller spec in order to account for that. Suppose that we have</p>
+
+<pre><code>resources :strawberries do
+ resources :my_model do
+</code></pre>
+
+
+<p>When you run the scaffholding for my_model controller tests, you&#8217;ll get a &#8220;No route matches&#8230;&#8221; followed by a hash of my_model.</p>
+
+<p>So what you have to do is to:
+* add the strawberry_id to the actions that require a /strawberries/:strawberry_id/my_model route;
+* add the @strawberry to the redirects that require the same type of routes.</p>
+
+<p>What I do is to use a before(:each) that creates the required @strawberry:</p>
+
+<pre><code>before(:each) do
+ @strawberry = FactoryGirl.create(:strawberry)
+ end
+
+it "redirects to the created my_model" do
+post :create, {:strawberry_id => @strawberry.id, :my_model => valid_attributes}, valid_session
+response.should redirect_to([@strawberry, MyModel.last])
+end
+
+it "redirects to the my_models list" do
+ my_model = MyModel.create! valid_attributes
+ delete :destroy, {:strawberry_id => @strawberry.id, :id => my_model.to_param}, valid_session
+ response.should redirect_to(strawberry_my_models_url(@strawberry))
+end
+
+# Don't forget to create my_models for @strawberry in the nested route!
+describe "GET index" do
+ it "assigns all my_models as @my_models" do
+ my_model = MyModel.create! valid_attributes
+ @strawberry.my_models << my_model
+ get :index, {:strawberry_id => @strawberry.id}, valid_session
+ assigns(:my_models).should eq([my_model])
+ end
+end
+</code></pre>
+
+
+<p>What if you also have a route like this:</p>
+
+<pre><code> resources :my_models, :only => [:index, :show]
+</code></pre>
+
+
+<p>What I do is that I divide the tests into two contexts:</p>
+
+<pre><code> context "directly via /my_models" do
+ #...
+end
+
+context "via /strawberries/:strawberry_id/my_models" do
+ #...
+end
+</code></pre>
+
+
+<p>and place the &#8220;before(:each)&#8221; that creates @strawberry in the nested route context.</p>
+
+<h2>Conclusion</h2>
+
+<p>I managed to detect many bugs in my controllers doing this (especially variables that I had assumed not to be nil and that were not set). Testing controllers is easy and fast, so I strongly advise you to test your controllers!</p>
+
+<p>Eventually, I&#8217;ll also do a post on integration testing with rspec, but for now, <a href="http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html">here&#8217;s another resource from Everyday Rails that covers the topic very nicely</a>. Enjoy!</p>
+]]></content>
+ </entry>
+
+ <entry>
<title type="html"><![CDATA[Understanding large systems on the web - interesting links]]></title>
<link href="http://luisalima.github.com/blog/2013/01/05/understanding-large-systems-on-the-web-interesting-links/"/>
<updated>2013-01-05T15:58:00+00:00</updated>
@@ -857,33 +1040,4 @@ git clone https://github.com/drakedevel/gsclient.git</code></pre>
]]></content>
</entry>
- <entry>
- <title type="html"><![CDATA[Adding automatic code reloading to rails console]]></title>
- <link href="http://luisalima.github.com/blog/2012/12/13/adding-automatic-code-reloading-in-rails-console/"/>
- <updated>2012-12-13T10:56:00+00:00</updated>
- <id>http://luisalima.github.com/blog/2012/12/13/adding-automatic-code-reloading-in-rails-console</id>
- <content type="html"><![CDATA[<p>So I have no idea why the rails console in development mode doesn&#8217;t already do this, since the whole idea of the console in development mode is for us to mess around and test random stuff in development mode, right? Anyway, I got inspired and helped by <a href="http://jkfill.com/">Jkfill</a> with his post on <a href="http://jkfill.com/2012/12/08/automatic-code-reloading-in-rails-console">Automatic Code Reloading in Rails Console</a>, so here goes, more for my records than yours since you should follow the instructions in his post. I am using Rails 3.2.9, so I had to copy the code from the <a href="https://github.com/rails/rails/blob/master/railties/lib/rails/console/app.rb#L25">reload! method in the rails console code</a>, which is not exactly the best practice, but it&#8217;s just for dev mode and it works :-)</p>
-
-<pre><code> #this should be in config/initializers/irb_reloading.rb
- if defined?(IRB::Context) && !defined?(Rails::Server) && Rails.env.development?
-
- class IRB::Context
- def evaluate_with_reloading(line, line_no)
-
- ActionDispatch::Reloader.cleanup!
- ActionDispatch::Reloader.prepare!
- evaluate_without_reloading(line, line_no)
- end
- alias_method_chain :evaluate, :reloading
- end
-
- puts "=> IRB code reloading enabled"
-end
-</code></pre>
-
-
-<p>In the meanwhile, shortly browsed the code at <a href="https://github.com/rails">the Rails gihub project</a> and it&#8217;s really a nice way to learn more about rails&#8230; must do it more often.</p>
-]]></content>
- </entry>
-
</feed>
View
363 blog/2013/01/09/how-i-test-part-iv/index.html
@@ -0,0 +1,363 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>How I test part IV -- testing controllers in rails - Entreprevelopment adventures</title>
+ <meta name="author" content="Luisa Lima">
+
+
+ <meta name="description" content="First and foremost, I advise you to read this post from Everyday Rails. It gives a very nice overview, by controller method, of what controller tests &hellip;">
+
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+
+ <link href="/atom.xml" rel="alternate" title="Entreprevelopment adventures" type="application/atom+xml">
+ <link rel="canonical" href="">
+ <link href="/favicon.png" rel="shortcut icon">
+ <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
+ <!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+
+</head>
+
+<body>
+ <header id="header" class="inner"><h1><a href="/">Entreprevelopment adventures</a></h1>
+<nav id="main-nav"><ul class="main">
+ <li><a href="/">Blog</a></li>
+ <li><a href="/blog/archives">Archives</a></li>
+</ul>
+</nav>
+<nav id="mobile-nav">
+ <div class="alignleft menu">
+ <a class="button">Menu</a>
+ <div class="container"><ul class="main">
+ <li><a href="/">Blog</a></li>
+ <li><a href="/blog/archives">Archives</a></li>
+</ul>
+</div>
+ </div>
+ <div class="alignright search">
+ <a class="button"></a>
+ <div class="container">
+ <form action="http://google.com/search" method="get">
+ <input type="text" name="q" results="0">
+ <input type="hidden" name="q" value="site:luisalima.github.com">
+ </form>
+ </div>
+ </div>
+</nav>
+<nav id="sub-nav" class="alignright">
+ <div class="social">
+
+
+
+ <a class="twitter" href="http://twitter.com/luisamoyalima" title="Twitter">Twitter</a>
+
+
+ <a class="github" href="https://github.com/luisalima" title="GitHub">GitHub</a>
+
+
+
+
+
+
+
+ <a class="rss" href="/atom.xml" title="RSS">RSS</a>
+
+ </div>
+ <form class="search" action="http://google.com/search" method="get">
+ <input class="alignright" type="text" name="q" results="0">
+ <input type="hidden" name="q" value="site:luisalima.github.com">
+ </form>
+</nav>
+
+</header>
+
+
+<div id="banner" class="inner">
+ <div class="container">
+ <ul class="feed"></ul>
+ </div>
+ <small><a href="http://twitter.com/luisamoyalima">luisamoyalima</a> @ <a href="http://twitter.com">Twitter</a></small>
+ <div class="loading">Loading...</div>
+</div>
+<script src="/javascripts/twitter.js"></script>
+<script type="text/javascript">
+ (function($){
+ $('#banner').getTwitterFeed('luisamoyalima', 4, false);
+ })(jQuery);
+</script>
+
+
+ <div id="content" class="inner"><article class="post">
+ <h2 class="title">How I Test Part IV -- Testing Controllers in Rails</h2>
+ <div class="entry-content"><p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers &#8211; rather, they would say &#8220;test models and do integration testing, and controllers will be covered by those tests&#8221;. But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">&#8220;Yes, you should write controller tests!&#8221;</a>, controllers should be tested. I&#8217;ll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It&#8217;s very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it&#8217;s because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don&#8217;t see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said&#8230; What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don&#8217;t want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don&#8217;t behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn&#8217;t set your valid_attributes properly &#8211; including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let&#8217;s go through each of these in order&#8230;</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won&#8217;t work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your &#8220;my_model&#8221; Factory, you&#8217;ll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally&#8230; if your model belongs_to another model, let&#8217;s say that it looks like this (can you tell I&#8217;m hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you&#8217;ll have a strawberry_id in your model&#8217;s attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization &#8211; you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here&#8217;s <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don&#8217;t forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>
+
+
+<p>The errors that are given are not clear at all in this case, so make sure to check that you didn&#8217;t forget to set up the valid session and the current_user!</p>
+
+<h2>Nested resources</h2>
+
+<p>If my_model is a nested resource, you need to adapt your controller spec in order to account for that. Suppose that we have</p>
+
+<pre><code>resources :strawberries do
+ resources :my_model do
+</code></pre>
+
+
+<p>When you run the scaffholding for my_model controller tests, you&#8217;ll get a &#8220;No route matches&#8230;&#8221; followed by a hash of my_model.</p>
+
+<p>So what you have to do is to:
+* add the strawberry_id to the actions that require a /strawberries/:strawberry_id/my_model route;
+* add the @strawberry to the redirects that require the same type of routes.</p>
+
+<p>What I do is to use a before(:each) that creates the required @strawberry:</p>
+
+<pre><code>before(:each) do
+ @strawberry = FactoryGirl.create(:strawberry)
+ end
+
+it "redirects to the created my_model" do
+post :create, {:strawberry_id => @strawberry.id, :my_model => valid_attributes}, valid_session
+response.should redirect_to([@strawberry, MyModel.last])
+end
+
+it "redirects to the my_models list" do
+ my_model = MyModel.create! valid_attributes
+ delete :destroy, {:strawberry_id => @strawberry.id, :id => my_model.to_param}, valid_session
+ response.should redirect_to(strawberry_my_models_url(@strawberry))
+end
+
+# Don't forget to create my_models for @strawberry in the nested route!
+describe "GET index" do
+ it "assigns all my_models as @my_models" do
+ my_model = MyModel.create! valid_attributes
+ @strawberry.my_models << my_model
+ get :index, {:strawberry_id => @strawberry.id}, valid_session
+ assigns(:my_models).should eq([my_model])
+ end
+end
+</code></pre>
+
+
+<p>What if you also have a route like this:</p>
+
+<pre><code> resources :my_models, :only => [:index, :show]
+</code></pre>
+
+
+<p>What I do is that I divide the tests into two contexts:</p>
+
+<pre><code> context "directly via /my_models" do
+ #...
+end
+
+context "via /strawberries/:strawberry_id/my_models" do
+ #...
+end
+</code></pre>
+
+
+<p>and place the &#8220;before(:each)&#8221; that creates @strawberry in the nested route context.</p>
+
+<h2>Conclusion</h2>
+
+<p>I managed to detect many bugs in my controllers doing this (especially variables that I had assumed not to be nil and that were not set). Testing controllers is easy and fast, so I strongly advise you to test your controllers!</p>
+
+<p>Eventually, I&#8217;ll also do a post on integration testing with rspec, but for now, <a href="http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html">here&#8217;s another resource from Everyday Rails that covers the topic very nicely</a>. Enjoy!</p>
+</div>
+
+
+<div class="meta">
+ <div class="date">
+
+
+
+
+
+
+
+
+
+
+
+<time datetime="2013-01-09T11:21:00+00:00" pubdate data-updated="true">Jan 9<span>th</span>, 2013</time></div>
+ <div class="tags">
+
+
+ <a class='category' href='/blog/categories/controllers/'>controllers</a>, <a class='category' href='/blog/categories/factory-girl/'>factory_girl</a>, <a class='category' href='/blog/categories/rails/'>rails</a>, <a class='category' href='/blog/categories/rspec/'>rspec</a>, <a class='category' href='/blog/categories/testing/'>testing</a>
+
+
+</div>
+
+</div></article>
+
+ <div class="share">
+ <div class="addthis_toolbox addthis_default_style ">
+
+
+ <a class="addthis_button_tweet"></a>
+
+
+ <a class="addthis_counter addthis_pill_style"></a>
+ </div>
+ <script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid="></script>
+</div>
+
+
+
+<section id="comment">
+ <h2 class="title">Comments</h2>
+ <div id="disqus_thread" aria-live="polite"><noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+</div>
+</section>
+</div>
+ <footer id="footer" class="inner">Copyright &copy; 2013
+
+ Luisa Lima
+
+</footer>
+ <script src="/javascripts/slash.js"></script>
+<script src="/javascripts/jquery.fancybox.pack.js"></script>
+<script type="text/javascript">
+(function($){
+ $('.fancybox').fancybox();
+})(jQuery);
+</script> <!-- Delete or comment this line to disable Fancybox -->
+
+
+<script type="text/javascript">
+ var disqus_shortname = 'luisalima';
+
+
+ // var disqus_developer = 1;
+ var disqus_identifier = 'http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/';
+ var disqus_url = 'http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/';
+ var disqus_script = 'embed.js';
+
+ (function () {
+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
+ dsq.src = 'http://' + disqus_shortname + '.disqus.com/' + disqus_script;
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
+ }());
+</script>
+
+
+
+ <script type="text/javascript">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-37431227-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+ </script>
+
+
+
+</body>
+</html>
View
20 blog/archives/index.html
@@ -7,7 +7,7 @@
<meta name="author" content="Luisa Lima">
- <meta name="description" content="2013 Understanding large systems on the web - interesting links Jan 5 mobile, nosql, sql, web Random notes on Factory Girl Part II Jan 4 &hellip;">
+ <meta name="description" content="2013 How I test part IV &#8211; testing controllers in rails Jan 9 controllers, factory_girl, rails, rspec, testing Understanding large systems on &hellip;">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
@@ -98,6 +98,24 @@
<section class="archives"><h1 class="year">2013</h1>
<article>
+ <h2 class="title"><a href="/blog/2013/01/09/how-i-test-part-iv/">How I test part IV &#8211; testing controllers in rails</a></h2>
+ <div class="meta">
+ <span class="date">Jan 9</span>
+ <span class="tags">
+
+
+ <a class='category' href='/blog/categories/controllers/'>controllers</a>, <a class='category' href='/blog/categories/factory-girl/'>factory_girl</a>, <a class='category' href='/blog/categories/rails/'>rails</a>, <a class='category' href='/blog/categories/rspec/'>rspec</a>, <a class='category' href='/blog/categories/testing/'>testing</a>
+
+
+</span>
+
+ </div>
+</article>
+
+
+
+
+<article>
<h2 class="title"><a href="/blog/2013/01/05/understanding-large-systems-on-the-web-interesting-links/">Understanding large systems on the web - interesting links</a></h2>
<div class="meta">
<span class="date">Jan 5</span>
View
2  blog/categories/advice/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: advice | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/advice/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/analytics/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: analytics | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/analytics/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/apps/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: apps | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/apps/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/blog/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: blog | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/blog/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/business/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: business | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/business/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/code/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: code | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/code/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/console/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: console | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/console/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
199 blog/categories/controllers/atom.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+
+ <title><![CDATA[Category: controllers | Entreprevelopment adventures]]></title>
+ <link href="http://luisalima.github.com/blog/categories/controllers/atom.xml" rel="self"/>
+ <link href="http://luisalima.github.com/"/>
+ <updated>2013-01-09T16:02:42+00:00</updated>
+ <id>http://luisalima.github.com/</id>
+ <author>
+ <name><![CDATA[Luisa Lima]]></name>
+
+ </author>
+ <generator uri="http://octopress.org/">Octopress</generator>
+
+
+ <entry>
+ <title type="html"><![CDATA[How I test part IV -- testing controllers in rails]]></title>
+ <link href="http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/"/>
+ <updated>2013-01-09T11:21:00+00:00</updated>
+ <id>http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv</id>
+ <content type="html"><![CDATA[<p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers -- rather, they would say "test models and do integration testing, and controllers will be covered by those tests". But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">"Yes, you should write controller tests!"</a>, controllers should be tested. I'll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It's very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it's because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don't see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said... What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don't want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don't behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn't set your valid_attributes properly -- including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let's go through each of these in order...</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won't work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your "my_model" Factory, you'll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally... if your model belongs_to another model, let's say that it looks like this (can you tell I'm hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you'll have a strawberry_id in your model's attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization -- you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here's <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don't forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>
+
+
+<p>The errors that are given are not clear at all in this case, so make sure to check that you didn't forget to set up the valid session and the current_user!</p>
+
+<h2>Nested resources</h2>
+
+<p>If my_model is a nested resource, you need to adapt your controller spec in order to account for that. Suppose that we have</p>
+
+<pre><code>resources :strawberries do
+ resources :my_model do
+</code></pre>
+
+
+<p>When you run the scaffholding for my_model controller tests, you'll get a "No route matches..." followed by a hash of my_model.</p>
+
+<p>So what you have to do is to:
+* add the strawberry_id to the actions that require a /strawberries/:strawberry_id/my_model route;
+* add the @strawberry to the redirects that require the same type of routes.</p>
+
+<p>What I do is to use a before(:each) that creates the required @strawberry:</p>
+
+<pre><code>before(:each) do
+ @strawberry = FactoryGirl.create(:strawberry)
+ end
+
+it "redirects to the created my_model" do
+post :create, {:strawberry_id => @strawberry.id, :my_model => valid_attributes}, valid_session
+response.should redirect_to([@strawberry, MyModel.last])
+end
+
+it "redirects to the my_models list" do
+ my_model = MyModel.create! valid_attributes
+ delete :destroy, {:strawberry_id => @strawberry.id, :id => my_model.to_param}, valid_session
+ response.should redirect_to(strawberry_my_models_url(@strawberry))
+end
+
+# Don't forget to create my_models for @strawberry in the nested route!
+describe "GET index" do
+ it "assigns all my_models as @my_models" do
+ my_model = MyModel.create! valid_attributes
+ @strawberry.my_models << my_model
+ get :index, {:strawberry_id => @strawberry.id}, valid_session
+ assigns(:my_models).should eq([my_model])
+ end
+end
+</code></pre>
+
+
+<p>What if you also have a route like this:</p>
+
+<pre><code> resources :my_models, :only => [:index, :show]
+</code></pre>
+
+
+<p>What I do is that I divide the tests into two contexts:</p>
+
+<pre><code> context "directly via /my_models" do
+ #...
+end
+
+context "via /strawberries/:strawberry_id/my_models" do
+ #...
+end
+</code></pre>
+
+
+<p>and place the "before(:each)" that creates @strawberry in the nested route context.</p>
+
+<h2>Conclusion</h2>
+
+<p>I managed to detect many bugs in my controllers doing this (especially variables that I had assumed not to be nil and that were not set). Testing controllers is easy and fast, so I strongly advise you to test your controllers!</p>
+
+<p>Eventually, I'll also do a post on integration testing with rspec, but for now, <a href="http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html">here's another resource from Everyday Rails that covers the topic very nicely</a>. Enjoy!</p>
+]]></content>
+ </entry>
+
+</feed>
View
160 blog/categories/controllers/index.html
@@ -0,0 +1,160 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Category: controllers - Entreprevelopment adventures</title>
+ <meta name="author" content="Luisa Lima">
+
+
+ <meta name="description" content="Category: controllers">
+
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+
+ <link href="/atom.xml" rel="alternate" title="Entreprevelopment adventures" type="application/atom+xml">
+ <link rel="canonical" href="">
+ <link href="/favicon.png" rel="shortcut icon">
+ <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
+ <!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+
+</head>
+
+<body>
+ <header id="header" class="inner"><h1><a href="/">Entreprevelopment adventures</a></h1>
+<nav id="main-nav"><ul class="main">
+ <li><a href="/">Blog</a></li>
+ <li><a href="/blog/archives">Archives</a></li>
+</ul>
+</nav>
+<nav id="mobile-nav">
+ <div class="alignleft menu">
+ <a class="button">Menu</a>
+ <div class="container"><ul class="main">
+ <li><a href="/">Blog</a></li>
+ <li><a href="/blog/archives">Archives</a></li>
+</ul>
+</div>
+ </div>
+ <div class="alignright search">
+ <a class="button"></a>
+ <div class="container">
+ <form action="http://google.com/search" method="get">
+ <input type="text" name="q" results="0">
+ <input type="hidden" name="q" value="site:luisalima.github.com">
+ </form>
+ </div>
+ </div>
+</nav>
+<nav id="sub-nav" class="alignright">
+ <div class="social">
+
+
+
+ <a class="twitter" href="http://twitter.com/luisamoyalima" title="Twitter">Twitter</a>
+
+
+ <a class="github" href="https://github.com/luisalima" title="GitHub">GitHub</a>
+
+
+
+
+
+
+
+ <a class="rss" href="/atom.xml" title="RSS">RSS</a>
+
+ </div>
+ <form class="search" action="http://google.com/search" method="get">
+ <input class="alignright" type="text" name="q" results="0">
+ <input type="hidden" name="q" value="site:luisalima.github.com">
+ </form>
+</nav>
+
+</header>
+
+
+<div id="banner" class="inner">
+ <div class="container">
+ <ul class="feed"></ul>
+ </div>
+ <small><a href="http://twitter.com/luisamoyalima">luisamoyalima</a> @ <a href="http://twitter.com">Twitter</a></small>
+ <div class="loading">Loading...</div>
+</div>
+<script src="/javascripts/twitter.js"></script>
+<script type="text/javascript">
+ (function($){
+ $('#banner').getTwitterFeed('luisamoyalima', 4, false);
+ })(jQuery);
+</script>
+
+
+ <div id="content" class="inner">
+
+
+
+
+
+ <section class="archives"><h1 class="year">2013</h1>
+
+<article>
+ <h2 class="title"><a href="/blog/2013/01/09/how-i-test-part-iv/">How I test part IV -- testing controllers in rails</a></h2>
+ <div class="meta">
+ <span class="date">Jan 9</span>
+ <span class="tags">
+
+
+ <a class='category' href='/blog/categories/controllers/'>controllers</a>, <a class='category' href='/blog/categories/factory-girl/'>factory_girl</a>, <a class='category' href='/blog/categories/rails/'>rails</a>, <a class='category' href='/blog/categories/rspec/'>rspec</a>, <a class='category' href='/blog/categories/testing/'>testing</a>
+
+
+</span>
+
+ </div>
+</article>
+
+</div>
+ <footer id="footer" class="inner">Copyright &copy; 2013
+
+ Luisa Lima
+
+</footer>
+ <script src="/javascripts/slash.js"></script>
+<script src="/javascripts/jquery.fancybox.pack.js"></script>
+<script type="text/javascript">
+(function($){
+ $('.fancybox').fancybox();
+})(jQuery);
+</script> <!-- Delete or comment this line to disable Fancybox -->
+
+
+<script type="text/javascript">
+ var disqus_shortname = 'luisalima';
+
+
+ var disqus_script = 'count.js';
+
+ (function () {
+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
+ dsq.src = 'http://' + disqus_shortname + '.disqus.com/' + disqus_script;
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
+ }());
+</script>
+
+
+
+ <script type="text/javascript">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-37431227-1']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+ </script>
+
+
+
+</body>
+</html>
View
2  blog/categories/cool-gems/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: cool_gems | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/cool-gems/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/cool/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: cool | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/cool/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
185 blog/categories/factory-girl/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: factory_girl | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/factory-girl/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
@@ -14,6 +14,189 @@
<entry>
+ <title type="html"><![CDATA[How I test part IV -- testing controllers in rails]]></title>
+ <link href="http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/"/>
+ <updated>2013-01-09T11:21:00+00:00</updated>
+ <id>http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv</id>
+ <content type="html"><![CDATA[<p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers -- rather, they would say "test models and do integration testing, and controllers will be covered by those tests". But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">"Yes, you should write controller tests!"</a>, controllers should be tested. I'll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It's very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it's because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don't see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said... What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don't want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don't behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn't set your valid_attributes properly -- including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let's go through each of these in order...</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won't work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your "my_model" Factory, you'll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally... if your model belongs_to another model, let's say that it looks like this (can you tell I'm hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you'll have a strawberry_id in your model's attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization -- you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here's <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don't forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>
+
+
+<p>The errors that are given are not clear at all in this case, so make sure to check that you didn't forget to set up the valid session and the current_user!</p>
+
+<h2>Nested resources</h2>
+
+<p>If my_model is a nested resource, you need to adapt your controller spec in order to account for that. Suppose that we have</p>
+
+<pre><code>resources :strawberries do
+ resources :my_model do
+</code></pre>
+
+
+<p>When you run the scaffholding for my_model controller tests, you'll get a "No route matches..." followed by a hash of my_model.</p>
+
+<p>So what you have to do is to:
+* add the strawberry_id to the actions that require a /strawberries/:strawberry_id/my_model route;
+* add the @strawberry to the redirects that require the same type of routes.</p>
+
+<p>What I do is to use a before(:each) that creates the required @strawberry:</p>
+
+<pre><code>before(:each) do
+ @strawberry = FactoryGirl.create(:strawberry)
+ end
+
+it "redirects to the created my_model" do
+post :create, {:strawberry_id => @strawberry.id, :my_model => valid_attributes}, valid_session
+response.should redirect_to([@strawberry, MyModel.last])
+end
+
+it "redirects to the my_models list" do
+ my_model = MyModel.create! valid_attributes
+ delete :destroy, {:strawberry_id => @strawberry.id, :id => my_model.to_param}, valid_session
+ response.should redirect_to(strawberry_my_models_url(@strawberry))
+end
+
+# Don't forget to create my_models for @strawberry in the nested route!
+describe "GET index" do
+ it "assigns all my_models as @my_models" do
+ my_model = MyModel.create! valid_attributes
+ @strawberry.my_models << my_model
+ get :index, {:strawberry_id => @strawberry.id}, valid_session
+ assigns(:my_models).should eq([my_model])
+ end
+end
+</code></pre>
+
+
+<p>What if you also have a route like this:</p>
+
+<pre><code> resources :my_models, :only => [:index, :show]
+</code></pre>
+
+
+<p>What I do is that I divide the tests into two contexts:</p>
+
+<pre><code> context "directly via /my_models" do
+ #...
+end
+
+context "via /strawberries/:strawberry_id/my_models" do
+ #...
+end
+</code></pre>
+
+
+<p>and place the "before(:each)" that creates @strawberry in the nested route context.</p>
+
+<h2>Conclusion</h2>
+
+<p>I managed to detect many bugs in my controllers doing this (especially variables that I had assumed not to be nil and that were not set). Testing controllers is easy and fast, so I strongly advise you to test your controllers!</p>
+
+<p>Eventually, I'll also do a post on integration testing with rspec, but for now, <a href="http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html">here's another resource from Everyday Rails that covers the topic very nicely</a>. Enjoy!</p>
+]]></content>
+ </entry>
+
+ <entry>
<title type="html"><![CDATA[Random notes on Factory Girl Part II]]></title>
<link href="http://luisalima.github.com/blog/2013/01/04/random-notes-on-factory-girl-part-ii/"/>
<updated>2013-01-04T20:25:00+00:00</updated>
View
18 blog/categories/factory-girl/index.html
@@ -98,6 +98,24 @@
<section class="archives"><h1 class="year">2013</h1>
<article>
+ <h2 class="title"><a href="/blog/2013/01/09/how-i-test-part-iv/">How I test part IV -- testing controllers in rails</a></h2>
+ <div class="meta">
+ <span class="date">Jan 9</span>
+ <span class="tags">
+
+
+ <a class='category' href='/blog/categories/controllers/'>controllers</a>, <a class='category' href='/blog/categories/factory-girl/'>factory_girl</a>, <a class='category' href='/blog/categories/rails/'>rails</a>, <a class='category' href='/blog/categories/rspec/'>rspec</a>, <a class='category' href='/blog/categories/testing/'>testing</a>
+
+
+</span>
+
+ </div>
+</article>
+
+
+
+
+<article>
<h2 class="title"><a href="/blog/2013/01/04/random-notes-on-factory-girl-part-ii/">Random notes on Factory Girl Part II</a></h2>
<div class="meta">
<span class="date">Jan 4</span>
View
2  blog/categories/future/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: future | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/future/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/gpio/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: gpio | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/gpio/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/grooveshark/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: grooveshark | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/grooveshark/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/inspiring/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: inspiring | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/inspiring/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/interesting/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: interesting | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/interesting/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/intriguing/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: intriguing | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/intriguing/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/js/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: js | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/js/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/learning/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: learning | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/learning/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/links/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: links | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/links/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/log/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: log | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/log/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/message/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: message | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/message/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/mobile/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: mobile | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/mobile/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/nosql/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: nosql | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/nosql/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/octopress/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: octopress | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/octopress/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/ot/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: OT | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/ot/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/pi/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: pi | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/pi/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/rails-tips/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: rails_tips | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/rails-tips/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
330 blog/categories/rails/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: rails | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/rails/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
@@ -14,6 +14,189 @@
<entry>
+ <title type="html"><![CDATA[How I test part IV -- testing controllers in rails]]></title>
+ <link href="http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/"/>
+ <updated>2013-01-09T11:21:00+00:00</updated>
+ <id>http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv</id>
+ <content type="html"><![CDATA[<p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers -- rather, they would say "test models and do integration testing, and controllers will be covered by those tests". But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">"Yes, you should write controller tests!"</a>, controllers should be tested. I'll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It's very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it's because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don't see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said... What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don't want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don't behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn't set your valid_attributes properly -- including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let's go through each of these in order...</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won't work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your "my_model" Factory, you'll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally... if your model belongs_to another model, let's say that it looks like this (can you tell I'm hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you'll have a strawberry_id in your model's attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization -- you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here's <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don't forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>
+
+
+<p>The errors that are given are not clear at all in this case, so make sure to check that you didn't forget to set up the valid session and the current_user!</p>
+
+<h2>Nested resources</h2>
+
+<p>If my_model is a nested resource, you need to adapt your controller spec in order to account for that. Suppose that we have</p>
+
+<pre><code>resources :strawberries do
+ resources :my_model do
+</code></pre>
+
+
+<p>When you run the scaffholding for my_model controller tests, you'll get a "No route matches..." followed by a hash of my_model.</p>
+
+<p>So what you have to do is to:
+* add the strawberry_id to the actions that require a /strawberries/:strawberry_id/my_model route;
+* add the @strawberry to the redirects that require the same type of routes.</p>
+
+<p>What I do is to use a before(:each) that creates the required @strawberry:</p>
+
+<pre><code>before(:each) do
+ @strawberry = FactoryGirl.create(:strawberry)
+ end
+
+it "redirects to the created my_model" do
+post :create, {:strawberry_id => @strawberry.id, :my_model => valid_attributes}, valid_session
+response.should redirect_to([@strawberry, MyModel.last])
+end
+
+it "redirects to the my_models list" do
+ my_model = MyModel.create! valid_attributes
+ delete :destroy, {:strawberry_id => @strawberry.id, :id => my_model.to_param}, valid_session
+ response.should redirect_to(strawberry_my_models_url(@strawberry))
+end
+
+# Don't forget to create my_models for @strawberry in the nested route!
+describe "GET index" do
+ it "assigns all my_models as @my_models" do
+ my_model = MyModel.create! valid_attributes
+ @strawberry.my_models << my_model
+ get :index, {:strawberry_id => @strawberry.id}, valid_session
+ assigns(:my_models).should eq([my_model])
+ end
+end
+</code></pre>
+
+
+<p>What if you also have a route like this:</p>
+
+<pre><code> resources :my_models, :only => [:index, :show]
+</code></pre>
+
+
+<p>What I do is that I divide the tests into two contexts:</p>
+
+<pre><code> context "directly via /my_models" do
+ #...
+end
+
+context "via /strawberries/:strawberry_id/my_models" do
+ #...
+end
+</code></pre>
+
+
+<p>and place the "before(:each)" that creates @strawberry in the nested route context.</p>
+
+<h2>Conclusion</h2>
+
+<p>I managed to detect many bugs in my controllers doing this (especially variables that I had assumed not to be nil and that were not set). Testing controllers is easy and fast, so I strongly advise you to test your controllers!</p>
+
+<p>Eventually, I'll also do a post on integration testing with rspec, but for now, <a href="http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html">here's another resource from Everyday Rails that covers the topic very nicely</a>. Enjoy!</p>
+]]></content>
+ </entry>
+
+ <entry>
<title type="html"><![CDATA[Random notes on Factory Girl Part II]]></title>
<link href="http://luisalima.github.com/blog/2013/01/04/random-notes-on-factory-girl-part-ii/"/>
<updated>2013-01-04T20:25:00+00:00</updated>
@@ -369,149 +552,4 @@ b.save
]]></content>
</entry>
- <entry>
- <title type="html"><![CDATA[How I test part II - testing models]]></title>
- <link href="http://luisalima.github.com/blog/2012/12/19/how-i-test-part-ii/"/>
- <updated>2012-12-19T10:17:00+00:00</updated>
- <id>http://luisalima.github.com/blog/2012/12/19/how-i-test-part-ii</id>
- <content type="html"><![CDATA[<h2>Testing models</h2>
-
-<p>From <a href="http://guides.rubyonrails.org/testing.html">rails guides</a>:</p>
-
-<p>"Ideally, you would like to include a test for everything which could possibly break. It’s a good practice to have at least one test for each of your validations and at least one test for every method in your model."</p>
-
-<p>Which leads me to...</p>
-
-<h2>Why I use shoulda</h2>
-
-<p>I think that the best thing is just to take a peek at <a href="http://cheat.errtheblog.com/s/rspec_shoulda/">The shoulda cheat sheets</a>.</p>
-
-<p>You actually get a guide of what to test by looking at the cheat sheet! It's too bad that it's not up to date (actually I think that the whole site is not up to date -- even the gem doesn't work anymore, which I think is too bad :-( )</p>
-
-<p>Furthermore, it's great for lazy people and it's definitely easier on the eyes. Just check the following example:</p>
-
-<p><div><script src='https://gist.github.com/4339197.js?file=shoulda_model.rb'></script>
-<noscript><pre><code># without shoulda... tons of code :-)
-it &quot;should belong to person&quot; do
- Model.reflect_on_association(:person).macro.should == :belongs_to
-end
-
-# with shoulda... one-liner!
-it{should belong_to(:person)}
-</code></pre></noscript></div>
-</p>
-
-<p>Convinced? ;-)</p>
-
-<h2>What I test exactly with shoulda</h2>
-
-<p>I go through the whole list of shoulda macros for models, and I just use and abuse them. Since the cheat sheet is not up to date, I created a new one (just for models for now) at:</p>
-
-<p><div><script src='https://gist.github.com/4339490.js?file=shoulda_cheat_model.rb'></script>
-<noscript><pre><code># updated from the original @ http://cheat.errtheblog.com/s/rspec_shoulda
-# just a subset -- models -- is included here. I'll update this, and create cheat sheets for others, as I go along.
-# I marked the ones I added with NEW and also added the links to the corresponding code, as I think it's useful.
-# Any comments/corrections are welcome!
-
-# ================= Data and Associations =======================
-# https://github.com/thoughtbot/shoulda-matchers/tree/master/lib/shoulda/matchers/active_record
-
-it { should_not have_db_column(:admin).of_type(:boolean) }
-it { should have_db_column(:salary).
- of_type(:decimal).
- with_options(:precision =&gt; 10, :scale =&gt; 2) }
-it { should have_readonly_attributes(:password) }
-it { should belong_to(:parent) }
-it { should have_db_index(:id) } # NEW
-
-it { should have_many(:friends) }
-it { should have_many(:enemies).through(:friends) }
-it { should have_many(:enemies).dependent(:destroy) }
-
-it { should have_one(:god) }
-
-it { should have_and_belong_to_many(:posts) }
-
-it {should accept_nested_attributes_for(:user)} # NEW
-
-# ================== Validation Matchers ============================
-# https://github.com/thoughtbot/shoulda-matchers/tree/master/lib/shoulda/matchers/active_model
-
-it { should validate_uniqueness_of(:keyword) }
-it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
-it { should validate_uniqueness_of(:email).scoped_to(:name) }
-it { should validate_uniqueness_of(:email).
- scoped_to(:first_name, :last_name) }
-it { should validate_uniqueness_of(:keyword).case_insensitive }
-
-it { should validate_presence_of(:name) }
-it { should validate_presence_of(:name).
- with_message(/is not optional/) }
-
-it { should validate_numericality_of(:age) }
-
-it { should validate_format_of(:name).
- with('12345').
- with_message(/is not optional/) }
-it { should validate_format_of(:name).
- not_with('12D45').
- with_message(/is not optional/) }
-
-it { should validate_acceptance_of(:eula) }
-
-it { should ensure_length_of(:password).
- is_at_least(6).
- is_at_most(20) }
-it { should ensure_length_of(:name).
- is_at_least(3).
- with_short_message(/not long enough/) }
-it { should ensure_length_of(:ssn).
- is_equal_to(9).
- with_message(/is invalid/) }
-
-it { should ensure_inclusion_of(:age).in_range(0..100) }
-it { should_not allow_mass_assignment_of(:password) }
-it { should allow_mass_assignment_of(:first_name) }
-it { should validate_format_of(:first_name).with(&quot;Carl&quot;) }
-
-it { should validate_confirmation_of(:password) } # NEW</code></pre></noscript></div>
-</p>
-
-<p>Yes, it's a bit tedious. But (1) you make sure that you are actually testing all these details, which is extremely useful when you are constantly refactoring your code, and (2) it's not that slow with the multiple cursors of Sublime Text 2. You just need to have a scaffold.</p>
-
-<h2>What I test without shoulda</h2>
-
-<p>Apart from model methods, is there that much to test without shoulda? I test:</p>
-
-<ul>
-<li>Any model methods, extensively</li>
-<li>Callbacks, extremely extensively</li>
-<li>Some validations - but just to make sure I coded them right. See more of this perspective in <a href="http://www.christopherbloom.com/2011/07/12/testing-model-validations-in-rspec-the-short-and-sweet-way/">Testing model validations in rspec the short and sweet way</a></li>
-</ul>
-
-
-<p>... and for now, that's about it.</p>
-
-<h2>Regarding generating fake data</h2>
-
-<p>Like I mentioned before, I do use FactoryGirl extensively. Maybe that means that <a href="http://blog.steveklabnik.com/posts/2012-07-14-why-i-don-t-like-factory_girl">my models are complicated</a>? I don't know, but for now, I am sort of using good sense and stuff. Let's see what happens. I do use <a href="http://faker.rubyforge.org">Faker</a> as well. Come to think about it, will do a post on that eventually.</p>
-
-<h2>Random issues in testing models</h2>
-
-<p>When everything seems to be absolutely correct in the models and in the tests and you are still getting unexplainable errors, do restart guard. I already spent several hours messing around with the tests because guard didn't refresh the models. <a href="http://stackoverflow.com/questions/5855587/spork-is-really-great-but-how-can-i-get-it-to-refresh-validations-and-other-cod">There are automatic solutions</a>, but I didn't feel like hacking away the spork/guard configurations again... yet.</p>
-
-<h2>Random resources</h2>
-
-<ul>
-<li><p><a href="http://everydayrails.com/2012/03/19/testing-series-rspec-models-factory-girl.html">Everyday Rails: How I learned to test my Rails applications, Part 3: Model specs</a> - this has a very nice overview of the actual steps required to test models. Now I just cheat using shoulda, but it's very nice and useful to get a</p></li>
-<li><p><a href="http://www.christopherbloom.com/2011/07/12/testing-model-validations-in-rspec-the-short-and-sweet-way/">Testing model validations in rspec the short and sweet way</a> - it's a bit outdated, but describes how to test validations in a short way.</p></li>
-</ul>
-
-
-<hr />
-
-<p>Again, as I already mentioned, I am a beginner to rails and, consequently, in testing rails, so I might be doing/saying terribly wrong things. Any input is very much appreciated :-)</p>
-]]></content>
- </entry>
-
</feed>
View
18 blog/categories/rails/index.html
@@ -98,6 +98,24 @@
<section class="archives"><h1 class="year">2013</h1>
<article>
+ <h2 class="title"><a href="/blog/2013/01/09/how-i-test-part-iv/">How I test part IV -- testing controllers in rails</a></h2>
+ <div class="meta">
+ <span class="date">Jan 9</span>
+ <span class="tags">
+
+
+ <a class='category' href='/blog/categories/controllers/'>controllers</a>, <a class='category' href='/blog/categories/factory-girl/'>factory_girl</a>, <a class='category' href='/blog/categories/rails/'>rails</a>, <a class='category' href='/blog/categories/rspec/'>rspec</a>, <a class='category' href='/blog/categories/testing/'>testing</a>
+
+
+</span>
+
+ </div>
+</article>
+
+
+
+
+<article>
<h2 class="title"><a href="/blog/2013/01/04/random-notes-on-factory-girl-part-ii/">Random notes on Factory Girl Part II</a></h2>
<div class="meta">
<span class="date">Jan 4</span>
View
2  blog/categories/raspberry/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: raspberry | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/raspberry/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
2  blog/categories/raspberrypi/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: raspberrypi | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/raspberrypi/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
View
233 blog/categories/rspec/atom.xml
@@ -4,7 +4,7 @@
<title><![CDATA[Category: rspec | Entreprevelopment adventures]]></title>
<link href="http://luisalima.github.com/blog/categories/rspec/atom.xml" rel="self"/>
<link href="http://luisalima.github.com/"/>
- <updated>2013-01-07T09:31:09+00:00</updated>
+ <updated>2013-01-09T16:02:42+00:00</updated>
<id>http://luisalima.github.com/</id>
<author>
<name><![CDATA[Luisa Lima]]></name>
@@ -14,6 +14,189 @@
<entry>
+ <title type="html"><![CDATA[How I test part IV -- testing controllers in rails]]></title>
+ <link href="http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv/"/>
+ <updated>2013-01-09T11:21:00+00:00</updated>
+ <id>http://luisalima.github.com/blog/2013/01/09/how-i-test-part-iv</id>
+ <content type="html"><![CDATA[<p>First and foremost, I advise you to <a href="http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html">read this post from Everyday Rails</a>. It gives a very nice overview, by controller method, of what controller tests should look like. Like Aaron, I had read about testing and noone advises you to test controllers -- rather, they would say "test models and do integration testing, and controllers will be covered by those tests". But as you can see in <a href="http://solnic.eu/2012/02/02/yes-you-should-write-controller-tests.html">"Yes, you should write controller tests!"</a>, controllers should be tested. I'll post their reasons here and add a few reasons extra why to test controllers:</p>
+
+<ul>
+<li>Controllers are models, too;</li>
+<li>Controller specs can be written and ran more quickly than integration tests;</li>
+<li>(My own reason #1) It's very easy to write them using the basic scaffholding provided;</li>
+<li>(My own reason #2) If they are not easy to write, it's because you are doing some nasty complex not-that-CRUDdy stuff. Uh oh. Which is what was happening in my case.</li>
+</ul>
+
+
+<p>But mainly, I don't see tests as testing instruments per se. I see tests as a way to reflect on your code, see how complex it is, and an opportunity for refactoring as well.</p>
+
+<p>That being said... What I did was to pick up the scaffholding and adapt it as needed, and here is the trouble that I ran into when testing controllers.</p>
+
+<p>First, run the scaffholding routing tests. Yeah, just go ahead and do it. And now correct them to account for all the nested resource paths that you have, as well as the paths that you don't want to allow. You are going to need it.</p>
+
+<p>Now open your scaffholding controller tests. Each example refers to a single method of the controller, and you have tests for each action that the method must take. If you run them and they don't behave as expected, there can be several reasons, but from my (very limited experience), here are the most common:</p>
+
+<ol>
+<li>you didn't set your valid_attributes properly -- including having nested attributes that are required and are not being set;</li>
+<li>you are using authentication and need a current_user (modifying valid_session);</li>
+<li>you have nested resources and so need to set the id(s) of the parent resource(s).</li>
+</ol>
+
+
+<p>Let's go through each of these in order...</p>
+
+<h2>Attributes</h2>
+
+<p>First and foremost, have you tested your factories in your models? Good.</p>
+
+<p>What I usually do is to make use of the <em>FactoryGirl attributes_for</em> method and just stick it in valid_attributes:</p>
+
+<pre><code>def valid_attributes
+ FactoryGirl.attributes_for(:my_model)
+end
+</code></pre>
+
+
+<p>However, this won't work when you have <em>validates_presence_of</em> for a has_one association. Say that my model has:</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+</code></pre>
+
+
+<p>Then, even if you have the association with the banana model in your "my_model" Factory, you'll get an error because attributes_for does not include associations and thus banana is not there. To fix that, we do:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Finally... if your model belongs_to another model, let's say that it looks like this (can you tell I'm hungry?):</p>
+
+<pre><code>
+ has_one :banana
+ validates_presence_of :banana
+ accepts_nested_attributes_for :banana
+ belongs_to :strawberry
+</code></pre>
+
+
+<p>Assuming that you set up this association correctly in FactoryGirl, you'll have a strawberry_id in your model's attributes. You have two options: either to set it up as an attr_accessible, or to get rid of it in the valid_attributes in order not to get a security error:</p>
+
+<pre><code>def valid_attributes
+ banana_attribs = FactoryGirl.attributes_for(:banana)
+ FactoryGirl.attributes_for(:my_model).except(:strawberry_id).merge({:banana_attributes => banana_attribs})
+end
+</code></pre>
+
+
+<p>Disclaimer: I was going to write a method go go through all the associations and do this automagically, but then I figured that it was kind of cheating because it is really valuable, like I said, to reflect on the way that you are writing your code. At least for me, it is.</p>
+
+<h2>Authentication and authorization -- you need valid session data and a current_user variable</h2>
+
+<p>If you are using any kind of authentication, you need to set up a valid current user and a valid session. Here's <a href="https://github.com/plataformatec/devise/wiki/How-To%3a-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29">how to do it if you are using Devise</a>:</p>
+
+<pre><code>before(:each) do
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+ user = FactoryGirl.create(:user)
+ user.add_role(:master_of_all_fruits) # don't forget that if you are doing authorization, you need to set up the correct role as well
+ user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
+ sign_in user
+end
+
+it "should have a current_user" do
+ subject.current_user.should_not be_nil
+end
+</code></pre>
+
+
+<p>Also, don't forget to set up the session:</p>
+
+<pre><code>def valid_session
+ {"warden.user.user.key" => session["warden.user.user.key"]}
+end
+</code></pre>