Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSpec terminology/syntax #14

Open
Integralist opened this issue Jan 17, 2014 · 3 comments
Open

RSpec terminology/syntax #14

Integralist opened this issue Jan 17, 2014 · 3 comments

Comments

@Integralist
Copy link
Contributor

Just having a quick re-read of "The RSpec Book" and it seems that some of the test code that caused me a bit of confusion might not have if a clearer syntax was used.

double(), stub() and mock() are all effectively the same (they all create an instance of a MockClass).

The reason they exist is to indicate the different intent (rather than using double all the time).

Example give in the book was...

describe Statement do
  it "logs a message on generate()" do
    customer = stub('customer')
    customer.stub(:name).and_return('Aslak')
    logger = mock('logger')
    statement = Statement.new(customer, logger)
    logger.should_receive(:log).with(/Statement generated for Aslak/)
    statement.generate
  end
end

We know the stub method should be used on a Stub and the should_receive should be used on a Mock.

Admittedly that has made the existing code become clearer. But I like the idea of code having clear intention through better terminology.

Thoughts?

@Integralist
Copy link
Contributor Author

On a related note I discovered (and you'll likely know this already) that chained methods can be stubbed all at once...

# example of chained methods
Article.recent.published.authored_by(params[:author_id])

Solution using RSpec's stub_chain...

article = double()
Article.stub_chain(:recent, :published, :authored_by).and_return(article)

...which replaces...

recent = double()
published = double()
authored_by = double()
article = double()
Article.stub(:recent).and_return(recent)
recent.stub(:published).and_return(published)
published.stub(:authored_by).and_return(article)

@Integralist
Copy link
Contributor Author

One other thing, it seems we can DRY up code by taking advantage of the fact that a Stub can be overridden temporarily by a message expectation.

For example...

describe Statement do
  before(:each) do
    @customer = double('customer')
    @logger = double('log', :log => nil)
    @statement = Statement.new(@customer, @logger)
  end

  it "uses the customer's name in the header" do
    @customer.should_receive(:name).and_return('Aslak')
    @statement.generate.should =~ /^Statement for Aslak/
  end

  it "logs a message on generate()" do
    @customer.stub(:name).and_return('Aslak')
    @logger.should_receive(:log).with(/Statement generated for Aslak/)
    @statement.generate
  end
end

By setting the log() stub on the @logger double in the before( ) block, we don’t need to set that stub in the first test.

In the second test, we override that stub with the expectation.

Once the expectation is met, any subsequent calls to log() are caught by the stub and essentially ignored.

@Integralist
Copy link
Contributor Author

Another thing, I just discovered (this was back in 2011 so I'm not sure if the RSpec has changed functionality since then) that you can use subject along with let to reduce code duplication: http://benscheirman.com/2011/05/dry-up-your-rspec-files-with-subject-let-blocks/

Wonder if it's worth doing some clean-up on our tests whilst keeping this technique in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant