-
Notifications
You must be signed in to change notification settings - Fork 89
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
$CLASS mock within DESCRIBE() affects other tests #936
Comments
So, the cause here is that $class_mock is closed-over by the subs you define inside the describe block. for instance the 'test_mocked' test sub. These subs do not get destroyed as they are kept in a structure inside the framework. This has to be this way because of the way the framework defines the blocks at the start, then runs them later. Also the blocks may be reused in other places, we do not know when they are no longer needed. Since the variable never gets garbage collected it is never destroyed and thus the mock never ends. This is not really a design decision of the framework, and also not something the framework can do anything about. This is a perl behavior, if you enclose a variable you have to be aware of when/where it may or may not be destroyed. Your "fix" of using an after_all to clear the variable is correct. Hmm, there is also a behavior that I can not find documented, which is a huge oversight. If mock is used in a void context inside a describe block, it will act like a before_each: use Test2::Tools::Spec;
use Test2::V0;
describe foo => sub {
mock 'Some::Class' => (
add => [foo => sub { 'foo' }],
);
tests foo => sub {
is(Some::Class->foo, 'foo', "Mock worked");
};
};
tests foo => sub {
ok(!Some::Class->can('foo'), "No Mock");
};
done_testing; ^ that test works fine. The behavior you have reported is "works as expected" and is the way perl works, not really a framework thing to address, except by documentation. So this ticket has 2 action items for me:
We may also want to consider ways to apply mocks as things other than 'before_each' as in your example you are using this as a before_case, there is no current way to do that apart from your way where you manually destroy the object. |
Thanks for the quick, thorough explanation and void workaround. Prominent documentation should go a long way. |
True, but whether they're still needed or not, we can weaken our reference to them once we no longer need them. Right? |
No. When I say the blocks may be used in other places, I mean within the framework itself. Specifically the framework often inspects the coderefs to get their line numbers and filenames. Also you can compose workflows from other workflows that may or may not have already run. We can make no assumptions about when the blocks are no longer needed. |
If
$CLASS
methods are overridden within adescribe()
block, the actual class methods may still be overridden for other tests within the .t. This is particularly nasty because the problem depends on execution order, and the test at cause is not the one that fails. Prior to recognizing this pattern (mock within describe), I fruitlessly triaged many "broken" tests, only to have them "self-correct" over time.If this is intended behavior, it should be very well documented.
Mnemonic: DESCRIBE [D]oesn't [E]ffectively [S]cope [C]lass, [R]esidual [I]nterference [B]eyond [E]nclosure.
Workaround is to undef $class_mock, presumably in an after_each.
May relate to #189.
Demonstration:
The text was updated successfully, but these errors were encountered: