Skip to content

Commit

Permalink
Add is_expected for expect-based one-liner syntax.
Browse files Browse the repository at this point in the history
  • Loading branch information
myronmarston committed Nov 14, 2013
1 parent d2ef379 commit 25590f5
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
### 3.0.0.beta2 Development
[full changelog](http://github.com/rspec/rspec-core/compare/v3.0.0.beta1...master)

Enhancements:

* Add `is_expected` for one-liners that read will with the
`expect`-based syntax. `is_expected` is simply defined as
`expect(subject)` and can be used in an expression like:
`it { is_expected.to read_well }`. (Myron Marston)

Bug Fixes:

* Fix failure (undefined method `path`) in end-of-run summary
Expand Down
4 changes: 2 additions & 2 deletions features/command_line/line_number_option.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ Feature: --line_number option
describe 9 do
it { should be > 8 }
it { is_expected.to be > 8 }
it { should be < 10 }
it { is_expected.not_to be < 10 }
end
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Feature: configure expectation framework
end
describe 6 do
it { should be_a_multiple_of(3) }
it { is_expected.to be_a_multiple_of 3 }
end
"""
When I run `rspec example_spec.rb`
Expand Down
2 changes: 1 addition & 1 deletion features/subject/explicit_subject.feature
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Feature: explicit subject
context "with index out of bounds" do
before { expect(Array).to receive(:one_two_three).once.and_return([1,2,3]) }
subject { Array.one_two_three[42] }
it { should be_nil }
it { is_expected.to be_nil }
end
end
end
Expand Down
29 changes: 0 additions & 29 deletions features/subject/implicit_receiver.feature

This file was deleted.

71 changes: 71 additions & 0 deletions features/subject/one_liner_syntax.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
Feature: One-liner syntax

RSpec supports a one-liner syntax for setting an expectation
on the `subject`. RSpec will give the examples a doc string
that is auto-generated from the matcher used in the example.
This is designed specifically to help avoid duplication in
situations where the doc string and the matcher used in the
example mirror each other exactly. When used excessively,
it can produce documentation output that does not read well
or contribute to understanding the object you are describing.

This comes in two flavors:

* `is_expected` is defined simply as `expect(subject)` and is
designed for when you are using rspec-expectations with its
newer expect-based syntax.
* `should` was designed back when rspec-expectations only had
a should-based syntax. However, it continues to be available
and work even if the `:should` syntax is disabled (since that
merely removes `Object#should` but this is
`RSpec::Core::ExampleGroup#should`).

Note: this feature is only available when using rspec-expectations.

Scenario: implicit subject
Given a file named "example_spec.rb" with:
"""ruby
describe Array do
describe "when first created" do
# Rather than:
# it "should be empty" do
# subject.should be_empty
# end
it { should be_empty }
# or
it { is_expected.to be_empty }
end
end
"""
When I run `rspec example_spec.rb --format doc`
Then the examples should all pass
And the output should contain:
"""
Array
when first created
should be empty
should be empty
"""

Scenario: explicit subject
Given a file named "example_spec.rb" with:
"""ruby
describe Array do
describe "with 3 items" do
subject { [1,2,3] }
it { should_not be_empty }
# or
it { is_expected.not_to be_empty }
end
end
"""
When I run `rspec example_spec.rb --format doc`
Then the examples should all pass
And the output should contain:
"""
Array
with 3 items
should not be empty
should not be empty
"""
2 changes: 1 addition & 1 deletion lib/rspec/core/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def self.delegate_to_metadata(*keys)
delegate_to_metadata :full_description, :execution_result, :file_path, :pending, :location

# Returns the string submitted to `example` or its aliases (e.g.
# `specify`, `it`, etc). If no string is submitted (e.g. `it { should
# `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to
# do_something }`) it returns the message generated by the matcher if
# there is one, otherwise returns a message including the location of the
# example.
Expand Down
46 changes: 40 additions & 6 deletions lib/rspec/core/memoized_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module MemoizedHelpers
# syntax embraced by shoulda matchers:
#
# describe Widget do
# it { is_expected.to validate_presence_of(:name) }
# # or
# it { should validate_presence_of(:name) }
# end
#
Expand All @@ -31,8 +33,10 @@ module MemoizedHelpers
# end
# end
#
# # one-liner syntax - should is invoked on subject
# # one-liner syntax - expectation is set on the subject
# describe Person do
# it { is_expected.to be_eligible_to_vote }
# # or
# it { should be_eligible_to_vote }
# end
#
Expand Down Expand Up @@ -64,6 +68,11 @@ def subject
# end
#
# @see #subject
# @see #is_expected
#
# @note This only works if you are using rspec-expectations.
# @note If you are using RSpec's newer expect-based syntax you may
# want to use `is_expected.to` instead of `should`.
def should(matcher=nil, message=nil)
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
end
Expand All @@ -78,10 +87,34 @@ def should(matcher=nil, message=nil)
# end
#
# @see #subject
# @see #is_expected
#
# @note This only works if you are using rspec-expectations.
# @note If you are using RSpec's newer expect-based syntax you may
# want to use `is_expected.to_not` instead of `should_not`.
def should_not(matcher=nil, message=nil)
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
end

# Wraps the `subject` in `expect` to make it the target of an expectation.
# Designed to read nicely for one-liners.
#
# @example
#
# describe [1, 2, 3] do
# it { is_expected.to be_an Array }
# it { is_expected.not_to include 4 }
# end
#
# @see #subject
# @see #should
# @see #should_not
#
# @note This only works if you are using rspec-expectations.
def is_expected
expect(subject)
end

private

# @private
Expand Down Expand Up @@ -265,8 +298,9 @@ def let!(name, &block)
before { __send__(name) }
end

# Declares a `subject` for an example group which can then be the
# implicit receiver (through delegation) of calls to `should`.
# Declares a `subject` for an example group which can then be wrapped
# with `expect` using `is_expected` to make it the target of an expectation
# in a concise, one-line example.
#
# Given a `name`, defines a method with that name which returns the
# `subject`. This lets you declare the subject once and access it
Expand All @@ -281,13 +315,13 @@ def let!(name, &block)
#
# describe CheckingAccount, "with $50" do
# subject { CheckingAccount.new(Money.new(50, :USD)) }
# it { should have_a_balance_of(Money.new(50, :USD)) }
# it { should_not be_overdrawn }
# it { is_expected.to have_a_balance_of(Money.new(50, :USD)) }
# it { is_expected.not_to be_overdrawn }
# end
#
# describe CheckingAccount, "with a non-zero starting balance" do
# subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
# it { should_not be_overdrawn }
# it { is_expected.not_to be_overdrawn }
# it "has a balance equal to the starting balance" do
# account.balance.should eq(Money.new(50, :USD))
# end
Expand Down
13 changes: 11 additions & 2 deletions spec/rspec/core/memoized_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,16 @@ def not_ok?; false; end
it { should_not be_not_ok }
end

expect(group.run).to be_truthy
expect(group.run).to be true
end

it 'supports a new expect-based syntax' do
group = ExampleGroup.describe([1, 2, 3]) do
it { is_expected.to be_an Array }
it { is_expected.not_to include 4 }
end

expect(group.run).to be true
end
end

Expand Down Expand Up @@ -456,7 +465,7 @@ def hello_message; "Hello from module"; end
expect(subject_id_in_let).to eq(@subject_id_in_before)
end

it { should eq(subject) }
it { is_expected.to eq(subject) }
end

describe Object do
Expand Down

0 comments on commit 25590f5

Please sign in to comment.