Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
If a method for a class is doubled more than once within the same test
run, the original implementation of that method will change from double
to double, even if the double is deactivated correctly. Say we have two
distinct tests that both double the same method. Here is how that method
will be overridden as it goes along:
A) START: original method
B) ACTIVATE (1st test): method is doubled
C) DEACTIVATE (1st test): calls original method (A)
D) ACTIVATE (2nd test): original method (C) stored; method is again
doubled
E) DEACTIVATE (2nd test): calls original method (C)
With this commit, this changes to:
A) START: original method
B) ACTIVATE (1st test): method is doubled
C) DEACTIVATE (1st test): calls original method (A)
D) ACTIVATE (2nd test): original method not stored again; method is
again doubled
E) DEACTIVATE (2nd test): calls original method (A)
The provided RSpec example looks a bit contrived, but it corresponds to
this perfectly reasonable Minitest test case that was not working:
class UserControllerTest < ActionController::TestCase
should permit(:name).for(:create)
should_not permit(:admin).for(:create)
end
This instantiates two matchers at class load time, which resulted in the
second one overriding the double collection of the first one. Then when
the tests are executed, the first matcher would fail, because its double
is not listening to ActionController::Parameters#permit -- only the
second matcher's double collection gets activated.
This is fixed in Doublespeak by only creating one double collection per
class.
This provides a robust solution for temporarily stubbing (and
unstubbing) methods. It will be internally by the strong parameters and
delegation matchers.