Mocha: Kesalahan: Metode resolusi ditentukan secara berlebihan.

Dibuat pada 2 Agu 2016  ·  84Komentar  ·  Sumber: mochajs/mocha

Ini:

before(done => {
    return Promise
        .all([])
        .then(() => Model.insert(...)) // Bookshelf model returning a Bluebird Promise
        .then(() => done())
        .catch(done)
})

akan menghasilkan kesalahan Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

Dokumen mengatakan:

Di Mocha v3.0.0 dan yang lebih baru, mengembalikan Promise dan memanggil done () akan menghasilkan pengecualian, karena ini biasanya merupakan kesalahan:

Panggilan model diselesaikan dengan Promise.<Object> dari entri yang baru dimasukkan, namun jika saya menghilangkan .then(() => done()) maka batas waktu pengujian.

confirmed-bug

Komentar yang paling membantu

async functions (babel) dengan done juga putus.

Semua 84 komentar

async functions (babel) dengan done juga putus.

Seperti yang dinyatakan oleh kesalahan, Anda tidak seharusnya menyediakan fungsi dengan arity> 0 (artinya menerima callback done ), _and_ mengembalikan Promise.

Cara termudah untuk memperbaikinya adalah dengan menghilangkan return , tetapi karena Anda menggunakan promise, saya sarankan untuk menghapus callback done , karena akan menghasilkan konstruksi yang jauh lebih sederhana :

before(() => Promise.all([]).then(() => Model.insert(...)));

Berikut adalah contoh async (pada dasarnya janji) dan done yang rusak:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

@elado Itu kasus penggunaan yang menarik, meskipun dari sudut pandang mocha situasinya sama - sebuah fungsi yang mengambil callback done dan mengembalikan sebuah promise. Saya akan menulis ulang menjadi sepenuhnya berdasarkan janji:

it('fires change event when calling blah.doSomethingThatFiresChange', async function () {
  const blah = await getBlah()
  return new Promise(resolve => {
    blah.on('change', resolve)
    blah.doSomethingThatFiresChange()
  })
})

... tapi saya rasa apa yang Anda maksud adalah bahwa dalam contoh ini, akan lebih baik jika moka menunggu _both_ janji diselesaikan dan panggilan balik dipanggil?

Sayangnya kombo tersebut paling sering merupakan bug dalam pengujian, itulah sebabnya pesan kesalahan ini ditambahkan sejak awal.

... jika saya menghilangkan. lalu (() => selesai ()) maka batas waktu pengujian.

Bagaimanapun, ini sepertinya bug.

@ScottFreeCode Hmm, ya, tampaknya karena kesalahan "ditentukan secara berlebihan" dalam fungsi yang disediakan sebagai panggilan balik done : https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib/ runnable.js # L357 -L373

... tetapi tentu saja kita dapat menentukan bahwa kita harus gagal dengan kesalahan itu segera setelah fungsinya kembali.

@ScottFreeCode Apa perbaikannya di sini?

Saya akan menebaknya "tunggu janji diselesaikan atau ditolak, lalu tolak dengan kesalahan 'ditentukan secara berlebihan'"?

Jika kita akan menganggap done plus janji dalam pengujian yang sama sebagai kesalahan, saya tidak melihat alasan untuk tidak mendeteksi kesalahan segera setelah fungsi pengujian yang mengambil done mengembalikan a janji, seperti yang disarankan @papandreou . Tidak masuk akal bagi saya untuk mencoba mencari tahu poin lain apa yang harus memicu kesalahan kecuali kita bermaksud untuk mengizinkan promise dan done bersama-sama dalam beberapa kasus.

@ScottFreeCode Saya setuju. Jadi begitu

  1. Deteksi masalah; membuat instance Error tetapi jangan panggil done() dengannya
  2. Tunggu sampai janji terpenuhi
  3. Tolak dengan Error

Pertanyaan bonus: Apa yang harus dilakukan dengan hasil pemenuhan Janji?

Ah, saya rasa saya mengerti - bahkan ketika kami mendeteksi kesalahan, kami harus membiarkan pengujian berjalan agar kode tersebut tidak tetap berjalan selama pengujian berikutnya dan, mungkin, agar kami juga dapat melaporkan hasil pengujian.

Mungkinkah tes juga akhirnya memanggil done tanpa menyelesaikan atau menolak janji? Jika demikian, itu kasus akhir lain yang harus kita tangani.

Kecenderungan saya, kembali. apa yang harus dilakukan dengan hasil tes, adalah jika waktu habis (tanpa _either_ memanggil done atau menyelesaikan / menolak janji) kita harus melaporkan penggunaan done dengan janji (karena kebingungan atas kombinasi tersebut bisa jadi menjadi alasan mengapa kombinasi tersebut tidak pernah mencapai salah satu dan malah habis waktunya), tetapi jika berhasil diselesaikan dengan cara apa pun maka kemungkinan hasilnya valid (atau setidaknya bermakna) dan kami harus melaporkan baik kemungkinan kesalahan penggunaan done dan janji bersama serta hasil tes dengan harapan setidaknya bisa membantu.

