JSBach
JSBach

Reputation: 4747

How to set a default value for a method call in a instance with minitest?

I am learning ruby and TDD. I am using Minitest as my unit test framework. Currently I have a class (pair) that will be initialized with two instances of a second class (participant). To unit test it, I would like to create two mocks of participants and pass them to the initialize method:

def test_init
    participantOne = Minitest::Mock.new
    participantOne.expect(:name, "P1")
    participantOne.expect(:dept, "D1")

    participantTwo = Minitest::Mock.new
    participantTwo.expect(:name, "P2")
    participantTwo.expect(:dept, "D2")

    pair = Pair.new(participantOne, participantTwo)

    assert_equal("P1", pair.participantOne.name) #I would like to change this line
    assert_equal("P2", pair.participantTwo.name) # and this line


end

This works, but it is not 100% correct. If I change the assert equal line to

assert_equal(participantOne.name, pair.participantOne.name) #I would like to change this line

To compare the value of the stored participant with the value of the mock (which I would like to store), I get

MockExpectationError: No more expects available for :name: []

To be honest, I don't really understand it. It seems that the "expect" I set before was already "used" during the execution of my code.

This is not exactly what I am looking for. I do not want to verify how many times a method is called, I would like to define a default value to be returned when the method is called in a given instance.

How can I do that using minitest?

I made some search and I read about stubs in Minitest. But as far as I understood they are meant to stub a method call for every instance of a class.

I would like to create two participants and define their name and dept information, without using the real class, so I can focus my test in the subject under test.

ps: this is the init in my pair class

def initialize(participantOne, participantTwo)
    @participantOne = participantOne
    @participantTwo = participantTwo
end

and participant class

def initialize(name, dept)
    @name=name
    @dept=dept
end

Upvotes: 3

Views: 781

Answers (1)

joelparkerhenderson
joelparkerhenderson

Reputation: 35483

You're close to correct.

Minitest provides mocks and stubs:

  • Use a mock when you want to verify which method calls will be called, and how many times.

  • Use a stub when you want a placeholder, such as an object with a method that always returns the same value.

What you're trying to test first is if your Pair.new sets the objects correctly.

You don't need a mock, and you don't need a stub; you can use a plain old Ruby object.

p1 = Object.new
p2 = Object.new
pair = Pair.new(p1, p2)
assert_equal(p1, pair.participant1)
assert_equal(p2, pair.participant2)

Suppose you decide to add some complex Pair initialization code, and then you want to test that the initialization code doesn't accidentally change the participants' names and descriptions.

You don't need mocks, and you don't need stubs; you can use plain old Ruby objects.

p1name = "p1n"
p1desc = "p1d"
p1 = Participant.new(p1name, p1desc)

p2name = "p2n"
p2desc = "p2d"
p2 = Participant.new(p2name, p2desc)

pair = Pair.new(p1, p2)

assert_equal(p1name, pair.participantOne.name)
assert_equal(p1desc, pair.participantOne.desc)
assert_equal(p2name, pair.participantTwo.name)
assert_equal(p2desc, pair.participantTwo.desc)

All of the above are plain old Ruby objects, not mocks nor stubs.

The reason you would switch from plain old Ruby objects to mocks or stubs is if you want to explicitly test which methods are called (so use a mock) or the setup is more complex, or time consuming (so use a stub).

Upvotes: 3

Related Questions