rspec not_to change from not behaving as expected

Johnny C picture Johnny C · Oct 2, 2012 · Viewed 17.9k times · Source

I am experiencing some baffling behavior in rspec and rspec-rails 2.11.0. I have reproduced the behavior on a co-worker's app running 2.7.1 (both on ruby 1.9.3)

These tests work as expected (fails):

it "should not change i" do
    i = 0
    expect {
      i = 2
    }.not_to change { i }
end

it "should not change i" do
    i = 0
    expect {
      i = 2
    }.not_to change { i }.from( 0 )
end

Failure message is "result should not have changed, but did change from 0 to 2" in both cases

Changing the "from" in the expectation to a different value inexplicably makes it pass, rather than fail no matter what happens to the value of i in the expect block:

it "should not change i" do
    i = 0
    expect {
        i = 2
    }.not_to change { i }.from( 1 )
end

I've only recently upgraded to 1.9.3 and I can say with some certainty I would have noticed this behavior before if I had been experiencing it. Can anyone explain this and/or what I'm doing wrong?

Answer

Gazler picture Gazler · Oct 2, 2012

I don't think it does quite what you expect, but the way the tests are written doesn't really expose this. Your last value fails because it was never defined as 1.

The main step in revealing this is inverting the test.

describe "stuff" do

  it "should not change i" do
      i = 0
      expect {
        i = 2
      }.to change { i }
  end

  it "should not change i" do
      i = 0
      expect {
        i = 2
      }.to change { i }.from( 0 )
  end


  it "should not change i" do
      i = 0
      expect {
          i = 2
      }.to change { i }.from( 1 )
  end

end

This results in the first two tests passing, and the last test failing with the error result should have initially been 1, but was 0

So if we take the problem test as it was, you are saying is i should not change from 1 and since i is never 1, this test will never fail.

it "should not change i" do
    i = 0
    expect {
        i = 2
    }.not_to change { i }.from( 1 ) # i goes from 0 to 2, not 1 to 2, so the test is valid
end