简体   繁体   中英

Dynamic asynchronous mocha tests

I have a service class, which 50 other "services" extend. While each service has its own test, I would like to write a test suite for all of the services shared functionality (I have a method thing which every service has to implement).

To test thing , every service also has a function thingConfig which returns an array of configurations thing can run in. I would like to do the following:

describe('service', () => {
  let configs;
  before(async () => configs = await service.thingConfig())

  configs.forEach(config => {
    it(config.name + ' should run thing', () => {
      thing = await service.thing(config);
      expect(thing).to....
    });
  });
})

Is it possible to make this dynamic tests (forEach) based on asynchronous data?

In order to make it work, you have to make some dummy it case.

Look at the following example:

describe('Dummy spec', () => {
    before(async () => {
        const configs = await service.thingConfig();
        describe('Generated spec', () => {
            configs.forEach((config) => {
                it(`Test for config: ${config}`, async () => {
                    const thing = await service.thing(config);
                    expect(thing).to....
                });
            });
        });
    });

    it('Dummy test case, so before is executed', () => assert.strictEqual(1, 1));
});

Assuming that everything went well, you should see results in following form:

  Dummy spec
    √ Dummy test case, so before is executed

  Generated spec
    √ Test for config: test1
    √ Test for config: test2
    √ Test for config: test3
    √ Test for config: test4


  5 passing (3s)

Since this seems to be still an issue in mocha (The delay flag breaks parallel mode and does not play well with TypeScript) I have been using a still hacky, but slightly more structured approach:

function asyncSuite(name: string, setupSuite: () => Promise<Suite>) {
  suite(`Async dummy Suite for ${name}`, function () {
    let suite: Suite;

    suiteSetup(async () => (suite = await setupSuite()));

    test(`Async tests for ${name} loaded`, async function () {
      assert.ok(suite.suites.length || suite.tests.length);
    });
  });
}

Asynchronously added suites are never attached to their parents, as the suite tree is built synchronously by mocha. This approach makes that a bit clearer and forces you to create a suite for your asynchronously added data.

Additionally it tests for added tests and suites, preventing a silent fail when no tests are dynamically added. Usage is fairly simple, although you do have one extra indentation level:

asyncSuite('async tests', async function () {
  const values = await new Promise<string[]>((r) => r(['abcd', 'efg', 'hijk']));

  return suite('Asynchronous Test Suite', function () {
    for (const value of values) {
      test(`${value} is length 4`, () => {
        assert.strictEqual(value.length, 4);
      });
    }
  });
});

This will result in one dummy suite that is used to load the asynchronous dynamic tests in it's setup function and the actual test suite:

mocha 测试输出示例

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM