-
-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent use of Mocha outside the context of a test/example #327
Conversation
Both Mocha::Integration::MiniTest::Adapter and Mocha::Integration::TestUnit::Adapter were already calling Mocha::Hooks#mocha_setup, but the legacy integration modules were not. Even though Mocha::Hooks#mocha_setup is currently a no-op, I'm planning to change this and making this change now means I'm starting from a consistent baseline. Note that I've confirmed that RSpec::Core::MockingAdapters::Mocha#setup_mocks_for_rspec already calls Mocha::Hooks#mocha_setup [1]. [1]: https://github.com/rspec/rspec-core/blob/a8aae27114dde9ebc429a168b9b3c63dc18e5088/lib/rspec/core/mocking_adapters/mocha.rb#L44
lib/mocha/mockery.rb
Outdated
class << self | ||
|
||
def instance | ||
@instance ||= new | ||
@instances.last || Null.new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This throws undefined method "last" for nil:NilClass
when you attempt to use mocha uninitialized (e.g. example snippets from documentation). This dirty change fixes it:
@instances.nil? ? Null.new : @instances.last
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lzap Gah! Looks like I went wrong with some of my interactive rebasing. Thanks for testing - I'll try to sort it out as soon as I can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lzap I'm in the process of fixing this and just to warn you I plan to force-push the branch shortly.
Works great here, I vote for some easy opt-out, I can imagine this can break tests for some. An environmental variable should be good enough for those who don't want to fix their tests, can't or want to postpone this. Kudos, great job! |
Previously the Mockery singleton instance was lazily instantiated the first time it was needed. This meant that it could be instantiated outside the scope of a test with no subsequent calls to Mockery.verify, Mockery.teardown, or Mockery.reset_instance. This had the potential to leave things in an inconsistent state and resulted in confusing errors in tests. See #292 for some examples reported by users. This change is a step on the way to solving that issue. Note that rather than simply instantiating a single instance of a Mockery, I've had to store a stack of instances. This is entirely to cope with the acceptance tests which result in nested calls to Mockery.setup and Mockery.teardown: 1. Outer acceptance test: Mockery.setup 2. Inner test in run_as_test(s) block: Mockery.setup 3. Inner test in run_as_test(s) block: Mockery.teardown 4. Outer acceptance test: Mockery.teardown I've also had to copy the logger instance from the outer Mockery instance to the inner one in order that the outer acceptance tests have access to the same logger as used by the inner one. Obviously the above complication is a bit undesirable, given that it's only needed to support the acceptance tests and should never be required by users. However, I tried various alternatives and I felt this one was the best option. Note also that this indirectly addresses #293.
Now that the Mockery instance is being explicitly instantiated and destroyed, I think this restoration of the original logger is redundant.
Hopefully this addresses #292. If no Mockery has been instantiated when a call is made to any method which would otherwise change the state of the Mockery instance then raise a NotInitializedError. This is achieved by returning an instance of Mockery::Null if no Mockery has been instantiated. Note that the Mockery::Null class inherits from the Mockery class, but overrides all methods which result in a change of state.
e89f7fc
to
89eeb1c
Compare
Thanks for testing it. 👍
I'm hesitant to offer an opt-out, because Mocha was never intended to be used outside the context of a test. I'm probably going to release it like this and see whether anyone runs into problems. In any case, a better solution for that problem is probably to call |
I just ran the tests in the GOV.UK Whitehall project with these changes and saw no ill effects, so I'm happy to merge. |
@lazyatom has just updated Kintama to work with this change and all appears to be well. |
I've released this change in v1.5.0. |
🙋♂️ I'm running into problems... A gem I've been contributing to now has failing tests on Travis CI for all branches. One of the tests stubs out method calls in (A release with a deprecation warning instead of raising an exception might have been nice in this case). |
@Evan-M Sorry to hear that. Can you point me at one of the tests which is failing? What test framework are you using? There shouldn't be a problem stubbing methods in a |
@Evan-M I've left a comment on the issue you raised on |
Yeah, no problem... I should have linked to the failures in my original comment. A little more pretext: Mocha::NotInitializedError: Mocha methods cannot be used outside the context of a test When I tried to re-run the build, all the build failed; I suspect I merged lynndylanhurley/devise_token_auth#1134 right about the same time that version 1.5.0 was released. The same build failure / test failure appears to happen for another (unmerged) PR as well.
The test suite is based on Minitest, but there is a mixture of the Minitest Spec DSL and the Minitest Test DSL. (It was that way when I became involved in the project 🤷♂️).
Yeah, I was wondering about stubbing methods in a
Anyway, the repo is lynndylanhurley/devise_token_auth. The failing tests are all in the test file Also the failing tests in question here are actually part of an Integration Test, so it is probably a bad idea to stub any method calls! |
@Evan-M The upgrade to Mocha v1.5.0 definitely revealed a problem, but I think there was already an underlying problem in that test. Did you see my comment: lynndylanhurley/devise_token_auth#1137 (comment) ? If you fix that, there's shouldn't be a problem stubbing/expecting methods in a |
This change means that any attempt to use Mocha outside the context of a test or example will result in a
Mocha::NotInitializedError
being raised with the following message: "Mocha methods cannot be used outside the context of a test".Fixes #292
Fixes #293 (indirectly)
Supersedes #319