Isolated controller and view testing in merb 2

Posted by Tim Connor Mon, 17 Dec 2007 08:41:00 GMT

As part of learning rspec for blerb, I’m working on better mocking for truer isolation tests. This includes true unit tests for models, controllers, and views, unlike the rails/testunit standard approach. This involves, primarily, mocking out the models and turning rendering off for the controller testing, and figuring out how to test just the rendering without the rest of the stack, for the views. I won’t claim any of this is well organized, or finalized, as we are just figuring out how to do some of it in the first place, but feel free to crib any of this you like off the git-web for blerb.

First a controller


describe “Articles Controller”, “index action” do
before(:each) do
article1 = mock("article") @article2 = mock("article") @articles = [article1, article2] Article.should_receive(:all).and_return(articles)
@controller = Articles.build(fake_request)
@controller.stub!(:render)
@controller.dispatch(‘index’)
end

it “should get successfully” do @controller.status.should == 200 end it “should assign all articles” do controller.instance_variable_get("articles").should == @articles end

end

The one non-intuitive part (for me), was making sure to stub render – otherwise you’ll be calling the view, and getting errors on your mocks because the view is calling methods you didn’t mock/stub out. Thus, it is key to isolate the controller from the model and the view if you want to easily test it, in this style. Oh yeah, and knowing about fake_request as part of the merb testing help.

The slightly trickier part was doing the same for the views. I dug through the merb source, and got most of the way, but hadn’t figured out how to pass in assigned instance variables by creating a dummy ViewContext, when ezra gave me a hand. I would NOT (and will not long term) do it exactly like this, but rather extract some sort of spec_helper to wrap the fake view_context creation and template engine calls, but it’s useful as is for an illustrative example.

class FakeArticleController
  def initialize(articles)
    @articles = articles
  end
end

describe "/articles" do
  before(:each) do
    @article1 = mock("article")
    @article2 = mock("article")
    @article1.should_receive(:title).and_return("Article 1")
    @article2.should_receive(:title).and_return("Article 2")    
    Article.stub!(:all).and_return([@article1,@article2])

    @template = "#{MERB_VIEW_ROOT}/articles/index.html.erb"
    @engine = Merb::Template.engine_for(@template)
    @fake = Merb::ViewContext.new FakeArticleController.new(Article.all)
    @body = @engine.transform(:file => @template, :view_context => @fake)
  end
  
  it "should list all articles" do
    @body.should include("<li>Article 1</li>")
    @body.should include("<li>Article 2</li>")
  end
end
Comments

Leave a comment

  1. Avatar
    Tim Connor about 10 hours later:

    Here is the quick first run at extracting a helper for it. I’ll blog about it when I come-up with something more official.


    @body = fake_render ‘articles/index.html.erb’, :articles => Article.all

    def fake_render(template, ivars={})
    dummy = Object.new
    ivars.each do |key, value|
    dummy.instance_variable_set “@#{key.to_s}”, value
    end
    template = “#{MERB_VIEW_ROOT}/#{template}”
    engine = Merb::Template.engine_for(template)
    view_context = Merb::ViewContext.new dummy
    engine.transform(:file => template, :view_context => view_context)
    end

  2. Avatar
    meekish 3 months later:

    Are you still using this method, or has it been deprecated by changes to the framework?

Comments