Skip to content

Instantly share code, notes, and snippets.

@ryanb
Created December 6, 2012 01:04
Show Gist options
  • Save ryanb/4221051 to your computer and use it in GitHub Desktop.
Save ryanb/4221051 to your computer and use it in GitHub Desktop.
Alternative expectation interface for MiniTest and RSpec

Expectations

I took the ideas presented here and built a gem called Mustard. Check it out!

There are several expectation/assertion interfaces available for writing tests/specs. Here are some issues I have with them.

Test::Unit/MiniTest

  • The order of assert_equals feels backwards
  • Oh wait, that should be assert_equal (that too)
  • I like to write the subject first

MiniTest::Spec

  • Adds 30+ methods to Object
  • Any matchers you want to add are another method on Object
  • Still too few matchers, so it's ugly: foo.must_be :<, 10

Bacon

  • too.many.method.calls.to.simulate.english

RSpec Should

  • The space makes for some awkward syntaxes that cause warnings (when used with ==, >, etc.)
  • eq() and such aren't bad, but may need parenthesis
  • Pollutes the spec context with methods for each matcher

I haven't used the RSpec expect() interface enough to know cons.

Proposed Solution

# should returns an object which can have matchers called on it
5.should.eq 5
5.should.equal 5

# should_not can be used to do a reverse match
5.should_not.equal 7

# some comparison matchers
5.should.be_less_than 8
5.should.be_greater_than 3
5.should.be_lt 8
5.should.be_gt 3
5.should.be_gte 5
true.should.be_true
false.should.be_false

# call the method and see if it is true
[].should.be :empty?
5.should.be :between?, 3, 7

# add matchers
Should.matcher :be_empty do # any args will be passed into block
  empty? # runs in the context of the object and returns true/false
end
[].should.be_empty
[].should_not.be_empty # message: Expected [] to not be empty

# add matcher through class
Should.matcher :be_empty, BeEmptyMatcher
class BeEmptyMatcher
  # methods in here for defining behavior and messages.
end

I like this because it does not pollute the Object space much (just shouldand should_not). Also every method called after it is simply a matcher. No crazy method chains.

What do you think? If there's enough interest I'll write this up as a plugin for both RSpec-Core and MiniTest.

@gshutler
Copy link

gshutler commented Dec 6, 2012

I mentioned this on Twitter this morning but thought I would be worth putting my thoughts here too (and over more than 140 chars).


I played around with similar earlier this year in https://github.com/gshutler/mustard. I never published it but it seems I had similar thoughts.

Things that motivated me:

Lack of pollution

Mustard extends Object with must and must_not, everything else hangs off the Mustard proxy.

Intuitive

RSpec and similar matchers have often tripped me up as I couldn't remember which way around spaces and statements needed to be, when parantheses became mandatory, how the be_ statement was written, etc.

Mustard works with regular boolean expressions which you would use with Ruby so there's no new syntax to learn. If your regular boolean statement is foo.vaguely_sensible? :bar then foo.must.vaguely_sensible? :bar is a working assertion.

Readability != "Is English"

We're developers, we read code for a living. I know that <= means less than or equal to so why no just use that rather than added another set of vocabulary to learn/forget?

More assertive verb

Referencing RFC 2119 SHOULD has weak connotations whereas MUST is assertive. Given that tests fail when your expectations aren't met must seems a better fit.


All that being said I was doing it more as a thought experiment rather than a serious effort to create an alternative and I've continued to use RSpec matchers day-to-day. However, if there would be wider interest in refining this (I stopped at the point where there were diminishing returns between effort and interest) I'd be happy to pick it up again.

@ryanb
Copy link
Author

ryanb commented Dec 6, 2012

I went ahead and coded my ideas out into a gem. You can find it at:

https://github.com/ryanb/musts

For me it strikes a nice balance of simplicity and utility. Feel free to use it if you like it too. If you don't, you don't have to use it. :)

@ryanb
Copy link
Author

ryanb commented Dec 6, 2012

Renamed the project to Mustard. It can be found here:

https://github.com/ryanb/mustard

@mattconnolly
Copy link

I would prefer:

[].must.be_empty

I think is better than be :empty? What if something was supposed to be a symbol and you are explicitly looking for the symbol :empty? ??

@jonah-williams
Copy link

If you're still considering interfaces have you looked at https://github.com/sconover/wrong ?

@hopsoft
Copy link

hopsoft commented Dec 7, 2012

I have similar objections to the test frameworks you call out. Unfortunately I don't care for expectations either. Here's my attempt to answer the problem. MicroTest

@aptinio
Copy link

aptinio commented Dec 8, 2012

+1 on checking Wrong out (https://github.com/sconover/wrong). I think it addresses a lot of the issues you listed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment