Wednesday, April 6, 2011

Testing methods called on yielded object

I have the following controller test case:

def test_showplain
  Cleaner.expect(:parse).with(@somecontent)
  Cleaner.any_instance.stubs(:plainversion).returns(@returnvalue)

  post :showplain, {:content => @somecontent}

end

This works fine, except that I want the "stubs(:plainversion)" to be an "expects(:plainversion)".

Here's the controller code:

def showplain
   Cleaner.parse(params[:content]) do | cleaner |
      @output = cleaner.plainversion
   end
end

And the Cleaner is simply:

class Cleaner

   ### other code and methods ###

   def self.parse(@content) 
     cleaner = Cleaner.new(@content)
     yield cleaner
     cleaner.close
   end

   def plainversion
      ### operate on @content and return ###
   end

end

Again, I can't figure out how to reliably test the "cleaner" that is made available from the "parse" method. Any suggestions?

From stackoverflow
  • This is a little tricky. The easiest a approach will be to break the problem into two pieces: the testing of the controller and the testing of the controller.

    You have the testing of the controller set-- just remove your expectation around plainversion call.

    Then, separately, you want to test the Cleaner.parse method.

    cleaner = Cleaner.new('x');
    cleaner.expects :close
    
    Cleaner.expects(:new).returns(cleaner)
    
    called_with = nil
    Cleaner.parse('test') do |p|
      called_with = p
    end
    assert_equal p, cleaner
    

    That is not very clear what's going on. Makes me think that there is a simpler version of this. Can cleaner just be a simple function that takes a string and returns another one? Skip all the yielding and variable scoping? That will be much easier to test.

    Todd R : The nice thing about the parse method is that it gives a Cleaner that is completely ready to use, and ensures close is called. This is fully tested in the Cleaner class. Even though the above code show using the Cleaner to retrieve "plainvesion", there are other operations that could be done with a fully prepared Cleaner. In the Controller, we could create a new Cleaner, make sure it is fully inited, use it, then make sure we close it. But that just seems so . . . not Rubyish. Especially when it's so convenient to use parse, use the Cleaner, and be done with it.
    ndp : Maybe if you just take the @output out of the block and pass it back to the caller then your controller method will be easier to test-- just test that the cleaner was called and you're done. As you say, your cleaner is tested elsewhere.

0 comments:

Post a Comment