Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Fixup change matcher #376

Merged
merged 8 commits into from

2 participants

@myronmarston
Owner

No description provided.

myronmarston added some commits
@myronmarston myronmarston Reword some failure messages to not reference should/should_not. 907dfc3
@myronmarston myronmarston Fix odd method formatting. 37a8a0e
@myronmarston myronmarston Reword an error message. 91c7863
@myronmarston myronmarston Doc the change matcher class. 198504f
@myronmarston myronmarston Refactor change matcher.
* Use separate classes for relative vs specific changes.
* This greatly simplifies the `matches?` and `failure_message`
  logic.
* Reduces the number of instance variables are floating around.
490648f
Changelog.md
@@ -13,6 +13,7 @@ Enhancements:
* `failure_message_for_should_not` => `failure_message_when_negated`
* `match_for_should` => `match`
* `match_for_should_not` => `match_when_negated`
+* Improve generated descriptions from `change` mather. (Myron Marston)
@soulcutter Collaborator

This typo stands out a bit.

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

I like the refactor - it's much cleaner this way.

@soulcutter soulcutter commented on the diff
lib/rspec/matchers/built_in/change.rb
((153 lines not shown))
end
- def to(to)
- @eval_after = true
- @expected_after = to
- self
+ def failure_message
+ if !matches_before?
@soulcutter Collaborator

Seems like the order of this if/else could be reversed to save a negation, though I hesitate to even mention it because it's so minor of a nitpick

@myronmarston Owner

This is the failure message, so we're figuring out which part failed, and then returning a corresponding failure message. If we switch it, we'll be figuring out which part didn't fail, and then returning a failure message for the part that did (that wasn't in the conditional) so it would be much more confusing, IMO.

@soulcutter Collaborator

I can see it both ways. I'm fine with leaving it as-is, it's plenty readable.

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

@soulcutter -- I pushed a couple more commits, which fix #154.

@soulcutter soulcutter merged commit d1f71c7 into master

1 check passed

Details default The Travis CI build passed
@soulcutter soulcutter deleted the fixup-change-matcher branch
@soulcutter
Collaborator

Looks good - definitely appreciate the attention to documentation also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 3, 2013
  1. @myronmarston
  2. @myronmarston
  3. @myronmarston

    Reword an error message.

    myronmarston authored
  4. @myronmarston
Commits on Dec 4, 2013
  1. @myronmarston

    Refactor change matcher.

    myronmarston authored
    * Use separate classes for relative vs specific changes.
    * This greatly simplifies the `matches?` and `failure_message`
      logic.
    * Reduces the number of instance variables are floating around.
  2. @myronmarston
  3. @myronmarston
  4. @myronmarston

    Improve change docs.

    myronmarston authored
This page is out of date. Refresh to see the latest.
View
6 Changelog.md
@@ -13,6 +13,7 @@ Enhancements:
* `failure_message_for_should_not` => `failure_message_when_negated`
* `match_for_should` => `match`
* `match_for_should_not` => `match_when_negated`
+* Improve generated descriptions from `change` matcher. (Myron Marston)
Breaking Changes for 3.0.0:
@@ -20,10 +21,15 @@ Breaking Changes for 3.0.0:
`Rspec` or `Spec`. (Myron Marston)
* Remove deprecated `RSpec::Expectations.differ=`. (Myron Marston)
* Remove support for deprecated `expect(...).should`. (Myron Marston)
+* Explicitly disallow `expect { }.not_to change { }` with `by`,
+ `by_at_least`, `by_at_most` or `to`. These have never been supported
+ but did not raise explicit errors. (Myron Marston)
Bug Fixes:
* Fix wrong matcher descriptions with falsey expected value (yujinakayama)
+* Fix `expect { }.not_to change { }.from(x)` so that the matcher only
+ passes if the starting value is `x`. (Tyler Rick, Myron Marston)
Deprecations:
View
4 features/built_in_matchers/expect_change.feature
@@ -37,7 +37,7 @@ Feature: expect change
"""
When I run `rspec spec/example_spec.rb`
Then the output should contain "1 failure"
- Then the output should contain "should have been changed by 2, but was changed by 1"
+ Then the output should contain "expected result to have changed by 2, but was changed by 1"
Scenario: expect no change
Given a file named "spec/example_spec.rb" with:
@@ -56,4 +56,4 @@ Feature: expect change
"""
When I run `rspec spec/example_spec.rb`
Then the output should contain "2 failures"
- Then the output should contain "should not have changed, but did change from 1 to 2"
+ Then the output should contain "expected result not to have changed, but did change from 1 to 2"
View
26 lib/rspec/matchers.rb
@@ -275,10 +275,19 @@ def be_within(delta)
# You can either pass <tt>receiver</tt> and <tt>message</tt>, or a block,
# but not both.
#
- # When passing a block, it must use the <tt>{ ... }</tt> format, not
- # do/end, as <tt>{ ... }</tt> binds to the `change` method, whereas do/end
+ # When passing a block, it must use the `{ ... }` format, not
+ # do/end, as `{ ... }` binds to the `change` method, whereas do/end
# would errantly bind to the `expect(..).to` or `expect(...).not_to` method.
#
+ # You can chain any of the following off of the end to specify details
+ # about the change:
+ #
+ # * `by`
+ # * `by_at_least`
+ # * `by_at_most`
+ # * `from`
+ # * `to`
+ #
# @example
#
# expect {
@@ -305,7 +314,7 @@ def be_within(delta)
# string = "string"
# expect {
# string
- # }.not_to change { string }
+ # }.not_to change { string }.from("string")
#
# expect {
# person.happy_birthday
@@ -326,12 +335,13 @@ def be_within(delta)
#
# == Notes
#
- # Evaluates <tt>receiver.message</tt> or <tt>block</tt> before and after it
- # evaluates the block passed to <tt>expect</tt>.
+ # Evaluates `receiver.message` or `block` before and after it
+ # evaluates the block passed to `expect`.
#
- # <tt>expect( ... ).not_to change</tt> only supports the form with no subsequent
- # calls to <tt>by</tt>, <tt>by_at_least</tt>, <tt>by_at_most</tt>,
- # <tt>to</tt> or <tt>from</tt>.
+ # `expect( ... ).not_to change` supports the form that specifies `from`
+ # (which specifies what you expect the starting, unchanged value to be)
+ # but does not support forms with subsequent calls to `by`, `by_at_least`,
+ # `by_at_most` or `to`.
def change(receiver=nil, message=nil, &block)
BuiltIn::Change.new(receiver, message, &block)
end
View
250 lib/rspec/matchers/built_in/change.rb
@@ -1,96 +1,135 @@
module RSpec
module Matchers
module BuiltIn
+ # Describes an expected mutation.
class Change
- def initialize(receiver=nil, message=nil, &block)
- @message = message
- @value_proc = block || lambda {receiver.__send__(message)}
- @expected_after = @expected_before = @minimum = @maximum = @expected_delta = nil
- @eval_before = @eval_after = false
+ # Specifies the delta of the expected change.
+ def by(expected_delta)
+ ChangeRelatively.new(@change_details, expected_delta, :==, :by)
+ end
+
+ # Specifies a minimum delta of the expected change.
+ def by_at_least(minimum)
+ ChangeRelatively.new(@change_details, minimum, :>=, :by_at_least)
+ end
+
+ # Specifies a maximum delta of the expected change.
+ def by_at_most(maximum)
+ ChangeRelatively.new(@change_details, maximum, :<=, :by_at_most)
+ end
+
+ # Specifies the new value you expect.
+ def to(value)
+ ChangeToValue.new(@change_details, value)
end
+ # Specifies the original value.
+ def from(value)
+ ChangeFromValue.new(@change_details, value)
+ end
+
+ # @api private
def matches?(event_proc)
raise_block_syntax_error if block_given?
+ @change_details.perform_change(event_proc)
+ @change_details.changed?
+ end
+ alias == matches?
- @actual_before = evaluate_value_proc
- event_proc.call
- @actual_after = evaluate_value_proc
+ # @api private
+ def failure_message
+ "expected #{@change_details.message} to have changed, but is still #{@change_details.actual_before.inspect}"
+ end
- (!change_expected? || changed?) && matches_before? && matches_after? && matches_expected_delta? && matches_min? && matches_max?
+ # @api private
+ def failure_message_when_negated
+ "expected #{@change_details.message} not to have changed, but did change from #{@change_details.actual_before.inspect} to #{@change_details.actual_after.inspect}"
+ end
+
+ # @api private
+ def description
+ "change #{@change_details.message}"
+ end
+
+ private
+
+ def initialize(receiver=nil, message=nil, &block)
+ @change_details = ChangeDetails.new(receiver, message, &block)
end
- alias == matches?
def raise_block_syntax_error
- raise SyntaxError.new(<<-MESSAGE)
-block passed to should or should_not change must use {} instead of do/end
-MESSAGE
+ raise SyntaxError,
+ "The block passed to the `change` matcher must use `{ ... }` instead of do/end"
end
+ end
- def evaluate_value_proc
- case val = @value_proc.call
- when Enumerable, String
- val.dup
- else
- val
- end
+ # Used to specify a relative change.
+ # @api private
+ class ChangeRelatively
+ def initialize(change_details, expected_delta, comparison, relativity)
+ @change_details = change_details
+ @expected_delta = expected_delta
+ @comparison = comparison
+ @relativity = relativity
end
def failure_message
- if @eval_before && !expected_matches_actual?(@expected_before, @actual_before)
- "#{message} should have initially been #{@expected_before.inspect}, but was #{@actual_before.inspect}"
- elsif @eval_after && !expected_matches_actual?(@expected_after, @actual_after)
- "#{message} should have been changed to #{failure_message_for_expected_after}, but is now #{@actual_after.inspect}"
- elsif @expected_delta
- "#{message} should have been changed by #{@expected_delta.inspect}, but was changed by #{actual_delta.inspect}"
- elsif @minimum
- "#{message} should have been changed by at least #{@minimum.inspect}, but was changed by #{actual_delta.inspect}"
- elsif @maximum
- "#{message} should have been changed by at most #{@maximum.inspect}, but was changed by #{actual_delta.inspect}"
- else
- "#{message} should have changed, but is still #{@actual_before.inspect}"
- end
+ "expected #{@change_details.message} to have changed #{@relativity.to_s.gsub("_", " ")} #{@expected_delta.inspect}, " +
+ "but was changed by #{@change_details.actual_delta.inspect}"
end
- def actual_delta
- @actual_after - @actual_before
+ def matches?(event_proc)
+ @change_details.perform_change(event_proc)
+ @change_details.actual_delta.__send__(@comparison, @expected_delta)
end
- def failure_message_when_negated
- "#{message} should not have changed, but did change from #{@actual_before.inspect} to #{@actual_after.inspect}"
+ def does_not_match?(event_proc)
+ raise NotImplementedError, "`expect { }.not_to change { }.#{@relativity}()` is not supported"
end
- def by(expected_delta)
- @expected_delta = expected_delta
- self
+ # @api private
+ def description
+ "change #{@change_details.message} #{@relativity.to_s.gsub("_", " ")} #{@expected_delta.inspect}"
end
+ end
- def by_at_least(minimum)
- @minimum = minimum
- self
+ # Base class for specifying a change from and/or to specific values.
+ # @api private
+ class SpecificValuesChange
+ MATCH_ANYTHING = ::Object.ancestors.last
+
+ def initialize(change_details, from, to)
+ @change_details = change_details
+ @expected_before = from
+ @expected_after = to
end
- def by_at_most(maximum)
- @maximum = maximum
- self
+ def matches?(event_proc)
+ @change_details.perform_change(event_proc)
+ @change_details.changed? && matches_before? && matches_after?
end
- def to(to)
- @eval_after = true
- @expected_after = to
- self
+ def failure_message
+ if !matches_before?
@soulcutter Collaborator

