12
votes

This should have an easy answer but I'm struggling to find it (have checked RSpec documentation, EverydayRails Testing with RSpec, Google results). In my model specs I like to include basic attribute specs as follows:

describe Foo do
  describe "basic attributes" do
    before { @foo = create(:foo) }
    subject { @foo }

    it { should be_valid }
    it { should respond_to(:color) }
    it { should respond_to(:height) }
    it { should respond_to(:some_other_attribute) }
    it { should respond_to(:you_get_the_idea) }
    ...

I like these specs because if there is some kind of error within my factory and/or model these specs help me pinpoint it quickly.

I've incorporated the expect syntax into all other specs and I like how it reads, but how to use it here? One option might be

expect(@foo).to respond_to(:color)

And another might be

expect(it).to respond_to(:color)

The former involves duplication that is avoided with the should syntax, but the latter looks strange to me (which could just be me).

I realize this question is more about style than functionality*, but we Ruby developers are conscientious about style, and I want to adhere to standard practices and have readable, idiomatic code. Any help is appreciated. Thanks.

UPDATE: Neither of my proposed options actually work, by the way. They both throw undefined method 'expect' errors. Now I'm really confused!

Having thought about the error, I realize it's because the should specs above are within one-line block. The confusion, then, is how can I write a single-line block with the expect syntax? In light of this update, the question is very much about functionality and I'll be excited to hear others' thoughts.

4/2015 UPDATE

rspec > 3.0 has added yet another way of handling these, and it sounds like rspec ~> 4.0 will do away with the should syntax. Per Myron Masters:

Some users have expressed confusion about how this should relates to the expect syntax and if you can continue using it. It will continue to be available in RSpec 3 (again, regardless of your syntax configuration), but we've also added an alternate API that is a bit more consistent with the expect syntax:

describe Post do
  it { is_expected.to allow_mass_assignment_of(:title) }
end

is_expected is defined very simply as expect(subject) and also supports negative expectations via is_expected.not_to matcher. [...]
In RSpec 3, we've kept the should syntax, and it is available by default, but you will get a deprecation warning if you use it without explicitly enabling it. This will pave the way for it being disabled by default (or potentially extracted into a seperate gem) in RSpec 4, while minimizing confusion for newcomers coming to RSpec via an old tutorial.

2
Can you share one of your specs which incorporated the expect syntax?Sun
These are pretty basic and are in the RSpec docs as well as in other sources. Here's a great blog post about the syntax: myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax. My examples essentially look like this, but they're in blocks (i.e. it "does something" do )...aceofbassgreg

2 Answers

16
votes

Myron Marston, one of the core RSpec committers, explains here that you should still use

it { should be_cool }

If you've disabled the should syntax, he offers a solution to alias expect_it to it:

RSpec.configure do |c|
  c.alias_example_to :expect_it
end

RSpec::Core::MemoizedHelpers.module_eval do
  alias to should
  alias to_not should_not
end

With this in place, you could write this as:

describe User do
  expect_it { to be_valid }
end
4
votes

I don't think there's a correct answer to this question, but I've been recently rewriting my test suites to move away from should and use the expect syntax exclusively, so I'll throw my two cents in. I decided to re-write pretty much because Myron Marston, the RSpec lead maintainer, wrote:

In the future, we plan to change the defaults so that only expect is available unless you explicitly enable should. We may do this as soon as RSpec 3.0, but we want to give users plenty of time to get acquianted with it.

He also commented:

We have no plans to ever remove "should"...but expect has fewer gotchas, and is the syntax I would recommend for new projects.

I do agree with Mark Rushakoff's answer as well, but personally I don't want to have to create those aliases just to keep a single it-block style syntax. So, using your example, where originally I wrote a model spec like your example in this form:

describe Foo do
  let(:foo) { create(:foo) }
  subject { foo }
  describe "model attributes" do
    it { should be_valid }
    it { should respond_to(:color) }
    it { should respond_to(:height) }
    it { should respond_to(:some_other_attribute) }
    it { should respond_to(:you_get_the_idea) }
  end
  # ...
end

I now would tend to write it like:

describe Foo do
  let(:foo) { create(:foo) }
  specify "model attributes" do
    expect(foo).to be_valid
    expect(foo).to respond_to(:color)
    expect(foo).to respond_to(:height)
    expect(foo).to respond_to(:some_other_attribute)
    expect(foo).to respond_to(:you_get_the_idea)
  end
  # ...
end

My opinion is that referencing foo directly in expect(foo).to respond_to(:color) is as much "duplication" as referencing a subject indirectly using it, so I'm not too phased about it, and I'm warming to the way that expect specs generally read. But, ultimately I think this comes down to just being an issue of preferred writing style.