Organic Testing?

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?

Advertisement

2 thoughts on “Organic Testing?

  1. Providing your environment is loaded before your spec files are evaluated that’s easy in RSpec. e.g:

    describe "Song" do
      song  = Song.new(:title => "Dragonfly")
      title = Song.find(song.id).title
    
      it { expect(song.size).to eq 1 }
      it { expect(title).to eq "Dragonfly" }
      it { expect(Something.else).to eq 2 }
    end
    

    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.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s