{"action":"created","issue":{"url":"https://api.github.com/repos/rspec/rspec-expectations/issues/791","labels_url":"https://api.github.com/repos/rspec/rspec-expectations/issues/791/labels{/name}","comments_url":"https://api.github.com/repos/rspec/rspec-expectations/issues/791/comments","events_url":"https://api.github.com/repos/rspec/rspec-expectations/issues/791/events","html_url":"https://github.com/rspec/rspec-expectations/issues/791","id":78195456,"number":791,"title":"expect {}.to change obj, :msg not behaving as expected","user":{"login":"knovoselic","id":7026420,"avatar_url":"https://avatars.githubusercontent.com/u/7026420?v=3","gravatar_id":"","url":"https://api.github.com/users/knovoselic","html_url":"https://github.com/knovoselic","followers_url":"https://api.github.com/users/knovoselic/followers","following_url":"https://api.github.com/users/knovoselic/following{/other_user}","gists_url":"https://api.github.com/users/knovoselic/gists{/gist_id}","starred_url":"https://api.github.com/users/knovoselic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/knovoselic/subscriptions","organizations_url":"https://api.github.com/users/knovoselic/orgs","repos_url":"https://api.github.com/users/knovoselic/repos","events_url":"https://api.github.com/users/knovoselic/events{/privacy}","received_events_url":"https://api.github.com/users/knovoselic/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-05-19T18:13:49Z","updated_at":"2015-05-19T19:16:07Z","closed_at":null,"body":"Hi. I wrote a spec like this:\r\n\r\n    expect {\r\n      put :add_device, {device_name: 'test'}\r\n    }.not_to change user, :devices\r\n\r\nThis test should fail because `add_device` is changing `user.devices` (by adding another object into the relation), but it's passing.\r\n`User` is Mongoid model, and `devices` is embedded relation in `User` model.\r\n\r\nAfter a little debugging with pry, I've found a object in `rspec/matchers/built_in/change.rb` on line 50  called `@change_details`. It contains two fields of interest - `actual_before` and `actual_after`. After comparing the two it seems that both point to the same object in memory (array), so `actual_after` got updated after new `Device` was added into User's embedded relation.\r\nHere's the proof that they are pointing to the same object:\r\n\r\n    [10] pry(#<RSpec::Matchers::BuiltIn::Change>)> @change_details.actual_before.object_id\r\n    => 70163135333520\r\n    [11] pry(#<RSpec::Matchers::BuiltIn::Change>)> @change_details.actual_after.object_id\r\n    => 70163135333520\r\n\r\nSo here's my question: is this expected behaviour or a bug? If it's a bug, it should be easy to fix by calling `#dup` (or maybe `#clone`?) while retrieving `actual_before`. I can create pull request.\r\nIf I change spec like this, test works (fails) as expected:\r\n\r\n    expect {\r\n      put :add_device, {device_name: 'test'}\r\n    }.not_to change user.devices, :dup"},"comment":{"url":"https://api.github.com/repos/rspec/rspec-expectations/issues/comments/103638134","html_url":"https://github.com/rspec/rspec-expectations/issues/791#issuecomment-103638134","issue_url":"https://api.github.com/repos/rspec/rspec-expectations/issues/791","id":103638134,"user":{"login":"myronmarston","id":49391,"avatar_url":"https://avatars.githubusercontent.com/u/49391?v=3","gravatar_id":"","url":"https://api.github.com/users/myronmarston","html_url":"https://github.com/myronmarston","followers_url":"https://api.github.com/users/myronmarston/followers","following_url":"https://api.github.com/users/myronmarston/following{/other_user}","gists_url":"https://api.github.com/users/myronmarston/gists{/gist_id}","starred_url":"https://api.github.com/users/myronmarston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/myronmarston/subscriptions","organizations_url":"https://api.github.com/users/myronmarston/orgs","repos_url":"https://api.github.com/users/myronmarston/repos","events_url":"https://api.github.com/users/myronmarston/events{/privacy}","received_events_url":"https://api.github.com/users/myronmarston/received_events","type":"User","site_admin":false},"created_at":"2015-05-19T19:16:07Z","updated_at":"2015-05-19T19:16:07Z","body":"> Shouldn't the following return true?\r\n\r\nIdeally, it would, but in some circumstances it might not.  Any object can define `is_a?` to return whatever value it wants (including lying about it being a type that it's not) where as `Array === val` calls a method on `Array` so you'd have to monkey patch `Array` to make that return `true` when given an object that's not really an array.  (That's actually why we tend to use `===` throughout RSpec: we are given all manner of user objects that may have redefined `is_a?`--such as `user.is_a?(:admin)`--and as such we can't trust it like we can trust `Class#===`).\r\n\r\nMy guess is that `val` is a proxy object that wraps an array and proxies method calls to it, including `is_a?`, so that it lies and claims to be an array when it's not really.  You can check by doing `val.method(:is_a?).source_location` and `val.method(:is_a?).owner` to see where `is_a?` is defined and in what module it is defined."}}