🚀 Feature: Scoped globals
alexreardon opened this issue · comments
Sometimes I write tests that modify a global in a beforeEach
and cleanup restore the global in an afterEach
. Right now this is a violation of the global variable leak detection. To get around this I allow modifications to explicit globals. However, it would be more ideal to allow global 'leaks' within the context of a suite. Something like this:
describe('My test suite', function () {
// same argument type as http://mochajs.org/#globals-names
this.allowGlobalLeaks('foo');
beforeEach(function () {
foo.bar = 5;
});
afterEach(function () {
delete foo.bar;
});
});
describe('My second suite', function () {
beforeEach(function () {
// this would be considered a global leak
foo.bar = 5;
});
});
I'm not a fan of encouraging use of globals. It would be better if we could teach people how to avoid this pattern. That's the whole reason for the global leak detection in the first place
This kind of thing would be really nice for testing simple browser things with injected DOM mocks in Node. Basically, I just want window
and document
to be allowed for two specific files.
Problem is, that's plainly not possible without some really ugly DI that would otherwise be unnecessary (it generally targets a browser environment, so injecting window
and document
is highly redundant). The modules I'm testing are one that renders things to the DOM and the small, but non-trivial DOM mock used for it.
Edit: I worked around this issue by just wrapping it
instead (which I frequently do for various reasons).
I've actually been able to successfully use this.ctx
as a namespace for this kind of variable. I doubt it is something anybody would condone at this point, since this.ctx
is not a documented or intentionally exposed object, but it is perfect for this kind of use-case, because the ctx object gets cloned for each sub-context (actually the parent object is used as the prototype for a new object in the sub-context), so you can set a variable on it, knowing it will not affect parent contexts, but if you don't set a variable, you can rely on the fact that it will be available from the parent context!
This works very similar to how let
works in Ruby's RSpec, with the main difference being that within any given context, the value is a persistent variable (so if you mutate it, you're in trouble) rather than a singleton function. To get around this, you can actually set the value of it to an arrow function, and later call the function i.e.:
context(... {
thix.ctx.currentUser = () => factory.create('user');
it("returns the right user id", function() {
// doesn't matter if this mutates currentUser
expect(response.id).to.equal(this.currentText.ctx.currentUser());
});
});
That is a little cumbersome unfortunately, so THIS is where I think a new feature would be useful. Just some syntactic sugar to allow the easy setting and getting of custom variables (or maybe require them to be functions?) on the ctx object (or a sub-object or separate object that is also "cloned" in the same way). Ideally, it would actually be more than syntactic sugar, and would convert a function into a singleton that is run once, and the result stored and returned upon additional runs within the same test.
This has been status: waiting for author
for quite a few years now. It's also a pretty big idea - which conflicts with our goals in #5027 of not shaking up Mocha anytime soon. Closing as aged away. Cheers!