mochajs / mocha

☕️ simple, flexible, fun javascript test framework for node.js & the browser

Home Page:https://mochajs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

🚀 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!