feel like I can write them in my sleep (as long as they don’t involve async React events 😅).
RSpec tests that I’m starting to form opinions about what I think they should look like.
that something’s missing in my Jest tests! My experiences with RSpec have me longing for two features in Jest:
in an RSpec test is, as I understand it, literally the same thing as a
describe block. Like it’s just an alias of
describe. What’s the point, you ask?
The difference is in, well, context. Well-organized RSpec tests use
describe to describe what’s being
context to describe scenarios of the thing being tested.
For example, if I wanted to test the
multiply method of a
Calculator class, I might write some test scenarios
that look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
See the difference in those test cases between
context? The way I think about it is: if the
statement coming after my
context describes a pre-condition for the test, it’s a
be an alias of
let blocks are used in an RSpec
test to set things up for your test scenario.
Here’s a test for a
Counter class, verifying that when I call the
increment method on an instance, its stored
1 2 3 4 5 6 7 8 9 10 11 12
let statement in RSpec creates a method with a specified name, which lazily evaluates to the result of a
block. In this case, we get a method named
counter, which is evaluated to a new instance of the
Counter class. There are a few important things to note about
counterdoesn’t actually get created until I reference it.
describe "counter"block, I’m getting the same
letblock deeper inside the tree of tests, by declaring another
let(:counter)later. When I
letblock in the tree for that thing is the one that gets used.
I don’t think it’s possible to implement
Ruby meta-programming to intercept calls to missing methods,
close, but it relies on string keys to define things, and there’s a bit of extra work when working with TypeScript.
On the surface these two features don’t seem like much, but they provide a really powerful framework for organizing
test cases and the associated test setup.
let blocks being lazily evaluated, and override-able, I can set up data at the exact level of tests that
I need it. When I need to override it for a certain set of tests, I can put another
let block in that set. In
beforeEach blocks, but all that can get pretty noisy.
context blocks, I can more clearly lay out the scenarios of my tests. Yes, I could just do this with
personally seen/written too many tests to count named something like
it("returns false when the flag is enabled, they're located in the US, but they have brown hair."). That’s three
scenarios rolled into one test name. It works, but being able to nest different
context blocks to define my
complex scenarios is much easier to read.
Here’s an example of some tests I could write in RSpec with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
HOW COOL IS THAT! Every
context block has exactly the setup data it needs defined clearly inside it.
And each block has very little noise to distract you.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
There’s definitely a bit more noise here, especially with the
let statements. It’s not a lot
more noise, but it is definitely more noise.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
You could certainly make the case for this contrived example that this is actually the most readable set of
tests, because there’s less code. I would have a hard time arguing. But most real-life tests are more complex than
these examples, with state and side-effects to mock out, and more scenarios and edge cases worth testing. Each test
case here includes multiple conditions, but there are only two permutations represented. Once things get a little
more complicated than these contrived examples, the RSpec tests become the clear winner for me — they’re easier to
read and manage, with their
context blocks more discretely describing your test scenarios.
You could also argue that the bigger win here would be breaking scenarios into individual
describe blocks in
it("...") statement. I wouldn’t argue
The day after I wrote this article, a conversation started in the Artsy slack about how confusing
let was because
it moved variable initializations far away from where the tests used them.
This makes sense! I think it points to two truths in software development:
For years I was convinced that practices like small functions or long and descriptive function names were
objectively more readable. I leaned into this, and my code reviews almost always included comments on what I
thought would make the code more readable.
As more people pushed back on my feedback over time, I realized that the feedback I was giving was subjective. I
still like code that uses many short functions wired together, but not everyone finds that more readable! I’ve
stopped giving readability feedback on PRs, unless I can provide nearly-objective facts or scenarios that point to
a readability improvement.
In this article, I find the RSpec
your team might not! Maybe the distance between a
let block’s definition and its method’s usage makes it hard for
you to follow the test. That’s cool!
Earlier in this article I linked to an article that describes
let blocks in more detail. It includes
a warning from the actual
letcan enhance readability when used sparingly (1,2, or maybe 3 declarations) in any given example
group, but that can quickly degrade with overuse. YMMV.
I’ve definitely seen code where I had a hard time following a stream of
let blocks. The RSpec example I gave
above reads nicely to me — but it’s probably teetering on the edge of where
let usage becomes confusing. I’m
guessing I have a slightly higher tolerance for this particular abstraction than my friends who don’t like
it…again pointing to readability being subjective.
recreate that RSpec example above. It represents exactly how I want to think about complex test scenarios. Each
level of the tests has exactly the setup that is unique to that level. There’s very little distraction or noise at
it. It totally aligns with
my desire to minimize irrelevant test setup.
I’m in ❤️ ❤️ ❤️ ❤️ ❤️.
This post originally appeared on