Gibt es eine Möglichkeit, Chai mit asynchronen Mocha-Tests zum Laufen zu bringen?

Lesezeit: 6 Minuten

Ich führe einige asynchrone Tests in Mocha mit dem Browser Runner durch und versuche, Chais Erwartungsstilzusicherungen zu verwenden:

window.expect = chai.expect;
describe('my test', function() {
  it('should do something', function (done) {
    setTimeout(function () {
      expect(true).to.equal(false);
    }, 100);
  }
}

Dies gibt mir nicht die normale fehlgeschlagene Assertion-Meldung, stattdessen bekomme ich:

Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
    at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
    at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
    at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)

Es fängt also offensichtlich den Fehler ab, es zeigt ihn nur nicht richtig an. Irgendwelche Ideen, wie man das macht? Ich denke, ich könnte einfach mit einem Fehlerobjekt “fertig” aufrufen, aber dann verliere ich die ganze Eleganz von etwas wie Chai und es wird sehr klobig …

  • Das Problem liegt bei browserseitigem Mocha. Sehen github.com/visionmedia/mocha/pull/278 für Informationen dazu.

    – Elliot Foster

    7. Oktober 2012 um 20:45 Uhr

  • Ab 2020 sollten Sie einen Blick auf die werfen chai-as-promised Plugin…

    – Elmar Zander

    8. September 2020 um 14:13 Uhr

Benutzer-Avatar
Jean Vincent

Ihr asynchroner Test generiert bei Fehler eine Ausnahme expect()ationen, die nicht erfasst werden können it() weil die Ausnahme außerhalb von geworfen wird it()Umfang.

Die erfasste Ausnahme, die angezeigt wird, wird mit erfasst process.on('uncaughtException') unter Knoten oder mit window.onerror() im Browser.

Um dieses Problem zu beheben, müssen Sie die Ausnahme in der asynchronen Funktion erfassen, die von aufgerufen wird setTimeout() um anzurufen done() mit Ausnahme als erster Parameter. Sie müssen auch anrufen done() ohne Parameter zur Anzeige des Erfolgs, sonst würde Mocha einen Timeout-Fehler melden, weil Ihre Testfunktion niemals signalisiert hätte, dass es fertig war:

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function ( done ) {
    // done() is provided by it() to indicate asynchronous completion
    // call done() with no parameter to indicate that it() is done() and successful
    // or with an error to indicate that it() failed
    setTimeout( function () {
      // Called from the event loop, not it()
      // So only the event loop could capture uncaught exceptions from here
      try {
        expect( true ).to.equal( false );
        done(); // success: call done with no parameter to indicate that it() is done()
      } catch( e ) {
        done( e ); // failure: call done with an error Object to indicate that it() failed
      }
    }, 100 );
    // returns immediately after setting timeout
    // so it() can no longer catch exception happening asynchronously
  }
}

Dies bei all Ihren Testfällen zu tun, ist ärgerlich und nicht DRY, daher möchten Sie vielleicht eine Funktion bereitstellen, die dies für Sie erledigt. Nennen wir diese Funktion check():

function check( done, f ) {
  try {
    f();
    done();
  } catch( e ) {
    done( e );
  }
}

Mit check() Sie können Ihre asynchronen Tests jetzt wie folgt umschreiben:

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function( done ) {
    setTimeout( function () {
      check( done, function() {
        expect( true ).to.equal( false );
      } );
    }, 100 );
  }
}

  • Ich habe gerade meinen vorherigen Kommentar gelöscht, nachdem mir klar wurde, dass der Teil, über den ich mich beschwert habe (setTimeout), tatsächlich von meiner Frage stammt. Es tut uns leid!!

    – Thomas Parlow

    7. März 2013 um 13:31 Uhr

  • Die obige Antwort scheint falsch zu sein. Eine fehlgeschlagene Erwartung wirft sofort und stoppt den Test mit einem bedeutsamen Fehler, ein kompliziertes Try/Catch ist nicht erforderlich. Ich habe es gerade mal mit einem Browsertest getestet.

    – Firmmo

    6. August 2015 um 10:37 Uhr


  • Ich hatte mit diesem Problem zu kämpfen und fand diesen Blogbeitrag äußerst hilfreich: staxmanade.com/2015/11/…

    – RichardForrester

    18. Dezember 2015 um 20:27 Uhr


  • @RichardForrester, äußerst hilfreicher Beitrag. Vielen Dank! Diese Überprüfung mit Promises zum Laufen zu bringen, vereinfacht den Code unglaublich. Aber es muss mit Versprechungen sein (keine asynchrone Funktion).

    – Pedro R.

    2. September 2016 um 9:38 Uhr

  • Ich möchte nur für die Nachwelt einstimmen, dass genau dieses Problem mit dem Vue nexttick() (das ein Wrapper für Promise ist) auftritt und auf die gleiche Weise gehandhabt werden kann.

    – Eli Albert

    19. April 2017 um 3:34 Uhr

Benutzer-Avatar
RichardForrester

Hier sind meine bestandenen Tests für ES6/ES2015 Promises und ES7/ES2016 async/await. Ich hoffe, dies bietet eine nette aktualisierte Antwort für alle, die zu diesem Thema forschen:

import { expect } from 'chai'

describe('Mocha', () => {
  it('works synchronously', () => {
    expect(true).to.equal(true)
  })

  it('works ansyncronously', done => {
    setTimeout(() => {
      expect(true).to.equal(true)
      done()
    }, 4)
  })

  it('throws errors synchronously', () => {
    return true
    throw new Error('it works')
  })

  it('throws errors ansyncronously', done => {
    setTimeout(() => {
      return done()
      done(new Error('it works'))
    }, 4)
  })

  it('uses promises', () => {
    var testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    testPromise.then(result => {
      expect(result).to.equal('Hello')
    }, reason => {
      throw new Error(reason)
    })
  })

  it('uses es7 async/await', async (done) => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    try {
      const result = await testPromise
      expect(result).to.equal('Hello')
      done()
    } catch(err) {
      done(err)
    }
  })

  /*
  *  Higher-order function for use with async/await (last test)
  */
  const mochaAsync = fn => {
    return async (done) => {
      try {
        await fn()
        done()
      } catch (err) {
        done(err)
      }
    }
  }

  it('uses a higher order function wrap around async', mochaAsync(async () => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    expect(await testPromise).to.equal('Hello')
  }))
})

  • @Pedro R. Ich habe geändert, um erledigt aus dem Versprechungstest zu entfernen. Wie du schon sagtest, ist es nicht nötig.

    – RichardForrester

    8. September 2016 um 22:28 Uhr

Benutzer-Avatar
ichdenke

Wenn es Ihnen wie versprochen gefällt, probieren Sie es aus Chai wie versprochen + Qdie so etwas erlauben:

doSomethingAsync().should.eventually.equal("foo").notify(done);

Ich habe dasselbe in der Mocha-Mailingliste gefragt. Sie haben mir im Grunde Folgendes gesagt: einen asynchronen Test mit Mocha und Chai zu schreiben:

  • Beginnen Sie den Test immer mit if (err) done(err);
  • Beenden Sie den Test immer mit done().

Es löste mein Problem und änderte zwischendurch keine einzige Zeile meines Codes (unter anderem Chai-Erwartungen). Das setTimout ist nicht der Weg, asynchrone Tests durchzuführen.

Hier ist die Link zur Diskussion in der Mailingliste.

Ich habe ein Paket veröffentlicht, das dieses Problem behebt.

Installieren Sie zuerst die check-chai Paket:

npm install --save check-chai

Verwenden Sie dann in Ihren Tests chai.use(checkChai); und benutze dann die chai.check Hilfsfunktion wie unten gezeigt:

var chai = require('chai');
var dirtyChai = require('dirty-chai');
var checkChai = require('check-chai');
var expect = chai.expect;
chai.use(dirtyChai);
chai.use(checkChai);

describe('test', function() {

  it('should do something', function(done) {

    // imagine you have some API call here
    // and it returns (err, res, body)
    var err = null;
    var res = {};
    var body = {};

    chai.check(done, function() {
      expect(err).to.be.a('null');
      expect(res).to.be.an('object');
      expect(body).to.be.an('object');
    });

  });

});

Per Gibt es eine Möglichkeit, Chai mit asynchronen Mocha-Tests zum Laufen zu bringen? Ich habe dies als NPM-Paket veröffentlicht.

Bitte sehen https://github.com/niftylettuce/check-chai für mehr Informationen.

Benutzer-Avatar
Manil

Versuchen Sie chaiAsPromised! Abgesehen davon, dass Sie einen hervorragenden Namen haben, können Sie Aussagen verwenden wie:

expect(asyncToResultingValue()).to.eventually.equal(true)

Kann bestätigenfunktioniert sehr gut für Mokka + Chai.

https://github.com/domenic/chai-as-promised

Benutzer-Avatar
Gemeinschaft

Sehr verwandt mit und inspiriert von Jean Vincents Antwort verwenden wir eine ähnliche Hilfsfunktion wie er check Funktion, aber wir nennen es eventually stattdessen (dies hilft, mit den Namenskonventionen von chai-as-promised übereinzustimmen). Es gibt eine Funktion zurück, die eine beliebige Anzahl von Argumenten entgegennimmt und sie an den ursprünglichen Callback übergibt. Dadurch wird ein zusätzlicher verschachtelter Funktionsblock in Ihren Tests eliminiert und Sie können jede Art von asynchronem Callback verarbeiten. Hier ist es in ES2015 geschrieben:

function eventually(done, fn) {
  return (...args) => {
    try {
      fn(...args);
      done();
    } catch (err) {
      done(err);
    }
  };
};

Beispielnutzung:

describe("my async test", function() {
  it("should fail", function(done) {
    setTimeout(eventually(done, (param1, param2) => {
      assert.equal(param1, "foo");   // this should pass
      assert.equal(param2, "bogus"); // this should fail
    }), 100, "foo", "bar");
  });
});

1129000cookie-checkGibt es eine Möglichkeit, Chai mit asynchronen Mocha-Tests zum Laufen zu bringen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy