I was looking at a spec https://github.com/JumpstartLab/donors_choose/blob/50e4f46ba01c3565663396ccb35fd1fda3fb6772/spec/donors_choose_spec.rb and was going to make a comment about how I prefer mock objects than dynamic mocks (b/c this test doesn't fail if the dependencies change). So I went to write my own version, and realized that it is very difficult.
What I want to do is inject the mock version of Donors::Request into the DonorsChoose class. So I was thinking give it a class accessor for
requester and then set it to the mock class.
Two problems here:
- Either the DonorsChoose::Request has a Mock::DonorsChoose::Request class that mirrors it and can handle expectations, or it is an instance of a class that can do this. If it is a class itself, then the mock is a singleton, so this one object needs to carry state that will change across different tests. If I make it an instance, then it is just confusing (the mock class can't stand in for the real class, but instead an instance of it stands in for the class... but wait, then what stands in for instances?)
- Either way, tests can't run in parallel because two different tests could set the requester variable to be their own mocked object, which could potentially clobber each other. Of course, it looks like the dynamic mocks should have the same issue. (Maybe make the variable be thread local?)
So I'm thinking about these things, and realize the problem is that I want to put the class into different states (its variables point to different objects) for different tests. But this is difficult because classes are unique, they are objects, sure, but they are singletons, every time I talk about that class, I'm talking about the same object.
Okay, so putting behaviour on classes sucks. But then where do I put it? Take a blog as an example. I want to find all the posts. Maybe I have a
Post.all method, I want to rig it to return some value for this test, and some other value for some other test. Do I instead make a subclass for finders that I can then instantiate, something like
Post::Finders.new.all ? But this doesn't solve the issue, we still get the new method being called on the singleton Post::Finders (Instead of being stuck trying to mock out the .all method, I'm stuck mocking the .new method).
I've got several other ideas, but they're all pretty extreme and highly hypothetical.
So, anyway, classes are singleton objects. That sucks, but what is there to do about it?