简体   繁体   中英

Why does Mocha not execute tests found in .then statements?

I'm working on a Protractor test suite using Mocha/Chai (and yes, I get the same problem in Jasmine).

Because the app is rather complicated, I'd like to be able to dryly set up test suites that allow me to chain together operations into functions. (Ie, "login, then browse to [parameterX], then browse to [parameterY], then expect first post title to be [parameterZ]).

But I seem to be having trouble with getting Mocha to run tests when I put them inside a .then() statement.

Here's a small code snippet which shows the behavior I mean.

var chai = require('chai');
var cAP = require('chai-as-promised')
chai.use(cAP);
const expect = chai.expect;

const doTest = (x) => {
  describe('main', function() {
    it('should return foo', function(done) {
      expect(x).to.equal('foo')
    })
  })
}

const wait = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve(), 10000)
})

wait()
  .then(() => doTest('foo'))

/* results in
 *   0 passing (4ms)
 */

describe and it blocks may not be done asynchronously. If you want to wait 10sec before your tests execute, you should use a before block, which supports async execution. Your before callback takes one arg which is a done callback. Inside this block you can wait 10sec and then call done. All subsequent blocks will wait until done is called from within the before block.

describe('your tests', () => {
    before(done => {
        setTimeout(done, 10000)
    })
    it('should return foo', () => {
        expect(x).to.equal('foo')
    })
})

The issue is that you are creating your test suite asynchronously. By default Mocha does not allow creating a suite asynchronously. I took your code and made the following changes:

  1. Removed the done parameter from the callback passed to it because it is useless.

  2. I added a call to run at the end of doTest .

  3. I invoke Mocha with the --delay option: mocha --delay .

The --delay option will make Mocha wait for the test suite to be built. You indicate that you are done building it by calling run() . Here's the full code with the changes mentioned above:

var chai = require('chai');
var cAP = require('chai-as-promised')
chai.use(cAP);
const expect = chai.expect;

const doTest = (x) => {
  describe('main', function() {
    it('should return foo', function() {
      expect(x).to.equal('foo')
    })
  })

  run();
}

const wait = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve(), 10000)
})

wait()
  .then(() => doTest('foo'))

chai-as-promised can make it a little bit cleaner

/**
 * Modified by cool.blue on 26-Aug-16.
 */
'use strict';
var chai = require('chai');
var cAP = require('chai-as-promised');
chai.use(cAP);
const should = chai.should();

const wait5 = (something) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(something + 5), 5000)
});

const wait10 = (something) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(something + 10), 10000)
});

const doTest = (x, y, prep) => {
  describe('main', function() {
    this.timeout(15000);
    it('should return the right number for ' + x + ' and ' + y, function() {
      return prep(y).should.become(x)
    })
  })
};

[[17, 12, wait5], [15, 5, wait10], [15, 5, wait5]].forEach((listing) => doTest(...listing));

But, be careful of the arrow functions: they treat statements and expressions differently.

    it('should return the right number for ' + x + ' and ' + y,
      () => prep(y).should.become(x)
    )

is completely different from

it('should return the right number for ' + x + ' and ' + y, () => {
  prep(y).should.become(x)
})

but exactly the same as

it('should return the right number for ' + x + ' and ' + y, () => {
  return prep(y).should.become(x)
})

Arrow functions make me nervous, which is why I prefer...

it('should return the right number for ' + x + ' and ' + y, function() {
  return prep(y).should.become(x)
})

Well, I couldn't find an exact answer, but I was able to find something that kinda will work for what I really need this for, and so, here's what I did.

Essentially, all the other asynchronous actions I can pass in as a parameter to the testing function, and can use the before block's done() callback to make sure they are executed at the correct time.

const wait5 = (something) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(something + 5), 5000)
})

const wait10 = (something) => new Promise((resolve, reject) => {
  setTimeout(() => resolve(something + 10), 10000)
})

const doTest = (x, y, prep) => {
  describe('main', function() {
    let z;
    before(function (done) {
      this.timeout(30000);
      prep(y).then((val) => {
        z = val;
        done();
      })
    })
    it('should return the right number for ' + x + ' and ' + y, () => {
      expect(x).to.equal(z)
    })
  })
}

[[17, 12, wait5], [15, 5, wait10], [15, 5, wait5]].forEach((listing) => doTest(...listing))


/* Result: 

  main
    √ should return the right number for 17 and 12

  main
    √ should return the right number for 15 and 5

  main
    1) should return the right number for 15 and 5


  2 passing (20s)
  1 failing

  1) main should return the right number for 15 and 5:

      AssertionError: expected 15 to equal 10
      + expected - actual

      -15
      +10
 */

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