Skip to content
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

How do you generate dynamic suites with describe()? #4126

Closed
plasticrake opened this issue Dec 13, 2019 · 6 comments
Closed

How do you generate dynamic suites with describe()? #4126

plasticrake opened this issue Dec 13, 2019 · 6 comments
Labels
type: question support question

Comments

@plasticrake
Copy link

I'm trying to dynamically generate suites based on examples I've seen generating tests using it() and addTest(). However using describe() seems to do something behind the scenes that doesn't work the same way.

My actual use case is more complicated. I need to be able to have mySuite() be run as-is, but then also be able to add it to other suites as well. However I can't figure out how to get it inside the context of another suite properly.

How can I place mySuite() inside another describe block while not losing it's context?

function mySuite(name) {
  return describe('mySuite:' + global.value + ': ' + name, function() {
    it('should pass', function() {});
  });
}

describe('ONE', function() {
  let suite = this;
  before(function() {
    global.value = 'ONE';
    suite.addSuite(
      mySuite('this runs the suite twice, and on the second time fails')
    );
  });
  it('', function() {});
});

describe('TWO', function() {
  before(function() {
    global.value = 'TWO';
    mySuite("This runs once but escapes the 'context two' scope");
  });
  it('', function() {});
});

describe('THREE', function() {
  before(function() {
    global.value = 'THREE';
  });
  mySuite("This runs once and in the right context, but I can't access the global");
});

Output:

ONE
    ✓ 
    mySuite:ONE: this runs the suite twice, and on the second time fails
      ✓ should pass

  TWO
    ✓ 

  THREE
    mySuite:undefined: This runs once and in the right context, but I can't access the global
      ✓ should pass

  mySuite:ONE: this runs the suite twice, and on the second time fails
    1) should pass

  mySuite:TWO: This runs once but escapes the 'context two' scope
    ✓ should pass


  5 passing (12ms)
  1 failing

  1) ONE
       mySuite:ONE: this runs the suite twice, and on the second time fails
         should pass:
     TypeError: Cannot read property 'call' of undefined

I can get it to work with mySuite written like this:

function mySuite(name) {
  const suite = Mocha.Suite.create(this, 'mySuite:' + global.value + ': ' + name);
  suite.addTest(it('should pass', function() {}));
  return suite;
}

However I don't like this clunky syntax, and I may have lots of nested suites/describes. Is there a better way?

@plasticrake plasticrake added the type: question support question label Dec 13, 2019
@juergba
Copy link
Contributor

juergba commented Dec 13, 2019

I have never used dynamics suites before, so I don't really know. It's Friday, 13th and my feeling tells me we should not open this coffin, but anyway ...

You should really be sure, that all this effort is worth while. Maybe you could set your various global.value's with the --file option. Like that the values would be known at parse time, which is not the case when you set them in a before hook. Or also by the --delay option.

I'm quite sure your examples will not work using describe, which callback should run at parse time. You can't set the parent suite with describe, and with an additional addSuite you add the same suite a second time (to a different parent).

Your last proposition seems to work, so you have to try and see.

function mySuite(name) {
  const suite = Mocha.Suite.create(this, 'mySuite:' + global.value + ': ' + name);
  suite.addTest(it('should pass', function() {}));
  return suite;
}

describe('ONE', function() {
  let suiteOne = this;
  before(function() {
    global.value = 'ONE';
    mySuite.call(suiteOne, 'this runs the suite once with success');
  });
  it('', function() {});
});
ONE
    √ 
    mySuite:ONE: this runs the suite once with success
      √ should pass

  2 passing (22ms)

@plasticrake
Copy link
Author

Thanks @juergba! I don't particularly like it, but I'm currently passing in a function to mySuite that mySuite executes in it's own before(). I wanted mySuite to be as carefree about what's going on as possible, but this is my workaround. The reason I'm doing this is I have dozens of test files that I'm require()'ing and then setting up various suites with different environments around them. And then I'm also shipping those files off to another software product with it's own mocha runtime and I want to test them WITHOUT doing all the mock-environment setup. So I want clean test suites I can also mess with in my local environment.

@TheSench
Copy link

@plasticrake is there a reason you need to set global.value in a before statement? That is the crux of your problem.

The describe statements run during "test setup", but the before block runs before Mocha runs any of the tests that are inside (but after they have executed any nested describes to do further setup). This means the logic in describe('THREE', ...) does not set global.value until after is has called mySuite.

@stale
Copy link

stale bot commented Apr 17, 2020

I am a bot that watches issues for inactivity.
This issue hasn't had any recent activity, and I'm labeling it stale. In 14 days, if there are no further comments or activity, I will close this issue.
Thanks for contributing to Mocha!

@stale stale bot added the stale this has been inactive for a while... label Apr 17, 2020
@juergba
Copy link
Contributor

juergba commented Apr 17, 2020

Meanwhile I don't think this is a good way to dynamically create tests. Our interface has not been designed for iterative parsing/runner-creation and tests execution.

We should think about extending our programmatic API and exposing some (new) functions like addSuite, addTest, addHook, etc.

@stale stale bot removed the stale this has been inactive for a while... label Apr 17, 2020
@juergba juergba closed this as completed Apr 17, 2020
@lppedd
Copy link

lppedd commented Mar 1, 2024

@juergba hey there! I've landed here after posting a StackOverflow question.

Is the above still the only way to do it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question support question
Projects
None yet
Development

No branches or pull requests

4 participants