🕷 zenspider.com

by ryan davis



sitemap
Looking for the Ruby Quickref?

Great Expectations

Published 2015-04-14 @ 12:00

Tagged minitest

minitest/spec has existed since v1.3.0 (6.5 years). It started as a simple proof of concept to show that there was a 1:1 mapping between “spec-style” and “unit-style” tests (not to be confused with BDD vs TDD, which is a development process). It showed (in 67 lines!), that every describe was just a Test subclass, and every it was just a test method (with strange inheritance).

minitest/spec has only grown to 152 lines since then but it really hasn’t changed all that much. Until yesterday.

The older spec system relied on a thread-local variable in order to understand what the current test method was. It was a limitation of how you called the expectation methods. So, when you wrote a test like this:

1
2
3
4
5
describe Something do
  it "should blah-blah" do                                      
    (1+1).must_equal 2
  end                                                                       
end

we call must_equal on the result of 1+1. This implies that the method is on Object in order to be called on the value 2. In order to map must_equal back to Minitest::Test’s assert_equal, minitest needed to know what test the expectation was in and that was done via Minitest::Spec.current, which just accesses a thread-local variable. That works great 99.9% of the time, until, you know, you use threads in your tests. (Why you would use threads inside your tests is irrelevant.)

So, if your test changed to this:

1
2
3
4
5
6
7
describe Something do
  it "should blah-blah threaded" do                                      
    my_threaded_test_thingy do
      (1+1).must_equal 2
    end
  end                                                                       
end

then it would blow up, because you’d be wrapping must_equal in your own thread and that would make the thread-local variable unavailable (they’re not scoped to parent threads, for better or worse).

Yesterday’s release of 5.6 brings new expect system from tenderlove that adds a struct and one new method _. The method wraps the resultant value in a Expectation value monad. All in all, it only adds 11 lines of code to minitest. All of the usual expectation methods are defined, but this time around, they know what test they’re in because it was stored off when the monad was created.

So, one character change later, and the following works:

1
2
3
4
5
6
7
describe Something do
  it "should blah-blah threaded" do                                      
    my_threaded_test_thingy do
      _(1+1).must_equal 2
    end
  end                                                                       
end

The method _ is also aliased to value if you prefer verbier tests. It is also aliased to expect, to make transitioning easier, but <bikeshed>1I really dislike that name</bikeshed> because it is non-descriptive. value is much better, and imo, _ is even better because it gets out of your way and lets you focus on the actual meat of the test.

At some point (pre-6.0) the old expectations are going to be deprecated in favor of the new system. Until then, they’re fine to use and won’t make any noise until the deprecation is officially planned. That will remove the monkeypatches on Object for all of the expectations that minitest supports. Hopefully that’ll reclaim the 11 lines we added.

Apologies are owed to Dickens. I couldn’t help myself, even tho I hated that book.

  1. <bikeshed> means: don’t argue with me about it. I don’t care to argue about it.