Search code examples
rubyunit-testingrspecmockingrr

Ruby Unit testing techniques, Mocking and Stubbing


I've been recruited as a SW Dev and I'm trying to get into Unit Testing with RSPEC and RR in ruby but having an hard time deciding on a specific strategy mainly because I was assigned to write unit tests to a code which is already written.

Consider the following code which is a part of a big method called method1:

  if (["5234541252", "6236253223"].include?(self.id))
    self.DoCheck
    logs.add 'Doing check', "id = #{self.id}"
    return
  end

The relevant unit test for this part of the method would've been something like:

"should do check only if id = 5234541252 or 6236253223"

But i'm running into a few questions, basically involving best practices, for example:

How can I check if DoCheck has been called from within "method1" using RR and RSPEC?

I've tried using dont_allow(Object).DoCheck but it won't work.

describe :do_route do
  it "should do check only if id = 5234541252 or 6236253223" do
  user = Factory(:User)
  user.id = "5234541252"

  dont_allow(user).DoCheck
  user.method1
end

Is there any other way to conclude if "DoCheck" was called or not?


Solution

  • You can use 'Partial Mocking' technique on your object under tests. This means you need to set expectation that user.method1 sends DoCheck message to the user. RSpec can set this expectation with should_receive() method. The test will fail if expectation won't be met.

    Try the following code:

    describe :do_route do
      it "should do check if id = 5234541252 or 6236253223" do
        user = Factory(:User)
        user.id = "5234541252"
    
        user.should_receive(:DoCheck) # set expectation that DoCheck will be called
    
        user.method1  # execute
      end
    end
    

    With RR this expectation may look like this:

    describe :do_route do
      it "should do check if id = 5234541252 or 6236253223" do
        user = Factory(:User)
        user.id = "5234541252"
    
        mock(user).DoCheck # set expectation that DoCheck will be called
    
        user.method1  # execute
      end
    end
    

    Update

    But be careful with usage of mock objects and partial mocks because they make your test code tightly coupled to the current behavior of the code under tests. If you are going to refactor this code in the future you will possibly get a lot of failing tests (those that use mocks). Try testing code without mocks (input-output) and use mocks only if they clearly explain intent of the class under test. When you write test with mock always ask yourself do you really need to assert that this method calls another method or what you really care is the output of the method or something else.

    Hope this helps!