I needed to write some shared examples in RSpec the other day for a simple ActiveRecord concern I was writing.
In doing so, it forced me to understand what RSpec means by the “subject” of an example. I’ll admit this is a very simple concept, but until now I didn’t take the time to understand it and the relish docs weren’t overly descriptive on how it works.
In general, the subject of an example is just the object being described. But what, specifically, does that mean?
In an implicit context, it means the subject is an instance of the object being described. For example, in a model spec, I might be describing a User class:
1 2 3 | |
Which can also be written as:
1 2 3 | |
Here, the subject is an instance of User. RSpec creates an instance of User by calling new on the User class without any arguments. It’s the same as saying this:
1 2 3 4 | |
That last example, is what RSpec calls an explicit subject. Instead of deducing the subject from the outer describe, an explicit subject is declared within the example and can be used afterwards the same way that an implicit subject would.
The key distinction for me, and one that I didn’t understand previously, was that the subject is an instance of the class being described (implicitly or explicitly).
This came up when I needed to test both an instance of a subject as well as a static class method of a subject’s parent class.
But how do I test a static class method against an instance?
Simple, just call class on the instance.
1 2 3 | |
It’s all very simple, but I just hadn’t grokked it until now. Fortunately, this realization came in handy when I was writing a simple shared example to test common functionality against multiple models.
In this case I wanted to test that a model had a specific instance method as well as some class-based scopes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
By using subject and passing in the model_type to generate some factories, I’m able to dynamically adjust to whatever model the shared example exists within at the time. Simple, dry, and efficient.