Either I’m missing something or the current test frameworks [1] don’t support what I call an “organic test”.
[1] …which are all excellent and have helped me sooo much in the past, present and future!
The fact that I prefer MiniTest over Rspec doesn’t matter in this write-up, as I couldn’t find a way to do what I want in either framework.
What I Do.
I constantly find myself writing tests like this.
it do song = Song.new(:title => "Dragonfly") song.size.must_equal 1 song.title.must_equal "Dragonfly" Something.else.must_equal 2 end
Note that the actual tested code needs to be run before the assertions, but not before every assertion. It’s important to me to run it once, have the same result for every assertion and save time. The result is treated as immutable.
What I Should Do.
The three assertions are what I’d love to have in it {}
blocks to have a better overview how many assertions I broke. So, following “the rules” I’d end up with the following ugly code.
describe do before { @res = Song.new(:title => "Dragonfly") } let(:song) { @res } it { song.must_equal 1 } it { song.title.must_equal "Dragonfly" } it { Something.else.must_equal 2 } end
This just feels wrong. I hate before
blocks in general, and usually, I want to set up more variables so I’d have several let
lines. It’s not readable.
I can’t use subject
or let
or let!
. As you have already seen, the third line doesn’t call subject
and relies on the test state after before
.
What I Want.
My idea is to extend MiniTest/Rspec to support this syntax.
spec do song = Song.new(:title => "Dragonfly") title = Song.find(song.id).title it { song.size.must_equal 1 } it { title.must_equal "Dragonfly" } it { Something.else.must_equal 2 } end
That is super straight-forward: Setup your variables, and then test them with isolated it
blocks. How can I do that with existing frameworks?
Providing your environment is loaded before your spec files are evaluated that’s easy in RSpec. e.g:
But the reason why we don’t recommend it is you’re doing all your setup in the beginning of suite when the specs are evaluated, you’re also sharing state *everywhere* (the local vars are cleaned up outside the closure sure, but is your app environment? no. That’s why both RSpec and minitest provide hooks to perform this sort of setup in the test cycle.
If all you’re worried about is performance then you can use a before(:context) hook to execute the instantiation once. You’d have to use ivars in RSpec for this to work, let’s are specifically disallowed for that purpose because we advocate not sharing state between your specs, it causes more harm than good.
LikeLike
Look at RSpec-Given, especially the “And” syntax.
LikeLike