Bagaimanapun, itulah yang terbaik yang dapat saya pikirkan saat ini, tetapi saya mungkin memiliki lebih banyak wawasan jika saya dapat menemukan waktu untuk menggali masalah ini.

Nah, kita bisa melakukan ini secara bertahap. Yang pertama adalah memastikan bahwa jika Promise dikembalikan dan done callback diberikan, maka Mocha akan rusak dengan ramah.

Sangatlah _ dapat dipercaya_ bahwa seseorang dapat melakukan sesuatu seperti ini (dengan Bluebird):

// to make this work, you need to omit `return`
it('should do something async for sure', function(done) {
  return somethingAsync()
    .then(makeAssertion)
    .asCallback(done);
});

Tapi itu hanya sesuatu yang Anda _could_ lakukan; Saya belum menentukan kasus penggunaan untuk ini.

Saya pikir saya telah bingung sendiri. Saya sama sekali tidak yakin ada bug di sini.

Ini lebih merupakan masalah jenis "UI yang buruk"

Waktu tunggu hingga done dipanggil meskipun janji yang dikembalikan diselesaikan / ditolak jelas merupakan bug, terlepas dari apakah kita ingin melarang pengujian semacam itu menggunakan done dan janji bersama di awal tempat. Ini harus menggunakan hasil dari promise dan / atau error karena promise dan done keduanya digunakan dalam pengujian yang sama, tetapi bukan hanya waktu habis karena salah satu dari keduanya tidak pernah selesai ketika yang lain melakukannya (yang mana adalah apa yang dilakukannya saat ini):

it("should not time out", function(done) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve.bind(42), 50)
  })
})

1) tidak boleh waktu habis:
Galat: batas waktu 2000 md terlampaui. Pastikan callback done () dipanggil dalam pengujian ini.

Bagaimanapun, saya akan melihat PR ...

Pihak yang tertarik harus menonton # 2413.

@briansipple

