rwehresmann
rwehresmann

Reputation: 1168

How to test with rspec if a method was called inside another method?

I have my method create:

module AnswerCreator
  class Creator
    def initialize(answer)
      @answer = answer
    end

    def create
      test_cases_results = run_test_cases
      @answer.attempt = get_attempt_number
      @answer.correct = correct?(test_cases_results)

      ActiveRecord::Base.transaction do
        @answer.save!
        save_test_cases_results(test_cases_results)
        Scorer::Changer.new(@answer).change
      end

      ComputeAnswerSimilarityJob.perform_later(@answer)
    end

    ...

  end
end

And I have the following test to it:

  it "creates the answer with the right attempt number, create the test case results and add the answer to be processed in background" do
        answer_test_case_result_count = AnswerTestCaseResult.count
        jobs_count = enqueued_jobs.size

        user = create(:user)
        team = create(:team)
        question = create(:question)
        create_pair(:test_case, question: question)
        create(:answer, question: question, user: user, team: team)
        answer = build(:answer, question: question, user: user, team: team)

        # These answers must be ignored >>
        create(:answer, question: question, user: user)
        create(:answer, question: question, team: team)
        create(:answer, user: user, team: team)

        described_class.new(answer).create

        expect(answer.new_record?).to be_falsey
        expect(answer.attempt).to eq 2
        expect(AnswerTestCaseResult.count).to eq answer_test_case_result_count + 2
        expect(enqueued_jobs.size).to eq jobs_count + 1
      end

This Creator class deals with the creation logic when an Answer object is created. My Changer class deals with the score logic associated to an Answer object. I don't want to test this logic inside the Creator, but I would like to ensure that the change method from Changer is called.

How can I test this?

Upvotes: 0

Views: 961

Answers (1)

max pleaner
max pleaner

Reputation: 26758

So you have this line of code you want to test:

    Scorer::Changer.new(@answer).change

My initial thought is to recommend allow_any_instance_of, but as you see in that link, the RSpec team advises against it. If you wanted to, though, you could putting this inside the test case, before calling Creator#create:

expect_any_instance_of(Scorer::Changer).to receive(:change).and_call_original

Withoug expect_any_instance_of, to the best of my knowledge you'd have to stub the constructor:

fake_answer = "???" # change to the value of 'answer' in your test case
fake_changer = Scorer::Changer.new fake_answer
expect(Scorer::Changer).to receive(:new).and_return fake_changer
expect(fake_changer).to receive(:change).and_call_original

This sort of thing is a little tedious, but is what happens when functions have a lot going on in terms of private state. Like, for example, what if you moved the Scorer::Changer.new call to Creator#initialize?

attr_reader :changer
def initialize(answer)
  @answer = answer
  @changer = Scorer::Changer.new @answer
end

Then you need to do a little less stubbing:

fake_answer = "???"
creator = AnswerCreator::Creator.new fake_answer
expect(creator.changer).to receive(:change).and_call_original
creator.create

Upvotes: 2

Related Questions