This topic is for the [Simple Stubs] exercise in the [Test Doubles] trail. Post any questions, corrections, or pointers you have to share with other Upcase subscribers.
[Simple Stubs]: https://exercises.upcase.com/exercises/simple-stubs
[Test Doubles]: Test Doubles | RSpec Stub Online Tutorial by thoughtbot
Thereâs something about test doubles that makes me feel like a complete idiot. I hardly even know how best to formulate what Iâm not getting. But here goes:
Thereâs a bunch of duplicated data between our two tests: the creation of two posts from today and one from yesterday. I donât understand how creating a dummy object allows me to eliminate this redundancy. The array that my test is expecting ([first_today, last_today]) has to come from somewhere, and I donât understand from the examples preceding the exercise, or from the video, how Iâm supposed to use my double and its stubbed method to produce that array.
@MatthewMDavis using doubles is going to be a change from how youâve approached testing in the past, so thereâs no need to worry about it being unfamiliar.
Take a look at the instructions around stubbing the class method on Post
. Youâre going to want to stub out that behavior to return your double rather than allow Post to look for real records in the database.
Two of the advantages youâll get from testing with doubles and stubs will be that youâre going to be able to avoid touching the database (which will speed up your test execution considerably) and that your controller tests will be less brittle if you have subtle changes in the results that a change in the model (maybe .today
in the future doesnât end at midnight, but ends at sundown, etc.)
Hello all.
I am a little bit confused as to what extent we should use mocks and stubs in our tests. Considering that tests should verify if your methods are working properly; if I do something like that:
describe Dashboard do
describe "#posts" do
it "returns posts created today" do
posts = double("posts_created_today")
allow(Dashboard).to receive(:new).with(posts).and_return(posts)
allow(Dashboard).to receive(:posts).and_return(posts)
expect(Dashboard.posts).to eq(posts)
end
end
end
This test is going to be green even though I change the #posts method completely.
Iâm guessing that of course, we shouldnât mock or stub everything in tests. What would be a different approach for this particular test?
Thank you!
@aclapinpepin In general, you shouldnât stub methods of the object youâre testing, only on the objects it collaborates with.
The article Donât Stub the System Under Test provides a good explanation of this.
In this case, you could stub a method on the Post
class which returns todayâs posts.
Thank you @andyw8! This was a very enlightening article.
It seems like something is missing in the example examplified here: https://exercises.upcase.com/exercises/simple-stubs
For example, if I was to change the logic to only show the five most recent post, how would I change âallow(Post).to receive(:latest_published).and_return(posts)â?
Also the first example also doesnât seem to be the best as itâs showing every post that was created. Hence, even if you didnât yet include the logic to limit it to 10, it would still pass.
@geoffharcourt @Upcase any answer on this? I also donât understand how the refactor in the example is finding the 10 latest published posts - it seems like it is finding just the last published post?
Hi @sstgithub,
In this example, weâre using the controller unit test to verify that the correct model scope (.latest_published
) gets called. The double that gets returned represents the collection of posts that Post.latest_published
ordinarily returns. I would then verify the query from the scope a model spec for post. This way, if your scope incorporates some new behaviors that require testing against edge cases, you only need to update the model spec, and not both the controller and the model specs.
I try to test the logic at the point where itâs defined. You might later write an integration spec that avoided using any stubs or mocks that exercised the whole stack to confirm that the contract between Post
and PostController
was valid (the methods called in PostController
exist in Post
and return whatâs expected). Integration tests are a valuable part of your overall testing strategy, but they tend to be slower because they are sending requests through the webserver and interact with the database.
When we are starting the application and writing the spec for the controller we wonât have the Post class and how are we expecting Post.all to work?
Hi all,
I have a question since I felt confused about the exercise.
The question is Why the original code didnât test the method itself?
In this exercise, I think it asks us to test the instance method #posts in Dashboard Model. Buy why the original code didnât really test the method it self.
The original code is
expect(result.map(&:title)).to match_array(%w(first_today last_today))
But I think it should be like
expect(dashboard.posts) ...
Or did I miss or misunderstand something?
Thanks