Saya mendapatkan kesalahan yang sama menggunakan beberapa async beforeEach hooks di versi 3:

  beforeEach((cb) => connection.db.collection('users').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').insertOne(account1, cb));

Apakah tidak mungkin untuk tetap menggunakan kait asinkron dengan cara itu?

Saya menemukan masalah saya: metode saya di dalam setiap hook mengembalikan promise (Saya tidak tahu itu karena saya tidak menggunakan promise dalam kasus tersebut) dan jelas saya juga meneruskan fungsi cb .

Saya rasa ini bukan ide yang bagus. Jika seseorang meminta panggilan balik, saya pikir mereka telah membuat niatnya jelas. Kesalahan di sini hanyalah gangguan. Tolong hapus itu.

Ini sebenarnya bukan masalah niat. Dari deskripsi # 1320:

Saat fungsi callback ditentukan dan objek Promise ditetapkan
dikembalikan, kondisi resolusi Runnable ambigu.

Itu tidak ambigu, panggilan balik itu diminta. Bagaimana itu bisa ambigu?

Saya setuju dengan @RobertWHurst itu tidak ada yang ambigu di sini.

Selain itu menurut saya masalah ini bisa menjadi sesuatu yang "berbasis opini" dan pengembang akan memiliki sudut pandang yang berbeda. Saya menganggap itu sangat ekstrim untuk melakukan perubahan besar dan memaksa orang menggunakan cara itu.

Kami tidak memaksa_ siapa pun untuk melakukan apa pun. Kami merilis sebuah jurusan, yang menurut definisi akan memiliki perubahan yang dapat merusak. Kami tidak melanggar semver.

Saya tidak berpikir dia berarti Anda melanggar setengah, saya pikir dia berarti Anda melanggar Mocha. Perubahan ini tidak membuat segalanya lebih mudah bagi pengembang, ini memaksakan opini.

beforeEach((cb) => connection.db.collection('users').remove({}, cb));
            ^^

^^ Itu tidak ambigu. Cukup jelas apa yang diharapkan oleh penulis. Penulis telah berusaha keras untuk meminta panggilan balik.

Sebenarnya alasan Anda mendapatkan pengecualian di sini adalah penghindaran maksimal dari penegakan opini. Mocha tidak memiliki pendapat tentang apakah janji yang dikembalikan atau panggilan balik itu berwibawa. Anda tidak dapat memiliki keduanya pada saat yang sama karena itu mengarah pada hasil yang ambigu. Hasil yang ambigu dalam kerangka pengujian harus dianggap sebagai kesalahan. Karenanya pesan kesalahan untuk membantu Anda menyadari hal ini dan membuat pilihan dan perubahan yang sesuai dengan pendapat Anda.

Mungkin bermanfaat untuk mengurangi jumlah drama. "Anda melanggar Mocha" tidak membantu siapa pun. Peningkatan versi semver mayor secara eksplisit didefinisikan sebagai perubahan yang dapat menyebabkan gangguan yang mungkin mengharuskan Anda menyesuaikan kode. Anda dapat tetap menggunakan 2.x untuk memberi Anda waktu untuk melakukan perubahan guna memperbaiki pengujian Anda. Ini adalah evolusi, bukan kerusakan

@ Munter Saya masih bingung tentang bagaimana meminta panggilan balik itu ambigu. Jika Anda meminta panggilan balik, Anda diharapkan untuk menggunakannya. Jika tidak, itu adalah kesalahan pemrograman. Ini adalah tindakan eksplisit oleh penulis tes.

Jika Anda merasakan drama, maksud saya tidak ada. "Anda melanggar Mocha" tidak dimaksudkan sebagai hiperbolik. Saya hanya berpikir ini bertentangan dengan desain modul dan melanggar kontrak api asli.

Seperti yang telah meintoned sebelum babel async / await tidak berfungsi dengan baik dengan mocha baru

it('should fill userName and password', async function (done) {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    try {
      assert.equal(userNameVal, 'admin');
      assert.equal(passwordVal, 'changeme');
    } catch (error) {
      done(error);
      return;
    }

    done();
  });

Kode ini bekerja dengan baik dengan mocha @ 2 tetapi tidak dengan mocha @ 3 karena di await itu mengembalikan janji

Saya pikir PR ini relevan di sini => https://github.com/mochajs/mocha/pull/2413
Lebih rumit untuk menangani kasus tepi kesalahan ini.

@artyomtrityak Itu adalah contoh yang bagus dimana done tidak diperlukan.

Para pencela telah mengatakan bagian mereka. Namun, pengelola Mocha tidak setuju dengan argumen untuk mengembalikan perubahan ini. Eran Hammer berkata (diparafrasekan), "Sebagai pengelola, salah satu hal tersulit yang dapat Anda lakukan adalah mengecewakan mereka yang ingin mengalihkan pekerjaan ke arah mereka."

Saya dipersilakan untuk menyelesaikannya - lebih banyak dokumentasi (misalnya lebih banyak contoh kesalahan ini dan cara memperbaikinya), pesan kesalahan yang lebih baik - tetapi saya tidak tertarik pada drama, kekasaran, atau keluhan. Berkontribusi salah satu solusi ini untuk Mocha akan membantu mengubah negatif menjadi positif.

Jika Anda tidak menyukai perubahan ini, dan tidak bisa bersikap konstruktif tentangnya, ini adalah OSS - Anda dapat melakukan fork proyek dan mengembalikan perubahan di sana.

@boneskull itu berubah menjadi fungsi async yang mengembalikan janji, saya tidak perlu menyelesaikan kasus uji saya ketika janji akan terpenuhi tetapi perlu melakukan beberapa pemeriksaan khusus di sekitar hasil. Seperti yang saya katakan, kode ini berfungsi dengan baik dengan mocha @ 2 tetapi dengan mocha @ 3 tidak. Jadi tim saya (~ 20ppl) tidak bisa pindah ke mocha terbaru karena ini.

Saat ini mocha 2.x memberikan banyak fleksibilitas, adakah alasan teknis untuk perubahan ini?

@artyomtrityak Itu adalah contoh bagus di mana dilakukan tidak perlu.

Bisakah Anda memberi contoh bagaimana ini akan terlihat dengan babel async/await sintaks dan tanpa return new Promise ?

@artyomtrityak Bagaimana kalau menepati janji sepenuhnya? Kemudian Anda dapat mempersingkat tes Anda menjadi:

it('should fill userName and password', async function () {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    assert.equal(userNameVal, 'admin');
    assert.equal(passwordVal, 'changeme');
  });

Jika Anda merasa diserang secara pribadi, maka menurut saya Anda perlu mempertimbangkan kembali seberapa besar investasi Anda dalam percakapan ini. Tidak ada komentar yang bersifat pribadi. Saya yakin Anda semua hebat, terutama mengingat Anda telah menyumbangkan waktu Anda untuk membantu memelihara proyek ini yang sangat dihargai dan sangat terpuji.

Sebagian besar dari Anda (pengelola yang saat ini aktif) mulai mengerjakan Mocha sekitar pertengahan 2014. Mocha sudah terbentuk pada saat kalian mulai berkontribusi. Itu hanya pendapat saya, tetapi saya tidak berpikir saya sendirian dalam berpikir bahwa seseorang tidak boleh membuat perubahan besar pada perpustakaan yang sudah mapan kecuali itu dibenarkan. Meskipun saya dapat membayangkan pembenaran asli untuk perubahan ini, itu tidak berlaku dengan baik ketika seseorang menunjukkan hal berikut. Meminta panggilan balik mengomunikasikan maksud yang jelas. Janji tidak sejelas karena tidak diminta, janji dikembalikan, yang dapat terjadi secara tidak langsung dan tidak sengaja (dikembalikan dari pustaka pihak ketiga misalnya). Karena perbedaan ini, kedua cara menghasilkan tidak sama, dan dengan demikian mencoba menggunakan keduanya tidak terlalu ambigu. Callback harus dituliskan ke dalam argumen pengujian. Anda tidak dapat melakukannya dengan janji, dan dengan meminta panggilan balik, Anda telah mengomunikasikan niat Anda secara eksplisit. Komunitas Anda menyuarakan keprihatinan ini, dan alih-alih mengakui kesalahan langkah, Anda malah melipatgandakannya. Tampaknya Anda bahkan mempertimbangkan untuk memaksa pengujian menjadi asinkron untuk memastikan kesalahan ini bekerja secara konsisten. Lihat => https://github.com/mochajs/mocha/pull/2413. Sepertinya perubahan besar untuk pesan kesalahan yang melindungi dari kesalahan yang tidak mungkin terjadi.

Kalian telah melakukan pekerjaan yang bagus dalam memelihara perpustakaan ini sejak kepergian @tj , bisakah Anda memikirkan lebih banyak tentang perubahan ini. Kekhawatiran saya adalah ini dapat membahayakan perpustakaan.

Sangat setuju dengan @RobertWHurst.

Meminta done harus menimpa perilaku janji yang dikembalikan. Kemungkinan tidak akan meminta done bila tidak diperlukan, dan skenario acara yang dipancarkan dalam fungsi async adalah contoh sempurna.

Dari komentar saya di atas:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

Karena semakin banyak orang pindah ke ES6 / 7 + async / await , ini akan menjadi gotcha umum saat menggunakan Mocha.

Harap pertimbangkan kembali perubahan ini.

@RobertWHurst Anda berpendapat bahwa mendefinisikan callback done adalah maksud eksplisit. Apakah pernyataan return bukan merupakan maksud eksplisit? Keduanya ditentukan dalam kode Anda oleh Anda. Bagaimana kami dapat memutuskan bahwa satu bagian kode Anda disengaja dan yang lainnya tidak? Jika Anda membayangkan dunia sebelum () => foo pernyataan pengembalian apa pun akan selalu eksplisit. Satu-satunya alasan Anda angkat senjata sekarang adalah karena Anda sudah mulai menggunakan pernyataan pengembalian implisit, karena apa yang saya hanya bisa anggap sebagai alasan estetika.

Mengingat sebagian besar penggunaan Mocha dilakukan oleh pemula yang biasanya menyalin / menempel contoh, yang kemungkinan besar berisi callback done , bagaimana Anda menangani kasus di mana pengguna baru ini secara eksplisit mengembalikan sebuah janji, tetapi mendapatkan waktu tunggu? Ini adalah hasil jika perubahan yang membuat Anda marah dikembalikan.

Perilaku saat ini jauh lebih jelas tentang apa yang salah daripada hanya waktu tunggu yang tidak terduga

@Munter Dengan async dalam gambar, saya pikir nilai janji yang dikembalikan lebih rendah pada skala eksplisit karena dibuat dan dikembalikan secara otomatis, baik Anda menggunakan await :

it('should work with a async function that could have been sync', async function () {
  assert.ok(true);
});

it('should work with a legitimately async function', async function () {
  assert.equal(await getBlah(), 'blah');
});

it('should work with a fully spelled-out Promise-based test', function () {
  return getBlah().then(function (blah) {
    assert.equal(blah, 'blah');
  });
});

Dan kemudian ada yang kontroversial:

it('should foo', async function (done) {
  assert.equal('foo', 'foo');
  done();
});

Juga mudah bagi yang mungil-kecilnya async untuk menyelinap masuk secara tidak sengaja, jadi kita harus memikirkan (setidaknya) contoh pertama sebagai jenis gotcha baru, seperti yang ditunjukkan @elado .

Setelah sedikit percakapan di sini => https://github.com/mochajs/mocha/pull/1320, saya punya ide untuk solusi alternatif untuk masalah tersebut. Saya telah menambahkan PR untuk kesenangan meninjau Anda di sini => https://github.com/mochajs/mocha/pull/2454

:bir:

Anda berpendapat bahwa mendefinisikan callback yang dilakukan adalah maksud eksplisit. Apakah pernyataan pengembalian bukan niat eksplisit?

@Munter Jangan lupa bahwa coffeescript dan ekspresi fungsi panah es6 mengembalikan _implicitly_, sehingga Anda dapat melakukan sesuatu seperti

it 'should foo', (done) -> request('/foo').end (err, res, body) -> done()

dan berpikir Anda aman. Tetapi masalah ini berarti Anda harus mengubah satu baris yang bagus itu menjadi sesuatu seperti

it 'should foo', (done) ->
  request('/foo').end (err, res, body) -> done()
  return

Ini persis masalah kami dengan basis kode keseluruhan kami di masing - mecano . Pertimbangkan ini:

it 'should foo', (done) ->
  obj =
    run: ->
      done()
      @
    then: -> "what the hell, i'm not a promise"
  obj.run()

Ini tidak sepenuhnya spesifik untuk coffeescript tetapi mengembalikan nilai implisit membuatnya menjadi lebih buruk. Mocha harus mendeteksi contoh janji yang valid. Juga, mungkin sebuah opsi dapat menonaktifkan fitur ini.

Hai semua,
Mengalami masalah ini dengan kode berikut;

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', (done) ->
      chai.request(app).get('/').end((err, res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
        done()
      )
    )
  )
)

Selesaikan masalah ini dengan mematuhi rantai perjanjian dan menghilangkan callback done() .

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', ->
      chai.request(app).get('/').then((res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
      )
    )
  )
)

Saya harap ini membantu orang lain, yang mengalami kesalahan serupa: tersenyum:

PS : hati: Mocha btw: +1:

EDIT Hapus catch() berdasarkan komentar @Munter .

@karlbh Terima kasih. Anda tidak membutuhkan .catch . Janji yang ditolak dihitung sebagai kesalahan oleh Mocha

@ Munter begitu, terima kasih: smiley:

Setelah memikirkannya sedikit lagi, saya sangat menyukai perilaku menunggu janji dan panggilan balik. Apakah ada kemajuan yang dibuat dengan menerapkannya?

@ light24bulbs Terima kasih atas tanggapannya! Tidak, afaik tidak ada yang mulai mengerjakan itu karena itu hanya ide yang saya lemparkan ke sana untuk melihat reaksinya. Saya baru saja memeriksa apakah ada umpan balik tambahan tentang gagasan tersebut, setiap kasus yang tidak berhasil, dll.

Apakah ada solusi untuk ini saat menggunakan babel?

Apakah ada solusi untuk ini saat menggunakan babel?

Saya membungkus dua kali:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

@SaschaNaz terima kasih, ini berfungsi di v3.2.0 :)

6 bulan kemudian, masalah ini masih mengerem semua tes js modern ...
malu

@benfavre Terima kasih atas kata-kata penyemangat yang pasti akan memotivasi relawan untuk mengambil tanggung jawab dalam menerapkan solusi apa pun yang belum Anda tentukan di waktu luang mereka daripada bermain dengan anak-anak mereka

@ Munter Jangan khawatir senang saya bisa Membantu mengidentifikasi masalah tertentu yang saya hadapi sebagai pengguna baru mochajs.
@SaschaNaz menyarankan solusi pembungkus dua kali tidak membantu.

=> Menggunakan promise secara eksklusif berhasil sebagaimana mestinya.

Lain kali saya kira saya hanya harus "+1" seperti orang bodoh jadi saya tidak dihina secara gratis.
Tidak ada yang menghina dalam pesan saya, apalagi pernyataan saya tetap benar.

Kebanyakan orang hanya akan memilih kerangka kerja lain ... untuk saat ini, kerangka itu hanya rusak dengan async / menunggu, tanpa indikasi yang jelas di mana pun di situs utama, dan tidak ada pesan kesalahan yang jelas di cli.

Selamat tahun baru dan bersenang-senang bermain dengan anak-anak.

Dingin...

Diskusi berkembang menjadi "memperhatikan" baik callback done dan Promise dikembalikan: https://github.com/mochajs/mocha/issues/2509

Penutupan. lihat # 2509

Jika beberapa tubuh masih berjuang ...

Apakah ada solusi untuk ini saat menggunakan babel?

Sekarang dengan penangan bawaan untuk janji seperti yang disebutkan di # 2509, kita tidak perlu menggunakan wrapping twice hack seperti ini:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

Sebagai gantinya, kita bisa pergi dengan ini:

it("should work", async () => {
  await something;
});

Lihat posting ini untuk detail lebih lanjut

@ lo-tp
1.) kode ini bekerja ketika dikompilasi dengan babel?

it("should work", async () => {
  await something;
});

@Serkanmakassar

  • Ya, untuk menggunakan sintaks async/await sekarang, kita membutuhkan bantuan babel.

Tidak yakin apakah saya melewatkan sesuatu ... tetapi saya memecahkan masalah ini dengan tidak menggunakan pernyataan pengembalian apa pun dengan Janji saya dan hanya mengandalkan done ().

Inilah contoh yang berhasil untuk saya

  describe('STRIPE CHARGES: Get Charges', () => {
    it('should list customer charges', async () => {
      const charges = await Stripe.getChargesList(customerObj);
      charges.data[0].should.have.property('id');
      charges.data[0].amount.should.have.property('id');
    });
  });

Saya telah menggunakan mocha 4 dengan chai.assert.

Awalnya saya mencoba menggunakan callback done () seperti itu.

it('should throw an error', async function(done) {
  try {
    result = await something('value that causes something() to throw an Error');
    done('failed');
  } catch (e) {
    done();
  }
});

Itu gagal dengan

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both."

Kesalahan itulah yang membawa saya ke sini, ke halaman ini. Dari mengarungi utas yang agak panjang ini (dan saya akui saya tidak sepenuhnya memahami semua perselisihan), apakah saya memahami bahwa saya tidak boleh menggunakan done () sama sekali dengan panggilan asinkron karena mereka berbasis Janji?

Jika demikian, bagaimana cara membuat tes berikut lulus ketika panggilan untuk menunggu sesuatu () menimbulkan Kesalahan dan ini yang saya harapkan akan terjadi?

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
  } catch (e) {
    // how to indicate that this should happen, if I can't call done() to tell mocha?
  }
});