Seems like the order of this if/else could be reversed to save a negation, though I hesitate to even mention it because it's so minor of a nitpick

@myronmarston Owner

This is the failure message, so we're figuring out which part failed, and then returning a corresponding failure message. If we switch it, we'll be figuring out which part didn't fail, and then returning a failure message for the part that did (that wasn't in the conditional) so it would be much more confusing, IMO.

@soulcutter Collaborator

I can see it both ways. I'm fine with leaving it as-is, it's plenty readable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ "expected #{@change_details.message} to have initially been #{@expected_before.inspect}, but was #{@change_details.actual_before.inspect}"
+ else
+ "expected #{@change_details.message} to have changed to #{failure_message_for_expected_after}, but is now #{@change_details.actual_after.inspect}"
+ end
end
- def from (before)
- @eval_before = true
- @expected_before = before
- self
+ private
+
+ def matches_before?
+ expected_matches_actual?(@expected_before, @change_details.actual_before)
end
- def description
- "change ##{message}"
+ def matches_after?
+ expected_matches_actual?(@expected_after, @change_details.actual_after)
end
- private
+ def expected_matches_actual?(expected, actual)
+ expected === actual || actual == expected
+ end
def failure_message_for_expected_after
if RSpec::Matchers.is_a_matcher?(@expected_after)
@@ -99,41 +138,102 @@ def failure_message_for_expected_after
@expected_after.inspect
end
end
+ end
- def message
- @message || "result"
+ # Used to specify a change from a specific value
+ # (and, optionally, to a specific value).
+ # @api private
+ class ChangeFromValue < SpecificValuesChange
+ def initialize(change_details, expected_before)
+ @description_suffix = nil
+ super(change_details, expected_before, MATCH_ANYTHING)
end
- def change_expected?
- @expected_delta != 0
+ def to(value)
+ @expected_after = value
+ @description_suffix = " to #{value.inspect}"
+ self
end
- def changed?
- @actual_before != @actual_after
+ def does_not_match?(event_proc)
+ if @description_suffix
+ raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
+ end
+
+ @change_details.perform_change(event_proc)
+ !@change_details.changed? && matches_before?
end
- def matches_before?
- @eval_before ? expected_matches_actual?(@expected_before, @actual_before) : true
+ def failure_message_when_negated
+ if !matches_before?
+ "expected #{@change_details.message} to have initially been #{@expected_before.inspect}, but was #{@change_details.actual_before.inspect}"
+ else
+ "expected #{@change_details.message} not to have changed, but did change from #{@change_details.actual_before.inspect} to #{@change_details.actual_after.inspect}"
+ end
end
- def matches_after?
- @eval_after ? expected_matches_actual?(@expected_after, @actual_after) : true
+ def description
+ "change #{@change_details.message} from #{@expected_before.inspect}#{@description_suffix}"
end
+ end
- def matches_expected_delta?
- @expected_delta ? (@actual_before + @expected_delta == @actual_after) : true
+ # Used to specify a change to a specific value
+ # (and, optionally, from a specific value).
+ # @api private
+ class ChangeToValue < SpecificValuesChange
+ def initialize(change_details, expected_after)
+ @description_suffix = nil
+ super(change_details, MATCH_ANYTHING, expected_after)
end
- def matches_min?
- @minimum ? (@actual_after - @actual_before >= @minimum) : true
+ def from(value)
+ @expected_before = value
+ @description_suffix = " from #{value.inspect}"
+ self
end
- def matches_max?
- @maximum ? (@actual_after - @actual_before <= @maximum) : true
+ def does_not_match?(event_proc)
+ raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
end
- def expected_matches_actual?(expected, actual)
- expected === actual || actual == expected
+ def description
+ "change #{@change_details.message} to #{@expected_after.inspect}#{@description_suffix}"
+ end
+ end
+
+ # Encapsulates the details of the before/after values.
+ # @api private
+ class ChangeDetails
+ attr_reader :message, :actual_before, :actual_after
+
+ def initialize(receiver=nil, message=nil, &block)
+ @message = message ? "##{message}" : "result"
+ @value_proc = block || lambda { receiver.__send__(message) }
+ end
+
+ def perform_change(event_proc)
+ @actual_before = evaluate_value_proc
+ event_proc.call
+ @actual_after = evaluate_value_proc
+ end
+
+ def changed?
+ @actual_before != @actual_after
+ end
+
+ def actual_delta
+ @actual_after - @actual_before
+ end
+
+ private
+
+ def evaluate_value_proc
+ case val = @value_proc.call
+ when Enumerable, String
+ val.dup
+ else
+ val
+ end
end
end
end
View
2  lib/rspec/matchers/built_in/match_array.rb
@@ -23,7 +23,7 @@ def failure_message
end
def failure_message_when_negated
- "Matcher does not support should_not"
+ "`match_array` does not support negation"
end
def description
View
159 spec/rspec/matchers/change_spec.rb
@@ -20,7 +20,7 @@ class SomethingExpected
it "fails when actual is not modified by the block" do
expect do
expect {}.to change(@instance, :some_value)
- end.to fail_with("some_value should have changed, but is still 5")
+ end.to fail_with("expected #some_value to have changed, but is still 5")
end
it "provides a #description" do
@@ -55,7 +55,7 @@ class SomethingExpected
it "fails when actual is not modified by the block" do
expect do
expect {}.to change(@instance, :some_value)
- end.to fail_with("some_value should have changed, but is still true")
+ end.to fail_with("expected #some_value to have changed, but is still true")
end
end
@@ -72,7 +72,7 @@ class SomethingExpected
it "fails when actual is not modified by the block" do
expect do
expect {}.to change(@instance, :some_value)
- end.to fail_with("some_value should have changed, but is still nil")
+ end.to fail_with("expected #some_value to have changed, but is still nil")
end
end
@@ -89,7 +89,7 @@ class SomethingExpected
it "fails when a predicate on the actual fails" do
expect do
expect {@instance.some_value << 1}.to change { @instance.some_value }.to be_empty
- end.to fail_with(/result should have been changed to/)
+ end.to fail_with(/result to have changed to/)
end
it "passes when a predicate on the actual passes" do
@@ -100,7 +100,7 @@ class SomethingExpected
it "fails when actual is not modified by the block" do
expect do
expect {}.to change(@instance, :some_value)
- end.to fail_with("some_value should have changed, but is still []")
+ end.to fail_with("expected #some_value to have changed, but is still []")
end
end
@@ -131,7 +131,7 @@ class SomethingExpected
string = "ab"
expect {
expect { }.to change { string }
- }.to fail_with(/should have changed/)
+ }.to fail_with(/to have changed/)
end
end
@@ -168,7 +168,7 @@ def ==(other)
it "fails when actual is not modified by the block" do
expect do
expect {}.to change(@instance, :some_value)
- end.to fail_with(/^some_value should have changed, but is still/)
+ end.to fail_with(/^expected #some_value to have changed, but is still/)
end
end
@@ -187,7 +187,7 @@ def ==(other)
it "fails when actual is not modified by the block" do
expect do
expect {@instance.some_value = 6}.not_to change(@instance, :some_value)
- end.to fail_with("some_value should not have changed, but did change from 5 to 6")
+ end.to fail_with("expected #some_value not to have changed, but did change from 5 to 6")
end
end
@@ -210,17 +210,17 @@ def ==(other)
it "fails when actual is not modified by the block" do
expect do
expect {}.to change{ @instance.some_value }
- end.to fail_with("result should have changed, but is still 5")
+ end.to fail_with("expected result to have changed, but is still 5")
end
it "warns if passed a block using do/end instead of {}" do
expect do
expect {}.to change do; end
- end.to raise_error(SyntaxError, /block passed to should or should_not/)
+ end.to raise_error(SyntaxError, /block passed to the `change` matcher/)
end
it "provides a #description" do
- expect(change { @instance.some_value }.description).to eq "change #result"
+ expect(change { @instance.some_value }.description).to eq "change result"
end
end
@@ -237,13 +237,72 @@ def ==(other)
it "fails when actual is not modified by the block" do
expect do
expect {@instance.some_value = 6}.not_to change { @instance.some_value }
- end.to fail_with("result should not have changed, but did change from 5 to 6")
+ end.to fail_with("expected result not to have changed, but did change from 5 to 6")
end
it "warns if passed a block using do/end instead of {}" do
expect do
expect {}.not_to change do; end
- end.to raise_error(SyntaxError, /block passed to should or should_not/)
+ end.to raise_error(SyntaxError, /block passed to the `change` matcher/)
+ end
+end
+
+describe "expect { ... }.not_to change { }.from" do
+ it 'passes when the value starts at the from value and does not change' do
+ k = 5
+ expect { }.not_to change { k }.from(5)
+ end
+
+ it 'fails when the value starts at a different value and does not change' do
+ expect {
+ k = 6
+ expect { }.not_to change { k }.from(5)
+ }.to fail_with(/expected result to have initially been 5/)
+ end
+
+ it 'fails when the value starts at the from value and changes' do
+ expect {
+ k = 5
+ expect { k += 1 }.not_to change { k }.from(5)
+ }.to fail_with(/but did change from 5 to 6/)
+ end
+end
+
+describe "expect { ... }.not_to change { }.to" do
+ it 'is not supported' do
+ expect {
+ expect { }.not_to change { }.to(3)
+ }.to raise_error(NotImplementedError)
+ end
+
+ it 'is not supported when it comes after `from`' do
+ expect {
+ expect { }.not_to change { }.from(nil).to(3)
+ }.to raise_error(NotImplementedError)
+ end
+end
+
+describe "expect { ... }.not_to change { }.by" do
+ it 'is not supported' do
+ expect {
+ expect { }.not_to change { }.by(3)
+ }.to raise_error(NotImplementedError)
+ end
+end
+
+describe "expect { ... }.not_to change { }.by_at_least" do
+ it 'is not supported' do
+ expect {
+ expect { }.not_to change { }.by_at_least(3)
+ }.to raise_error(NotImplementedError)
+ end
+end
+
+describe "expect { ... }.not_to change { }.by_at_most" do
+ it 'is not supported' do
+ expect {
+ expect { }.not_to change { }.by_at_most(3)
+ }.to raise_error(NotImplementedError)
end
end
@@ -264,13 +323,17 @@ def ==(other)
it "fails when the attribute is changed by unexpected amount" do
expect do
expect { @instance.some_value += 2 }.to change(@instance, :some_value).by(1)
- end.to fail_with("some_value should have been changed by 1, but was changed by 2")
+ end.to fail_with("expected #some_value to have changed by 1, but was changed by 2")
end
it "fails when the attribute is changed by unexpected amount in the opposite direction" do
expect do
expect { @instance.some_value -= 1 }.to change(@instance, :some_value).by(1)
- end.to fail_with("some_value should have been changed by 1, but was changed by -1")
+ end.to fail_with("expected #some_value to have changed by 1, but was changed by -1")
+ end
+
+ it "provides a #description" do
+ expect(change(@instance, :some_value).by(3).description).to eq "change #some_value by 3"
end
end
@@ -287,13 +350,17 @@ def ==(other)
it "fails when the attribute is changed by unexpected amount" do
expect do
expect { @instance.some_value += 2 }.to change{@instance.some_value}.by(1)
- end.to fail_with("result should have been changed by 1, but was changed by 2")
+ end.to fail_with("expected result to have changed by 1, but was changed by 2")
end
it "fails when the attribute is changed by unexpected amount in the opposite direction" do
expect do
expect { @instance.some_value -= 1 }.to change{@instance.some_value}.by(1)
- end.to fail_with("result should have been changed by 1, but was changed by -1")
+ end.to fail_with("expected result to have changed by 1, but was changed by -1")
+ end
+
+ it "provides a #description" do
+ expect(change { @instance.some_value }.by(3).description).to eq "change result by 3"
end
end
@@ -314,9 +381,12 @@ def ==(other)
it "fails when the attribute is changed by less than the expected amount" do
expect do
expect { @instance.some_value += 1 }.to change(@instance, :some_value).by_at_least(2)
- end.to fail_with("some_value should have been changed by at least 2, but was changed by 1")
+ end.to fail_with("expected #some_value to have changed by at least 2, but was changed by 1")
end
+ it "provides a #description" do
+ expect(change(@instance, :some_value).by_at_least(3).description).to eq "change #some_value by at least 3"
+ end
end
describe "expect { ... }.to change { block }.by_at_least(expected)" do
@@ -336,7 +406,11 @@ def ==(other)
it "fails when the attribute is changed by less than the unexpected amount" do
expect do
expect { @instance.some_value += 1 }.to change{@instance.some_value}.by_at_least(2)
- end.to fail_with("result should have been changed by at least 2, but was changed by 1")
+ end.to fail_with("expected result to have changed by at least 2, but was changed by 1")
+ end
+
+ it "provides a #description" do
+ expect(change { @instance.some_value }.by_at_least(3).description).to eq "change result by at least 3"
end
end
@@ -358,9 +432,12 @@ def ==(other)
it "fails when the attribute is changed by greater than the expected amount" do
expect do
expect { @instance.some_value += 2 }.to change(@instance, :some_value).by_at_most(1)
- end.to fail_with("some_value should have been changed by at most 1, but was changed by 2")
+ end.to fail_with("expected #some_value to have changed by at most 1, but was changed by 2")
end
+ it "provides a #description" do
+ expect(change(@instance, :some_value).by_at_most(3).description).to eq "change #some_value by at most 3"
+ end
end
describe "expect { ... }.to change { block }.by_at_most(expected)" do
@@ -380,7 +457,11 @@ def ==(other)
it "fails when the attribute is changed by greater than the unexpected amount" do
expect do
expect { @instance.some_value += 2 }.to change{@instance.some_value}.by_at_most(1)
- end.to fail_with("result should have been changed by at most 1, but was changed by 2")
+ end.to fail_with("expected result to have changed by at most 1, but was changed by 2")
+ end
+
+ it "provides a #description" do
+ expect(change { @instance.some_value }.by_at_most(3).description).to eq "change result by at most 3"
end
end
@@ -398,7 +479,7 @@ def ==(other)
it "fails when attribute is not == to expected value before executing block" do
expect do
expect { @instance.some_value = 'foo' }.to change(@instance, :some_value).from(false)
- end.to fail_with("some_value should have initially been false, but was true")
+ end.to fail_with("expected #some_value to have initially been false, but was true")
end
end
context "with non-boolean values" do
@@ -420,7 +501,11 @@ def ==(other)
it "fails when attribute is not === to expected value before executing block" do
expect do
expect { @instance.some_value = "knot" }.to change(@instance, :some_value).from("cat")
- end.to fail_with("some_value should have initially been \"cat\", but was \"string\"")
+ end.to fail_with("expected #some_value to have initially been \"cat\", but was \"string\"")
+ end
+
+ it "provides a #description" do
+ expect(change(@instance, :some_value).from(3).description).to eq "change #some_value from 3"
end
end
end
@@ -444,7 +529,11 @@ def ==(other)
it "fails when attribute is not === to expected value before executing block" do
expect do
expect { @instance.some_value = "knot" }.to change{@instance.some_value}.from("cat")
- end.to fail_with("result should have initially been \"cat\", but was \"string\"")
+ end.to fail_with("expected result to have initially been \"cat\", but was \"string\"")
+ end
+
+ it "provides a #description" do
+ expect(change { }.from(3).description).to eq "change result from 3"
end
end
@@ -462,7 +551,7 @@ def ==(other)
it "fails when attribute is not == to expected value after executing block" do
expect do
expect { @instance.some_value = 1 }.to change(@instance, :some_value).from(true).to(false)
- end.to fail_with("some_value should have been changed to false, but is now 1")
+ end.to fail_with("expected #some_value to have changed to false, but is now 1")
end
end
context "with non-boolean values" do
@@ -484,7 +573,7 @@ def ==(other)
it "fails when attribute is not === to expected value after executing block" do
expect do
expect { @instance.some_value = "cat" }.to change(@instance, :some_value).from("string").to("dog")
- end.to fail_with("some_value should have been changed to \"dog\", but is now \"cat\"")
+ end.to fail_with("expected #some_value to have changed to \"dog\", but is now \"cat\"")
end
end
end
@@ -508,7 +597,11 @@ def ==(other)
it "fails when attribute is not === to expected value after executing block" do
expect do
expect { @instance.some_value = "cat" }.to change{@instance.some_value}.from("string").to("dog")
- end.to fail_with("result should have been changed to \"dog\", but is now \"cat\"")
+ end.to fail_with("expected result to have changed to \"dog\", but is now \"cat\"")
+ end
+
+ it "provides a #description" do
+ expect(change { }.to(3).description).to eq "change result to 3"
end
end
@@ -529,13 +622,13 @@ def ==(other)
it "shows the correct messaging when #after and #to are different" do
expect do
expect { @instance.some_value = "cat" }.to change(@instance, :some_value).from("string").to("dog")
- end.to fail_with("some_value should have been changed to \"dog\", but is now \"cat\"")
+ end.to fail_with("expected #some_value to have changed to \"dog\", but is now \"cat\"")
end
it "shows the correct messaging when #before and #from are different" do
expect do
expect { @instance.some_value = "cat" }.to change(@instance, :some_value).from("not_string").to("cat")
- end.to fail_with("some_value should have initially been \"not_string\", but was \"string\"")
+ end.to fail_with("expected #some_value to have initially been \"not_string\", but was \"string\"")
end
end
@@ -552,6 +645,14 @@ def ==(other)
it "passes when #from comes before #to" do
expect { @instance.some_value = "cat" }.to change{@instance.some_value}.from("string").to("cat")
end
+
+ it "provides a #description when #from comes before #to" do
+ expect(change { }.from(1).to(3).description).to eq "change result from 1 to 3"
+ end
+
+ it "provides a #description when #to comes before #from" do
+ expect(change { }.to(1).from(3).description).to eq "change result to 1 from 3"
+ end
end
describe RSpec::Matchers::BuiltIn::Change do
View
4 spec/rspec/matchers/match_array_spec.rb
@@ -53,7 +53,7 @@ def array.send; :sent; end
it "is not supported" do
expect {
[1,2,3].should_not =~ [1,2,3]
- }.to fail_with(/Matcher does not support should_not/)
+ }.to fail_with(/`match_array` does not support negation/)
end
end
@@ -160,7 +160,7 @@ def array.send; :sent; end
it "is not supported" do
expect {
expect([1,2,3]).not_to match_array [1,2,3]
- }.to fail_with(/Matcher does not support should_not/)
+ }.to fail_with(/`match_array` does not support negation/)
end
end
Something went wrong with that request. Please try again.