简体   繁体   中英

Stubbing a costly method with Rspec

In the below case, I just want to pass a hash to the stubbed test double. I do not want to test the double per se. But I need to mimic it in my test. The actual method looks something like this:

# user.rb
def do_work
  assignments = fetch_assignments
  if assignments.any?
    assignments.each do |assignment|
      if assignment.valid?
        client.approve_assignment({assignment_id: assignment.assignment_id})
      else
        client.reject_assignment({assignment_id: assignment.assignment_id})
      end
    end 

    mark_assignments_as_complete    
  end
end

Now I can test all the functionality easily, except the client. The client is a real expensive (and cost money per query) API query that I do not want to run every time I run test. So I use test doubles and stubs:

describe "#do_work" do
  context "when two assignments are valid" do
    before :context do
      @user = User.create!({name: "Bob"})
      [{assignment_id: "1", valid: false}, {assignment_id: 2, valid: false}, {assignment_id: "3", valid: true}].each do |assignment|
        @user.assignments.create!(assignment)
      end
      @client = ExpensiveAPI.new
      @user.client = @client
    end
  end

  describe "#do_work" do
    it "accepts valid assignments" do
      user.valid_assignments.each do |assignment|
        allow(@client).to receive(:accept_assignment, {assignment_id: assignment.assignment_id}).and_return(true)
      end

      expect(@user.do_work).to have 2_completed_assignments
    end

    it "rejects invalid assignments" do
      user.valid_assignments.each do |assignment|
        allow(@client).to receive(:accept_assignment, {assignment_id: assignment.assignment_id}).and_return(true)
      end

      expect(@user.do_work).to have 1_incomplete_assignment
    end
  end
end

When I try to pass an argument into the stubbed method, I get this error:

ArgumentError: wrong number of arguments (2 for 1)

I think this is a syntax issue. The format for stubbing a method with arguments (as per https://relishapp.com/rspec/rspec-mocks/v/2-99/docs/method-stubs ) is:

allow(obj).to receive(:message).with('an argument') { ... }

But you are passing your expected arguments as a secondary argument to receive(...) which is why it's complaining that it only takes 1 argument.

Change your spec to be this:

describe "#do_work" do
  context "when two assignments are valid" do
    before :context do
      @user = User.create!({name: "Bob"})
      [{assignment_id: "1", valid: false}, {assignment_id: 2, valid: false}, {assignment_id: "3", valid: true}].each do |assignment|
        @user.assignments.create!(assignment)
      end
      @client = ExpensiveAPI.new
      @user.client = @client
    end
  end

  describe "#do_work" do
    it "accepts valid assignments" do
      user.valid_assignments.each do |assignment|
        allow(@client).to receive(:accept_assignment).with({assignment_id: assignment.assignment_id}).and_return(true)
      end

      expect(@user.do_work).to have 2_completed_assignments
    end

    it "rejects invalid assignments" do
      user.valid_assignments.each do |assignment|
        allow(@client).to receive(:accept_assignment).with({assignment_id: assignment.assignment_id}).and_return(true)
      end

      expect(@user.do_work).to have 1_incomplete_assignment
    end
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM