Joe Letizia

This is where I write my simple ramblings on martial arts, software, life, and other nonsense.

Jasmine vs RSpec Cheatsheet

20 Mar 2014

Jasmine and RSpec are both test frameworks for JavaScript and Ruby respectively. While working on a web project in a test driven environment, you're sure to run into both a lot; RSpec more so if you work on primarilly back end code, but a full stack developer is expected to know both.

Jasmine and RSpec have different ways of accomplishing essentially the same things, particularly mocks and stubs. This article is a simple example of those differences.

RSpec

RSpec accomplishes stubbing and mocking via two different but related methods, #stub and #double.

#double is used to create a mock object. This mock can be used as a directly passed collaborator to a method you are testing, or you can have this double be returned by a collaborator within the method you are testing.

A double is typically created in the following fashion

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:mock_object) { double(:mock_object) }

    it "this is my test" do
      ...
    end
  end
end

#stub is used to override the behavior of a method by replacing its definition and simply returning a predetermined value. This is useful when you want to simply assert that a method is called on a collaborator, or to have that collaborator return a planned value.

Generally, you can stub methods on mocks in order to guide your code under test down a specific path in order to test that it works properly.

#stub is used in the following fashion.

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:mock_object) { double(:mock_object) }

    before do 
      mock_object.stub(:method_1).and_return(5)
    end

    it "this is my test" do
      mock_object.method_1.should == 5
    end
  end
end

This can also be accomplished via:

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:mock_object) { double(:mock_object, method_1: 5) }

    it "this is my test" do
      mock_object.method_1.should == 5
    end
  end
end

The second example sets the return value of method_1 on mock_object to 5. Both the second example and the third example are relatively the same. It is a matter of preference.

As well as stubbing return values from methods on mocked collaborators, you can assert that a particular method was called on the double in the following way.

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:mock_object) { double(:mock_object) }

    it "this is my test" do
      mock_object.should_receive(:method_1)
      ClassUnderTest.new.method_to_test
    end
  end
end

One thing to note, the #should_receive method creates an implicit stub on the method. If this method is tested more than once, you would be well advised to do the following:

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:mock_object) { double(:mock_object) }

    before do
      mock_object.stub(:method_1)
    end

    it "this is my test" do
      ClassUnderTest.new.method_to_test
      mock_object.should have_received(:method_1)
    end
  end
end

Another nice scenario for stubbing is to stub conditional values to direct logic flow in your method under test. Take the following method for example:

class ClassUnderTest
  def initialize(checker)
    @checker = checker
  end

  def method_to_test
    if checker.do_something
      "it was truthy"
    else
      "it was falsy"
    end
  end
end

describe ClassUnderTest do
  describe "#method_to_test" do
    let(:checker) { double(:checker, do_something: do_something_value}

    context "when the checker returns true" do
      let(:do_something_value) { true }

      it "returns 'it was truthy'" do
        ClassUnderTest.new(checker).method_to_test.should == "it was truthy"
      end
    end

    context "when the checker returns false" do
      let(:do_something_value) { false }

      it "returns 'it was falsy'" do
        ClassUnderTest.new(checker).method_to_test.should == "it was falsy"
      end
    end
  end
end

Since we have stubed checker.do_something to return true or false in different contexts, we can verify the return value of the method method_to_test.

Jasmine

Jasmine accomplishes stubbing and mocking via similar means. The typical stub method in Jasmine is spyOn. spyOn can be called on any object to stub out methods on that object. The stubbed method will not execute the implementation code, which is one of the main points of stubbing. In the case below, an alert will not be raised since the method is stubbed.

describe("object-under-test", function() { 
  var spy, object;

  beforeEach(function(){
    object = {
      methodToTest: function(){ alert("hello"); }
    };

    spy = spyOn(object, "alert");
  });

  it("this is a test", function(){
    object.alert();
    expect(spy).toHaveBeenCalled();
  });
});

In order to create a mock, analogous to a double in RSpec, one would do the following:

describe("class-under-test", function() { 
  var mock, object;

  beforeEach(function(){
    object = {
      methodToTest: function(collaborator){ collaborator.methodToWatch("hello"); }
    };

    mock = jasmine.createSpyObj("nameOfSpy", ["methodToWatch"]);
  });

  it("this is a test", function(){
    object.methodToTest(mock);
    expect(mock.methodToWatch).toHaveBeenCalledWith("hello");
  });
});

This is similar and different from RSpec in a few ways.

  • Similarities

    • The method being stubbed does not call through to the original declaration of the method.
    • Injecting a mock into an object under test in both frameworks allows you to assert that a method was called on that mock.
  • Differences

    • In Jasmine, spyOn is called independent of the method you are stubbing, not directly on the object like in RSpec.
    • The expectation toHaveBeenCalled is checked after the invocation of the method. This has since been added in RSpec but legacy RSpecs are full of expectations prior to invocation of the method under test.