This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/stubs-mocks-spies-and-fakes
Very useful and practical advice. Definitely one of my favourite episodes.
Signup.save method in the examples violate command-query separation? (It returns a boolean but also modifies
save method from ActiveRecord is both a command and query method. There isn’t a rule this is violating, but such methods are a little more difficult to implement, test, and understand, so it’s best to avoid combining commands/queries into a single method when there isn’t a really good reason to do so.
save method is a tricky one, because it does something that might fail. If
save were just a command method, you’d need to follow up by invoking a different query method (
persisted?). The authors of ActiveRecord decided the convenience of a single
save method was worth the complexity of a combined query/command method.
Do you guys generally try to avoid stubbing methods when you create doubles? For example, in the first set of test cases, the line
could be removed by stubbing
solution when the clone
double is created e.g.
double("clone", solution: solution)
Then when you use spies it ends up being the same number of lines as the “stub” solution, but all the expectations are at the end which is nice.
describe "#solution" do solution = double("solution") clone = double("clone", solution: solution) participation = Participation.new(clone: clone) result = participation.solution expect(clone).to have_received(:solution) expect(result).to eq(solution) end
Just wondering if it’s more of a best practice type convention that I haven’t picked up on yet. I’ve been loving this series and looking forward to new episodes every week, thank you!
I stuck with the
allow syntax in order to be consistent during the video. If I’m stubbing out a method that takes no arguments and returns a meaningful value (like an attribute), I’ll use the
Do you mean you would like more readable if used allow for stubbing?
double just double.
if want to stubbing attributes or methods, allow is good chose?
Another option I use a lot is the
spy method (introduced in RSpec 3.1) which means you don’t have to repeat what the name of the method you’re mocking:
clone = spy("clone") expect(clone).to have_received(:solution)
Something I’m still not fully grasping about using stubs or mocks. In most examples, a stub - or mock - is created that receives some method and returns some value. Then this stub is passed to an object, and then we test that this object received stubbed method or is equal to the passed value. Aren’t we just testing our setup?
No. You’re testing that the system under test is sending the right messages to its collaborators.
You should only be stubbing collaborators, not the class you’re testing.
You should only be stubbing collaborators, not the class you're testing.
That sentence finally allowed me to understand stubbing and mocking objects. I never quite grasped it, and now it makes perfect sense. (The video was a bit confusing for me because I didn’t understand the why before we dove into implementation details).
Oh I see. So a ‘collaborator’ that’s the class, who’s method is called by the class under test?
If I’m testing any class A, I refer to any other class that A uses as a collaborator.
When I’m testing A, I want to do so in isolation from its collaborators. One way to achieve this is by stubbing A’s collaborators.
Yeah, that makes sense. Thanks Ben.
It took me almost 1/2 the video to figure out that they were only stubbing collaborators. I wish I had read the comments before. Thanks Ben for clarifying.
I’ve been trying to implement something similar to the video’s first example in one of my own tests. Setup looks like this:
route = double("route") flight = Flight.new(route: route)
However, this raises the following exception:
 pry(#<RSpec::ExampleGroups::Flight::Duration>)> flight = Flight.new(route: route) ActiveRecord::AssociationTypeMismatch: Route(#70156849968700) expected, got RSpec::Mocks::Double(#70156836399260)
What I’m doing seems to be more or less the same as what is going on in the video with the
clone double and
Participation.new(clone: clone). Is it possible that I’m missing some option that would allow me to use a
double this way? I did some googling and found some people mentioning the
mock_model method, which is now deprecated, but that wasn’t used in the video to begin with.
Yeah, this is Rails being annoyingly class-focused.
In this case I’d probably use
Thanks! I’m guessing the classes used in the video aren’t ActiveRecord models?
The thing that @benorenstein mentioned about stubbing out only collaborators helped so much.
I was wondering in which use case you would prefer using something like Factory Girl ( or fixtures ) instead of mocks/stubs ?