Bisakah seseorang membantu saya memahami kasus khusus ini? Haruskah saya menggunakan pustaka pernyataan atau adakah sesuatu tambahan yang perlu saya masukkan ke dalam kode di atas?

Terimakasih banyak.

Solusi tingkat rendah dan rawan kesalahan:

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
    throw new Error('Promise unexpectedly fulfilled');
  } catch (e) {
    // Optionally assert something about e...
  }
});

Saya akan menggunakan perpustakaan pernyataan setiap hari dalam seminggu:

const expect = require('unexpected');
it('should throw an error', async function () {
    await expect(something(), 'to be rejected with', 'the error message');
});

Atau chai + chai-seperti-dijanjikan:

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

it('should throw an error', async function () {
    await expect(something()).to.be.rejectedWith('argh');
});

Hai teman-teman, yang ini bekerja dengan baik dengan asyncawait (hilangkan saja 'done ()')

describe('New order from landing', function() {
    describe('check new client function', function () {
        it("must check db and return that random client is new", async function () {
            var result = await handler.checkClient(reqNewClient.body)
                expect(result).to.have.property('newclient').equal(true)
            })
    })
})

hasil:

Pesanan baru dari pendaratan
periksa fungsi klien baru
√ harus memeriksa db dan mengembalikan klien acak itu baru
1 passing (9 ms)

@kolykhalov Terima kasih Ini berhasil untuk saya

Dalam kasus saya, saya membungkus blok asinkron dalam coba / tangkap

it('It should activate a user.', async() => {
        return new Promise((resolve, reject) => {
            try {
                // Get the last activation token from the DB
                let sql = 'select * from activation_tokens order by created_at desc limit 1';
                let res = await DB.query(sql);
                let {token} = res[0];

                server
                    .post('/auth/activate')
                    .send({token})
                    .set('Content-Type', 'application/x-www-form-urlencoded')
                    .expect('Content-Type', /json/)
                    .expect(200)
                    .end((err, res) => {
                        res.body.data.should.contain.keys('message');
                        res.body.data.message.should.equal("Activated");
                        resolve();
                    });
            } catch (e) {
                console.error(e);
            }
        });
    });

Diupgrade untuk menghindari kerentanan. Sekarang saya harus menulis ulang 99 tes. FML

Jadi untuk memperjelas apa masalahnya, karena beberapa orang mengatakan Anda tidak perlu menggunakan done dan async , berikut adalah contoh di mana Anda ingin menggunakan keduanya.

it('should error on fn', async function(done) {
  try {
    await fn();
  } catch (e) {
    // assert some things here
    done()
  }
});

Tidak menggunakan done akan membiarkan pengujian lulus jika tidak ada kesalahan yang terjadi. Beberapa orang menyarankan untuk menggunakan hal-hal seperti expect(await fn).to.throw('blah') , tetapi terkadang Anda perlu memeriksa lebih banyak properti daripada yang akan muat dalam satu baris.

Saya mendapatkan kesalahan ini tetapi saya tidak sengaja mengembalikan janji (supertest)

it('Should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Menghapus klausa 'kembali' memecahkan masalah

Untuk siapa pun yang tergila-gila dengan ini ...

Ini bekerja:

it("should work", async () => {
  await something;
});

Ini tidak:

it("should work", async (done) => {
  await something;
});

Anda harus menghapus done sebagai parameter.

@tamoyal , yeah - seperti yang diposting elado pada 3 Agustus 2016.

Menggunakan async / await mengembalikan Promise implisit. Penggunaan callback done adalah untuk
Asinkron berbasis sini . Mocha mendukung keduanya ... tetapi tidak pada saat bersamaan.
Ini didokumentasikan dan telah menjadi perilaku standar sejak rilis Mocha-3.0.

Saya mendapatkan kesalahan ini tetapi saya tidak sengaja mengembalikan janji (supertest)

it('should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Menghapus klausa 'kembali' memecahkan masalah

@victorsferreira , sepertinya ini seharusnya menjadi solusinya ...

it('should do something', () => {
  return supertest(server)
    .get(`/api/endpoint`)
    .set('somekey', 'somevalue')
    .expect(200);
});

@tamoyal Yap itulah yang membuat saya tersandung. Sangat tidak intuitif untuk memiliki pustaka pihak ketiga yang melihat parameter fungsi yang saya buat dan menggunakannya untuk membuat keputusan tanpa memberi tahu saya bahwa itu melakukannya.

@mienaikoe , skenario persis ini didokumentasikan secara eksplisit lho ...

@plroebuck dua hal:

1) Ada perbedaan antara mendokumentasikan sesuatu dan menjadi salah satu dari sedikit perpustakaan di npm yang melakukan introspeksi pada parameter fungsi. Saya dapat mendokumentasikan bahwa saya mengambil pemeriksaan latar belakang semua teman saya ketika saya pertama kali bertemu mereka, tetapi itu masih aneh dan orang-orang masih akan mengeluh tentang hal itu kecuali saya punya alasan dan secara eksplisit memberi tahu mereka alasannya.

2) Ada kekurangan dalam dokumentasi:

Di Mocha v3.0.0 dan yang lebih baru, mengembalikan Janji dan menelepon selesai () ...

Ini bukan tentang pemanggilan selesai, ini tentang _menentukan done sebagai parameter_ apakah Anda menggunakannya atau tidak.

@nenen

  1. Mocha memeriksa parameter kedua it untuk melihat apakah ada fungsi, dan (jika ada) aritasnya untuk menentukan apakah pengguna menambahkan callback. Ini hampir tidak memenuhi syarat sebagai introspeksi, dan biasa digunakan dalam JavaScript.
  2. Kirim PR untuk mengoreksi dokumentasi jika menurut Anda terlalu kabur.

Menghapus selesai sebagai param berhasil untuk saya!

SEBELUM:

image

SETELAH (berhasil!)

image

@QauseenMZ , lihat tidak ada perbedaan antara kode "sebelum" dan "setelah" Anda.

Anda juga tidak akan membalas janji Anda. Bukankah seharusnya lebih seperti berikut ini?

  it('should receive successful RPC response', async () => {
    return await getResponse(unitData)
      .then((res) => {
        expect(res).to.have.status(200);
      });
  });

PS. Pengurutan argumen callback Node Anda gagal ... error _always_ berjalan lebih dulu.

@plroebuck Terima kasih telah menyebutkan! Saya baru saja mengeditnya.

Saya tidak menangani promise karena fungsi getResponse mengembalikan callback. Itu satu-satunya cara saya membuatnya bekerja. Fungsi getResponse adalah sebagai berikut:

image

Di sini kesalahan adalah parameter kedua hanya karena fungsi getResponse callback kembali. Tolong beri tahu saya pendapat Anda tentang itu. Terima kasih!

Beberapa bagian sepertinya tidak masuk akal. Hanya untuk kejelasan, paket request yang Anda gunakan?
Mengapa Anda perlu mengembalikan objek options (yang Anda beri nama unitData )?

  • Dari mana obj berasal?
  • Mengapa Anda memiliki res.body.error dengan res.statusCode === 200 ?

PS. Harap cukup tempelkan kode itu sendiri, bukan gambar kode ...

Untuk siapa pun yang tergila-gila dengan ini ...

Ini bekerja:

it("should work", async () => {
  await something;
});

Ini tidak:

it("should work", async (done) => {
  await something;
});

Anda harus menghapus done sebagai parameter.

@tamoyal Anda menyelamatkan hidup saya <3

Ini memecah dan menghasilkan kesalahan yang sama:

it('Location Querying Works', async () => {
    await WeatherComponent.pullLocationData();
    Vue.nextTick(()=>{
      expect(WeatherComponent.$el.textContent).contain("Spain")
    })
  })

Untuk memberi Anda solusi cepat untuk masalah ini, bungkus seluruh pengujian Anda dengan janji, dan gunakan resolve seperti yang Anda lakukan done .

Putar ini:

it.only("Works with the database", async (done) => {
    let browser = await puppeteer.launch();
    let query = db.collection('lists').where('ownerId', '==', 'UJIXXwynaCj8oeuWfYa8');
    let ct = 0;
    query.onSnapshot(async querySnapshot => {
        if (ct === 0) {
            await addAPurpose(browser, session, sessionSig); // A function with a bunch of Puppeteer code.
            ct++
        } else {
            expect(querySnapshot.size).to.equal(1);
            expect(querySnapshot.docs[0].get().title).to.equal("Understand Mocha");
            done();
        }
        console.log(`Received query snapshot of size ${querySnapshot.size}`);
    }, err => {
        console.log(`Encountered error: ${err}`);
    });
});

ke dalam ini:

it.only("Works with the database", async () => {
    const onSnap = new Promise(async (res, rej) => {
        let browser = await puppeteer.launch();
        let query = db.collection('lists').where('ownerId', '==', 'UJIo1gGMueoubgfWfYa8');
        let ct = 0;
        let unsubscribe = query.onSnapshot(async querySnapshot => {
            if (ct === 0) {
                await addAPurpose(browser, session, sessionSig);
                ct++
            } else {
                expect(querySnapshot.size).to.equal(1);
                expect(querySnapshot.docs[0].data().title).to.equal("Evolution");
                // done(); 
                unsubscribe();
                res();
            }
            console.log(`Received query snapshot of size ${querySnapshot.size}`);
        }, err => {
            console.log(`Encountered error: ${err}`);
            rej()
        });
    });
    return onSnap;
});

Dan itu bekerja seperti yang Anda inginkan.

Kebutuhan untuk menghapus done mengejutkan saya, karena pengujian berjalan sampai selesai dipanggil. Untuk membuatnya lebih jelas bahwa done tidak boleh digunakan dengan async, fungsi asinkron harus gagal _immediately_ jika selesai diteruskan. Mocha harus memulai tes dengan:

  1. Melihat apakah fungsinya asinkron, dan
  2. Mendeteksi argumen done .

