1. 40
  1.  

  2. 8

    I believe it was at RubyConf.new(2001) that Nathaniel Talbott demonstrated Lapidary, a powerful new unit testing library. Before that we were all using RubyUnit, another xunit clone. Lapidary became Test::Unit, which shipped with the Ruby interpreter. It was a conscious decision of the community that what would set us apart is we would adopt unit testing as a core value (at this time Rails did not exist, and we were trying to figure out what would be Ruby’s “killer app”).

    Nathaniel went on to work on Test::Unit version 2. One of the biggest drawbacks of Test::Unit is having to name your tests e.g. test_some_method_does_something; this has an effect on how programmers think about testing. Version 2 let you name them as strings, no more snake_case. But it was rspec’s innovative use of ‘it’ that really made the difference, not just turning underscores into spaces, but giving you pause to think about what the name means and what the test should do. In a way rspec is the fulfillment of the original vision of Lapidary: it finally made testing accessible to ordinary people, so test first becomes natural. BDD is a nice side effect.

    1. 6

      Throwing this out there - the distance you created can be reclaimed. The maintainers today would welcome fresh contributions!

      I also agree that Chelimsky was an incredible steward, maintainer and contributor. He set the course that made RSpec what it is today, and it’s dang good.

      It’s worth calling out that Myron Marsten, Jon Rowe, Penelope Phippen carried that forward into being rock solid, reliable, and dependable.

      What I would like to see going forward is some kind of bridge into property based testing. I don’t have a complete vision for what that might look like, but it would open up a world of possibility.

      1. 8

        Oh, I’m not sure I care to reclaim any distance. It’s a mostly good memory at this point. I have regrets, which I think I shared effectively, but I couldn’t be more proud of where it’s at today. And the current maintainers are fantastic.

        I tried picking up some tickets a few years back, and it didn’t feel like home anymore.

        I guess I like what they’ve done with the place, but all of my furniture is gone, so it doesn’t feel like home anymore. Maybe the future holds something different, though.

      2. 3

        I remember being confused and amazed the first time I came across Rspec years ago. After some years of using it as a black box I finally sat down to try to imagine how the nifty syntax even worked, and came up with this little gist.

        That said, now that I’ve moved onto Elixir, I actually much prefer the ExUnit syntax:

        describe "my_func/4" do
          test "it works as expected" do
            assert foo == blah
          end
        end
        

        It’s more minimal. When I use the expect(foo).toBe(...) syntax, I feel like I’m constantly having to look up the different matchers and what they mean. A simple matching assert is much clearer to me. It’s a bit of friction every time I have to work on our frontend jest tests.

        That said, I got my start with RSpec and it taught me to think in terms of “expectations”, and how it’s even possible to write tests before the code, which I still do from time to time. So it’s a testament to how much RSpec changed programming that what was revolutionary at the time and needed these syntactical guidelines is now taken for granted and just feels clunky to me.

        1. 4

          Matchers are not just syntactic sugar; there’s another reason they exist.

          Imagine writing a testing framework in Ruby where you can write assertions the way you suggest:

          assert foo == blah
          

          How do you get Ruby to print the values of foo and blah if the assertion fails?

          You can do it, by inspecting the AST or the bytecode of the calling method at runtime, but it’s really kludgy to do in Ruby, and the interpreter must withhold optimizations for it to work (e.g. foo and blah must be stored somewhere in the stack frame, not held in registers and then optimized away since they are no longer used). That’s why in Test::Unit (or minitest) we instead write:

          assert_equal blah, foo
          

          But now you have a new problem: any new type of comparison you might want to do needs a new assertion method, so we end up with assert_equal, assert_not_equal, assert_instance_of, assert_match, assert_same, and so on. And they all end up looking something like this:

          def assert_equal(expected, actual)
            msg = build_message(expected, actual) {
              "Expected #{expected} to equal #{actual}"
            }
            assert_block(msg) { expected == actual }
          end
          

          What matchers do is take away some of the boilerplate involved in writing a new assertion. So instead of the above, we could eliminate some of the duplication like this:

          def assertx(actual, matcher, *expected)
            msg = build_message(expected, actual) {
              "Expected #{actual} to #{matcher} #{expected}"
            }
            assert_block(msg) { @matchers[matcher].call(actual, *expected) }
          end
          
          def define_matcher(name, &block)
            @matchers[name] = block
          end
          
          define_matcher :equal { |expected, actual| expected == actual }
          

          then use it like this:

          assertx foo, :equal, blah
          

          Once you have that, it’s a few more steps to flip it around and make it read like Engilsh, which is how we end up with expect:

          expect(foo).to.equal(blah)
          
          1. 4

            Oooh, good point. I didn’t think of that. In Elixir assert is a macro, which I see now is how we get all the developer niceties of showing what was expected and given and all that. That plus asserting against a pattern match to select out map fields and the like makes it real slick and easy feeling. But I don’t think I had considered how much the underlying language contributes to what sort of testing is possible. Thanks, this was interesting!

        2. 2

          This was fascinating! I really enjoyed the ergonomics using RSpec when doing Ruby, so it’s great to learn some of the background on where it came from and why some of the decisions wer made.

          I always had problems with the word “specification.” I still do. To me, an elder of the Internet who implemented many things from RFCs and specifications, the word specification was a reserved word, that should not be used for new purposes. I thought we should find a new word.

          This is really interesting! I must admit as somebody who’s a big fan of specifications (both in prose or using formal verification tools), I’ve found this use of the term uncomfortable, but I can understand that’s easy to say with hindsight.

          1. [Comment removed by author]

            1. 4

              The author made it several times in the article that he prefers built-in/standard utilites.