Jika mereka berdua ada, itu seharusnya membuang alih-alih membiarkan pengujian saya berjalan hingga done dipanggil dan kemudian membuat saya marah. Kemudian kesalahan akan menyarankan agar Anda membungkus kode Anda dengan janji lain, dan menggunakan penyelesaian seperti yang Anda lakukan done .

Saya tahu Anda dapat menggunakan function.prototype.name === "AsyncFunction" . Lalu itu

if (function.prototype.name === "AsyncFunction && arg1.name === "done") {
  throw new Error("Can't use done with an async function")
}

untuk menerapkan ini.

Saya hampir SOL dalam hal ini. Saya perlu mengeksekusi kode async DAN diharapkan kode itu tidak akan selesai dieksekusi (selamanya) jadi saya HARUS menggunakan selesai. Hack-a-round yang mengganggu adalah membungkus kode tes async saya dalam fungsi async yang memanggil sendiri tetapi membiarkan it func sebagai fungsi sinkronisasi.

Larutan:

it("It shouldn't be like this", function (done) {
    ( async function(){
        var life = require('InfiniteLife');
        var asyncRes = await life.someCoolstuff();
        assert(asyncRes);
        setTimeout( function(){
            done();
        },letsCallItQuitsNow);
    })();
});

Contoh fungsi asinkron dengan pemutusan selesai.

it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
        let aObj = await admin.createAdmin();
        chai.request(server)
        .post("/authenticate")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: aObj.login,password:aObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            done();
        });
    });

Kasus Sukses

it('If the credentials exists in the system it should return the token generated against it.', async () => {
        let adminObj = await admin.createAdmin();
        chai.request(server)
        .post("/auth/login")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: adminObj.login,password:adminObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            // done();
        });
    });

@ Hanifahrizal2

Anda tidak memerlukan done atau async dalam pengujian Anda. Chai http mengembalikan sebuah janji. Tes Mocha bekerja dengan kode async jika Anda mengembalikan sebuah promise.

it('If the credentials exists in the system it should return the token generated against it.', () => {
  let adminObj = await admin.createAdmin();
  return chai.request(server)
    .post("/auth/login")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: adminObj.login,password:adminObj.password})
    .end((err, res) => {
      res.should.have.status(200);
      res.body.should.be.a("string");
    });
});

@Bayu_joo

Solusi yang Anda posting adalah untuk mengatur waktu pengujian jika terlalu lama. Mocha sudah memiliki fungsi ini di dalamnya.

it("It should be liek this", async function () {
  this.timeout(letsCallItQuitsNow);

  var life = require('InfiniteLife');
  var asyncRes = await life.someCoolstuff();
  assert(asyncRes);
});

Contoh Anda juga tampaknya tidak gagal dalam pengujian jika batas waktu terlampaui, jadi berhati-hatilah dengan bergantung pada kebenarannya

`Menggunakan express / js, dan firebase cloud functions, periksa respons error api, lalu dapatkan token dan coba lagi. Pastikan jika menggunakan express Anda mengembalikan .json dan bukan .send, juga berfungsi bug logger dengan mocha jadi gunakan console.log biasa.
Pastikan Anda menggunakan async dan emit done sepenuhnya

  process.env.NODE_ENV='TESTING';
  const { FIREBASE_UID } = require('dotenv').config()?.parsed;
  const { red, green, white } = require('chalk');
  const chai = require('chai');
  const chaiHttp = require('chai-http');
  const server = require('../lib/api').API;
  const should = chai.should();
  const expect = chai.expect;
  chai.use(chaiHttp);
  const test = chai.request(server).keepOpen();

  const shouldPrint = (details, status) => {
      return white(`details: ${details}, status: ${status}`);
  };

describe('Authorization Middleware Error Check for bad token', () => {

    it(shouldPrint('Bad Request', red(400)), async () => {
        try {
            const res = await test.get('/').send()
            res.should.have.status(400);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Bad request')
            expect(status).to.equal(400)
        } catch (error) { 
            throw error 
        }
    })


    it(shouldPrint('Unauthorized', red(401)), async () => {
        try {
            const res = await test.get('/').set('Authorization', 'Bearer bad123token').send()
            res.should.exist
            res.should.have.status(401);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Unauthorized')
            expect(status).to.equal(401) 
        } catch (error) {
            throw error
        }
    })

})

 describe('Get token and ping', () => {
    let adminToken

    it(shouldPrint( 'Create custom jwt for testing', green(200)), async () => {
        try {
            const res = await test.get(`/createToken/${FIREBASE_UID}`).send()
            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            adminToken = (JSON.parse(res.text)).token
        } catch (error) {
            throw error
        }
    })

    it(shouldPrint('PING', green(200)), async () => {
        try {
            const res = await test.get('/')
                .set('x-tenorders-testing', 'true')
                .set('Authorization', `Bearer ${adminToken}`).send()

            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('PING')
            expect(status).to.equal(200)
        } catch (error) {
            throw error
        }   
    })

})
`

Apakah halaman ini membantu?
0 / 5 - 0 peringkat