Mocha: خطأ: أسلوب الحل محدد بشكل زائد.

تم إنشاؤها على ٢ أغسطس ٢٠١٦  ·  84تعليقات  ·  مصدر: mochajs/mocha

هذه:

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

سينتج عن الخطأ Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

يقول المستندات:

في Mocha v3.0.0 والإصدارات الأحدث ، ستؤدي إعادة الوعد والاتصال المنجز () إلى استثناء ، لأن هذا خطأ بشكل عام:

يتم حل استدعاء النموذج باستخدام Promise.<Object> من الإدخال المُدرج حديثًا ، ولكن إذا حذفت .then(() => done()) فإن مهلة الاختبار.

confirmed-bug

التعليق الأكثر فائدة

async وظائف (بابل) مع done أيضًا فاصل.

ال 84 كومينتر

async وظائف (بابل) مع done أيضًا فاصل.

كما يوضح الخطأ ، ليس من المفترض أن تقدم دالة بحالات <0 (بمعنى أنها تقبل رد اتصال done ) ، _ و_ إرجاع الوعد.

أسهل طريقة لإصلاحها هي حذف return ، ولكن نظرًا لأنك تستخدم الوعود ، أقترح التخلص من رد الاتصال done بدلاً من ذلك ، لأنه سينتج عنه بنية أبسط بكثير :

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

فيما يلي مثال على كل من async (وعد بشكل أساسي) و done الذي يكسر:

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

elado إنها حالة استخدام مثيرة للاهتمام ، على الرغم من أنها من وجهة نظر mocha هي نفس الحالة - وظيفة تتطلب رد استدعاء done وتعيد وعدًا. سأعيد كتابته ليكون مبنيًا على الوعد تمامًا:

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

... لكني أعتقد أن ما ستحصل عليه هو أنه في هذا المثال ، سيكون من الأفضل أن تنتظر mocha _ كل من _ الوعد ليتم حله واستدعاء رد الاتصال؟

لسوء الحظ ، غالبًا ما يكون هذا السرد المعين خطأً في الاختبار ، وهذا هو سبب إضافة رسالة الخطأ هذه في المقام الأول.

... إذا حذفت. ثم (() => تم ()) ثم مهلة الاختبار.

هذا يبدو وكأنه خطأ ، على أي حال.

ScottFreeCode Hmm ، نعم ، يبدو أن السبب في ذلك هو إصدار الخطأ "overpecified" في الوظيفة المقدمة على أنها رد الاتصال done : https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib/ runnable.js # L357 -L373

... ولكن يمكننا بالطبع تحديد أنه يجب علينا الفشل مع هذا الخطأ بمجرد عودة الوظيفة.

ScottFreeCode ما هو الإصلاح هنا؟

سأخمن أنه "انتظر الوعد بحل أو رفض ، ثم أرفض بالخطأ" غير المحدد "؟

إذا كنا سنعتبر أن الوعد done plus في نفس الاختبار خطأ ، فلا أرى أي سبب لعدم اكتشاف الخطأ بمجرد إرجاع دالة الاختبار التي استغرقت done الوعد ، كما اقترح @ باباندريو . ليس من المنطقي بالنسبة لي محاولة معرفة النقاط الأخرى التي يجب أن تؤدي إلى حدوث الخطأ ما لم نعتزم السماح بالوعود و done معًا في بعض الحالات.

ScottFreeCode أنا موافق. إذن كذلك

  1. كشف المشكلة قم بإنشاء مثيل لـ Error لكن لا تستدعي done() معها
  2. انتظر حتى الوفاء بالوعد
  3. رفض باستخدام Error

سؤال إضافي: ما العمل بنتيجة الوفاء بالوعد؟

آه ، أعتقد أنني فهمت - حتى عندما نكتشف الخطأ ، نحتاج إلى السماح بتشغيل الاختبار حتى لا يزال قيد التشغيل أثناء الاختبار التالي ، وربما ، حتى نتمكن أيضًا من الإبلاغ عن نتيجة الاختبار.

هل يمكن أن ينتهي الاختبار أيضًا باستدعاء done دون حل الوعد أو رفضه؟ إذا كان الأمر كذلك ، فهذه حالة نهائية أخرى علينا التعامل معها.

ميولي ، إعادة. ما يجب فعله بنتيجة الاختبار ، هو أنه إذا انقضت مهلة الاختبار (بدون أن يكون لديك _ إما _ يُسمى done أو حل / رفض الوعد) يجب علينا فقط الإبلاغ عن استخدام done مع الوعد (لأن الارتباك حول المجموعة يمكن أن يكون سببًا جيدًا لعدم وصولها إلى أيٍّ منهما وتوقف المهلة بدلاً من ذلك) ، ولكن إذا تمكنت من الانتهاء بأي من الوسيلتين ، فمن المفترض أن تكون النتيجة صحيحة (أو على الأقل ذات مغزى على الأقل) ويجب علينا الإبلاغ كل من الاستخدام الخاطئ المحتمل لـ done والوعد معًا وأيضًا نتيجة الاختبار على أمل أن تكون مفيدة إلى حد ما على الأقل.

هذا أفضل ما يمكنني التوصل إليه في الوقت الحالي ، على أي حال ، ولكن قد يكون لدي المزيد من البصيرة إذا تمكنت من إيجاد الوقت للبحث في هذه المشكلة.

حسنًا ، يمكننا القيام بذلك على مراحل. الأول هو التأكد من أنه إذا تم إرجاع الوعد وتم إعطاء رد اتصال done ، فإن Mocha سوف تنكسر بطريقة ودية.

من المعقول أن يستطيع المرء فعل شيء كهذا (باستخدام بلوبيرد):

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

ولكن هذا مجرد شيء يمكنك فعله ؛ لا يزال يتعين علي تحديد حالة استخدام لهذا الغرض.

أعتقد أنني أربكت نفسي. لست متأكدًا من وجود خطأ هنا على الإطلاق.

هذه مشكلة من نوع "واجهة المستخدم الضعيفة"

مهلة انتظار استدعاء done على الرغم من حل / رفض الوعد المرتجع هو بالتأكيد خطأ ، بغض النظر عما إذا كنا نريد عدم السماح لمثل هذه الاختبارات باستخدام done والوعود معًا في الأول مكان. يجب أن يستخدم هذا إما نتيجة الوعد و / أو الخطأ لأن الوعد و done تم استخدامهما في نفس الاختبار ، ولكن ليس فقط مهلة لأن أحدهما لم يكتمل أبدًا عند الآخر (وهو ما هو ما يفعله حاليا):

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

1) يجب ألا تنتهي المهلة:
خطأ: تجاوز مهلة 2000 مللي ثانية. تأكد من استدعاء رد الاتصال done () في هذا الاختبار.

سألقي نظرة على العلاقات العامة في أي حال ...

يجب على الأطراف المهتمة مشاهدة # 2413.

تضمين التغريدة

أحصل على نفس الخطأ باستخدام خطافات متعددة غير متزامنة beforeEach في الإصدار 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));

ليس من الممكن الاستمرار في استخدام الخطافات غير المتزامنة بهذه الطريقة؟

لقد اكتشفت مشكلتي: أساليبي داخل كل خطاف تعيد promise (لم أكن أعرف ذلك لأنني لم أستخدم الوعود في تلك الحالات) ومن الواضح أنني مررت أيضًا وظيفة cb .

لا أعتقد أن هذه فكرة جيدة. إذا طلب أحدهم رد الاتصال ، أعتقد أنهم أوضحوا نواياهم. الخطأ هنا مجرد إزعاج. يرجى إزالته.

هذه ليست في الحقيقة مسألة نية. من وصف # 1320:

عندما يتم تحديد كل من وظيفة رد الاتصال وكائن الوعد
عاد ، شرط دقة Runnable غامض.

إنه ليس غامضًا ، تم طلب رد الاتصال. كيف هذا غامض؟

أنا أتفق مع @ RobertWHurst لا يوجد غموض هنا.

إلى جانب ذلك ، أعتقد أن هذه المسألة يمكن أن تكون "قائمة على الرأي" وسيكون للمطورين وجهات نظر مختلفة. أنا أعتبر أن القيام بتغيير جذري وإجبار الناس على استخدام هذه الطريقة أمر متطرف للغاية.

لم نفرض على أي شخص القيام بأي شيء. أصدرنا تخصصًا رئيسيًا ، والذي بحكم تعريفه سيكون له تغييرات عاجلة. نحن لم نكسر سمفر.

لا أعتقد أنه يعني أنك كسرت سمفر ، أعتقد أنه يعني أنك كسرت موكا. هذا التغيير لا يجعل الأمور أسهل للمطورين ، إنه يفرض رأيًا.

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

^^ هذا ليس غامضا. من الواضح تمامًا ما هو متوقع من قبل المؤلف. لقد بذل المؤلف قصارى جهده لطلب معاودة الاتصال.

في الواقع سبب حصولك على استثناء هنا هو أقصى قدر من تجنب إنفاذ الرأي. ليس لدى Mocha رأي في ما إذا كان الوعد المرتجع أو رد الاتصال موثوقًا به. لا يمكنك الحصول على كليهما في نفس الوقت لأن ذلك يؤدي إلى نتائج غامضة. يجب اعتبار النتائج الغامضة في إطار الاختبار خطأ. ومن هنا تظهر رسالة الخطأ لمساعدتك على إدراك ذلك والقيام بالاختيار والتغيير المطابقين لرأيك.

قد يكون من المفيد تقليل حجم الدراما. "لقد كسرت موكا" لا يساعد أحدا. يتم تعريف زيادة إصدار semver الرئيسي بشكل صريح على أنه تغييرات متقطعة قد تتطلب منك تعديل التعليمات البرمجية. يمكنك البقاء في 2.x لمنحك الوقت لإجراء التغييرات لإصلاح اختباراتك. هذا تطور وليس كسر

Munter ما زلت في حيرة من أمر طلب رد الاتصال. إذا طلبت رد اتصال ، فمن المتوقع أن تستخدمه. وإلا فهو خطأ في البرمجة. هذا إجراء صريح من قبل مؤلف الاختبار.

إذا كنت تستشعر الدراما ، فأنا لا أعني ذلك. لا يُقصد بعبارة "لقد كسرت موكا" أن تكون زائدية. أعتقد أن هذا يتعارض مع تصميم الوحدة ويخالف عقد واجهة برمجة التطبيقات الأصلي.

كما meintoned قبل بابل async / انتظار لا يعمل بشكل جيد مع موكا الجديدة

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();
  });

يعمل هذا الرمز جيدًا مع mocha @ 2 ولكنه لا يعمل مع mocha @ 3 لأنه في await يُعيد الوعد

أعتقد أن هذا العلاقات العامة مناسب هنا => https://github.com/mochajs/mocha/pull/2413
مزيد من التعقيد للتعامل مع الحالات المتطرفة لهذا الخطأ.

artyomtrityak هذا مثال رائع على أن done غير ضروري.

المنتقدون قالوا مقالهم. ومع ذلك ، يختلف المشرفون على Mocha مع الحجة (الحجج) للتراجع عن هذا التغيير. قال عيران هامر (معاد صياغته) ، "بصفتك مشرفًا ، فإن أحد أصعب الأشياء التي يمكنك القيام بها هو عدم تعيين أولئك الذين يريدون تحريك العمل في اتجاههم."

أنا مرحب بك في الحلول البديلة - المزيد من التوثيق (على سبيل المثال ، المزيد من الأمثلة على هذا الخطأ وكيفية إصلاحه) ، رسائل خطأ أفضل - ولكني لست مهتمًا بالدراما أو الوقاحة أو الشكوى. ستساعد المساهمة بأي من هذه الحلول في Mocha في تحويل السلبية إلى إيجابية.

إذا لم يعجبك هذا التغيير ، وببساطة لا يمكنك أن تكون بنّاءً بشأنه ، فهو OSS - يمكنك تفرع المشروع وإعادة التغييرات هناك.

boneskull يتحول إلى وظائف غير mocha @ 2 ولكن مع mocha @ 3 لا يعمل. لذلك لا يستطيع فريقي (حوالي 20ppl) الانتقال إلى أحدث أنواع المخاوي بسبب هذا.

يوفر mocha 2.x حاليًا قدرًا كبيرًا من المرونة ، فهل هناك أي سبب فني لهذا التغيير؟

artyomtrityak هذا مثال رائع على عدم ضرورة القيام بذلك.

هل يمكنك من فضلك أن تضرب مثالاً كيف يجب أن يبدو ذلك مع بنية babel async/await وبدون return new Promise ؟

artyomtrityak ماذا عن قبول الوعود بشكل كامل؟ ثم يمكنك تقصير اختبارك إلى:

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');
  });

إذا شعرت بالهجوم الشخصي ، فأعتقد أنك بحاجة إلى إعادة النظر في مدى استثمارك عاطفيًا في هذه المحادثة. لم يكن أي من التعليقات شخصية. أنا متأكد من أنك جميعًا رائعون ، لا سيما بالنظر إلى أنك قد تبرعت بوقتك للمساعدة في الحفاظ على هذا المشروع الذي يحظى بتقدير كبير وجدير بالثناء.

بدأ معظمكم (المشرفون النشطون حاليًا) العمل على Mocha في منتصف عام 2014 تقريبًا. تم إنشاء Mocha بالفعل من خلال النقطة التي بدأت فيها يا رفاق المساهمة. إنه رأيي فقط ، لكنني لا أعتقد أنني وحدي في التفكير في أنه لا ينبغي لأحد إجراء تغييرات عاجلة على مكتبة قائمة ما لم يكن ذلك مبررًا. على الرغم من أنني أستطيع تخيل المبرر الأصلي لهذا التغيير ، إلا أنه لا يصمد جيدًا عندما يشير المرء إلى ما يلي. طلب رد الاتصال يدل على نية واضحة. الوعود ليست واضحة لأنه لم يتم طلبها ، يتم إعادتها ، وهو ما يمكن أن يحدث بشكل غير مباشر أو عرضي (يتم إرجاعها من مكتبة جهة خارجية على سبيل المثال). بسبب هذه الاختلافات ، فإن طريقتين للإنتاج غير متساويين ، وبالتالي فإن محاولة استخدام كليهما ليست غامضة حقًا. يجب كتابة عمليات الاسترجاعات في وسيطات الاختبار. لا يمكنك فعل ذلك بالوعود ، وبالتالي من خلال طلب معاودة الاتصال ، تكون قد أوصلت نواياك صراحةً. أثار مجتمعك هذه المخاوف ، وبدلاً من الاعتراف بالخطأ ، أنتم يا رفاق تضاعفون من شأنكم. يبدو أنك تفكر في إجبار الاختبارات على أن تكون غير متزامنة للتأكد من أن هذا الخطأ يعمل باستمرار. انظر => https://github.com/mochajs/mocha/pull/2413. يبدو أنه تغيير كبير لرسالة خطأ تحمي من خطأ غير محتمل.

لقد قمتم يا رفاق بعمل رائع في صيانة هذه المكتبة منذ رحيلtj ، هل يمكنكم من فضلكم التفكير قليلاً في هذا التغيير. قلقي هو أن هذا قد يعرض المكتبة للخطر.

أتفق تمامًا معRobertWHurst.

يجب أن يتجاوز طلب done سلوك الوعد الذي تم إرجاعه. ليس من المحتمل أن تطلب done عندما لا تكون هناك حاجة لذلك ، وسيناريوهات الأحداث المنبعثة في دالة async هي مثال ممتاز.

من تعليقي أعلاه:

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

مع انتقال المزيد من الأشخاص إلى ES6 / 7 + async / await ، سيصبح هذا مسلكًا شائعًا عند استخدام Mocha.

يرجى إعادة النظر في هذا التغيير.

RobertWHurst أنت تجادل بأن تحديد رد اتصال done هو نية صريحة. هل بيان return ليس نية صريحة؟ كلاهما محدد في التعليمات البرمجية الخاصة بك بواسطتك. كيف يمكننا أن نقرر أن جزءًا من التعليمات البرمجية الخاص بك مقصودًا والآخر غير مقصود؟ إذا تخيلت عالماً قبل () => foo أي بيان إرجاع سيكون دائمًا صريحًا. السبب الوحيد الذي يجعلك متحمسًا الآن هو أنك بدأت في استخدام عبارات الإرجاع الضمنية ، لأن ما لا أستطيع إلا أن أعتقد أنه أسباب جمالية.

نظرًا لأن الكثير من استخدام Mocha يتم بواسطة المبتدئين الذين عادةً ما يقومون بنسخ / لصق الأمثلة ، والتي من المحتمل جدًا أن تحتوي على رد اتصال done ، كيف يمكنك التعامل مع الحالة التي يُرجع فيها هذا المستخدم الجديد وعدًا صريحًا ، ولكن يحصل على مهلة؟ هذه هي النتيجة إذا تم إرجاع التغيير الذي كنت غاضبًا منه.

السلوك الحالي أكثر وضوحًا بشأن الخطأ من مجرد مهلة غير متوقعة

Munter مع وجود async في الصورة أعتقد أن درجات الوعد المرتجعة أقل على مقياس explicitness لأنه تم إنشاؤها وإعادتها تلقائيًا ، سواء كنت تستخدم 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');
  });
});

ثم هناك الخلاف المثير للجدل:

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

كما انها سهلة لذلك صغير-ويني async التسلل في طريق الصدفة، لذلك يجب علينا التفكير في (على الأقل) المثال الأول كنوع جديد من مسكتك، كماelado نقطة الخروج.

بعد قليل من المحادثة هنا => https://github.com/mochajs/mocha/pull/1320 ، خطرت لي فكرة عن حل بديل للمشكلة. لقد أضفت PR من أجل متعة المراجعة هنا => https://github.com/mochajs/mocha/pull/2454

:بيرة:

أنت تجادل بأن تحديد رد اتصال تم هو نية صريحة. هل بيان العودة ليس نية صريحة؟

Munter لا تنس أن تعبيرات دالة coffeescript و es6 arrow تعيد _implicitly_ ، لذا يمكنك القيام بشيء مثل

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

وتعتقد أنك بأمان. لكن هذه المشكلات تعني أنه يجب عليك تحويل هذا الخط الجميل إلى شيء مثل

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

هذه هي مشكلتنا بالضبط مع قاعدة الكود الشاملة في كل منها و mecano . ضع في اعتبارك هذا:

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

هذا ليس خاصًا تمامًا بالقهوة ولكن إرجاع القيم الضمنية يجعل الأمر أسوأ. يجب أن يكتشف Mocha مثيلات الوعد الصالحة. أيضًا ، ربما يمكن لأحد الخيارات تعطيل هذه الميزة.

مرحبا جميعا،
ركض في هذه المشكلة مع الكود التالي ؛

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()
      )
    )
  )
)

تم حل هذه المشكلة عن طريق احترام سلسلة الوعد وإهمال رد الاتصال 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')
      )
    )
  )
)

أتمنى أن يساعد هذا الآخرين الذين يقعون في خطأ مشابه: ابتسم:

ملاحظة : القلب: موكا راجع للشغل: +1:

EDIT إزالة catch() بناءً على تعليق Munter .

@ karlbateman شكرا. لا تحتاج هذا الأخير .catch رغم ذلك. الوعد المرفوض يعتبر خطأ من قبل Mocha

Munter أرى ، شكرا لك: مبتسم:

عند التفكير في الأمر أكثر من ذلك بقليل ، أحب حقًا سلوك انتظار كل من الوعد ورد الاتصال. هل تم إحراز أي تقدم في تنفيذ ذلك؟

@ light24bulbs شكرا على ردود الفعل! لا ، لم يبدأ أحد في العمل على ذلك لأنه مجرد فكرة ألقيتها هناك لأرى ردود الفعل. كنت الآن فقط أتحقق مما إذا كانت هناك أي ملاحظات إضافية حول الفكرة ، وأي حالات لا تعمل فيها ، وما إلى ذلك.

هل هناك حل لهذا عند استخدام بابل؟

هل هناك حل لهذا عند استخدام بابل؟

ألتف مرتين:

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

SaschaNaz شكرا لك ، هذا يعمل في v3.2.0 :)

بعد 6 أشهر ، لا تزال هذه المشكلة تضغط على جميع اختبارات js الحديثة ...
عار

benfavre أشكرك على الكلمات المشجعة التي

Munter لا تقلق من دواعي سروري أنه يمكنني المساعدة في تحديد مشكلة معينة واجهتها كمستخدم جديد لـ mochajs.
اقترح SaschaNaz أن حل التغليف مرتين لم يساعد.

=> استخدام الوعود كان يعمل كما ينبغي.

في المرة القادمة أعتقد أنني يجب أن "+1" فقط مثل مغفل حتى لا أتعرض للإهانة مجانًا.
لم يكن هناك أي شيء مهين في رسالتي ، فكلما بقي بياني صحيحًا.

سيختار معظم الأشخاص إطار عمل آخر ... اعتبارًا من الآن ، إنه مجرد كسر عادي مع عدم التزامن / انتظار ، مع عدم وجود مؤشرات واضحة في أي مكان على الموقع الرئيسي ، ولا توجد رسالة خطأ واضحة في cli.

عام جديد سعيد واستمتع باللعب مع الأطفال.

قشعريرة...

تطورت المناقشات إلى "الانتباه" لكل من رد الاتصال done و Promise : https://github.com/mochajs/mocha/issues/2509

إغلاق. انظر # 2509

في حال كان بعض الجسد لا يزال يعاني ...

هل هناك حل لهذا عند استخدام بابل؟

الآن مع المعالج المدمج للوعد كما هو مذكور في # 2509 ، لا يتعين علينا استخدام wrapping twice hack مثل هذا:

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

بدلاً من ذلك ، يمكننا ببساطة اتباع هذا:

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

تحقق من هذا المنشور لمزيد من التفاصيل

@ lo-tp
1.) هذا الرمز يعمل عند تجميعه مع بابل؟

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

تضمين التغريدة

  • نعم ، من أجل استخدام بناء الجملة async/await الآن ، نحتاج إلى مساعدة babel.

لست متأكدًا مما إذا كنت أفتقد شيئًا ... لكنني قمت بحل هذه المشكلة من خلال عدم استخدام أي بيانات إرجاع مع وعودي والاعتماد فقط على ما تم ().

هذا مثال يعمل لدي

  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');
    });
  });

لقد كنت أستخدم mocha 4 مع chai.assert.

في البداية حاولت استخدام رد الاتصال المنجز () مثل ذلك.

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();
  }
});

فشلت مع

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

هذا الخطأ هو ما قادني هنا ، إلى هذه الصفحة. من الخوض في هذا الخيط الطويل نوعًا ما (وأعترف أنني لم أفهم تمامًا جميع النزاعات) ، هل أفهم أنه لا ينبغي أن أستخدم عمل () على الإطلاق مع المكالمات غير المتزامنة لأنها تستند إلى الوعد؟

إذا كان الأمر كذلك ، كيف أجعل الاختبار التالي ينجح عندما تلقي مكالمة انتظار شيء ما () خطأ وهذا ما أتوقع حدوثه؟

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?
  }
});

هل يمكن لأي شخص مساعدتي في فهم هذه الحالة المحددة من فضلك؟ هل يجب أن أستخدم مكتبة التأكيد أم أن هناك شيئًا إضافيًا أحتاج إلى إدخاله في الكود أعلاه؟

شكرا جزيلا.

المستوى المنخفض والحل المعرض للخطأ:

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...
  }
});

سأستخدم مكتبة التأكيد في أي يوم من أيام الأسبوع:

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

أو تشاي + تشاي كما وعدت:

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');
});

مرحبًا يا شباب ، هذا يعمل جيدًا مع asyncawait (فقط حذف "تم ()")

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)
            })
    })
})

نتيجة:

أمر جديد من الهبوط
تحقق من وظيفة العميل الجديدة
√ يجب فحص ديسيبل وإرجاع أن العميل العشوائي جديد
مرة واحدة (9 مللي ثانية)

kolykhalov شكرا لقد عملت بالنسبة لي

في حالتي ، قمت بلف الكتلة غير المتزامنة في محاولة / التقاط

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);
            }
        });
    });

ترقية لتجنب نقاط الضعف. الآن عليّ إعادة كتابة 99 اختبارًا. FML

لذا فقط لتوضيح المشكلة ، حيث يقول البعض أنه لا يجب عليك استخدام done و async ، إليك مثال حيث تريد استخدام كليهما.

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

سيؤدي عدم استخدام done إلى اجتياز الاختبار في حالة عدم حدوث خطأ. اقترح البعض استخدام أشياء مثل expect(await fn).to.throw('blah') ، لكن في بعض الأحيان تحتاج إلى التحقق من خصائص أكثر مما ستلائمه في سطر واحد.

تلقيت هذا الخطأ ولكني كنت أعطي وعدًا عن طريق الخطأ (الاختبار الفائق)

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

أدت إزالة بند "العودة" إلى حل المشكلة

لأي شخص آخر كان مجنونًا بهذا ...

هذا يعمل:

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

لم يحدث ذلك:

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

يجب عليك إزالة done كمعامل.

@ Tamoyal ، نعم - كما نشر Elado في 3 أغسطس 2016.

يؤدي استخدام المتزامن / انتظار إلى إرجاع Promise ضمنيًا. استخدام رد الاتصال done لـ
غير متزامن يستند إلى هنا . يدعم Mocha كليهما ... ولكن ليس في نفس الوقت.
تم توثيق هذا وكان سلوكًا قياسيًا منذ إصدار Mocha-3.0.

تلقيت هذا الخطأ ولكني كنت أعطي وعدًا عن طريق الخطأ (الاختبار الفائق)

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

أدت إزالة بند "العودة" إلى حل المشكلة

victorsferreira ، يبدو أن هذا كان يجب أن يكون الحل ...

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

@ Tamoyal Yep هذا ما

mienaikoe ، هذا السيناريو الدقيق موثق بشكل صريح ، كما تعلم ...

plroebuck شيئين:

1) هناك فرق بين توثيق شيء ما وبين أن تكون إحدى المكتبات القليلة في npm التي تتأمل في معاملات الوظيفة. يمكنني أن أوثق أنني أقوم بفحص الخلفية لجميع أصدقائي عندما التقيتهم لأول مرة ، لكن الأمر لا يزال غريبًا وسيظل الناس يشكون من ذلك ما لم يكن لدي سبب وأخبرهم بالسبب صراحة.

2) هناك خلل في التوثيق:

في Mocha v3.0.0 والإصدارات الأحدث ، يتم إرجاع الوعد والاتصال () ...

لا يتعلق الأمر بالاستدعاء "تم" ، إنه يتعلق _ بتحديد done كمعامل_ سواء كنت تستخدمه أم لا.

تضمين التغريدة

  1. يتحقق Mocha من المعلمة الثانية لـ it لمعرفة ما إذا كانت الوظيفة موجودة أم لا ، و (إذا كان الأمر كذلك) صلاحيتها لتحديد ما إذا كان المستخدم قد أضاف رد اتصال أم لا. هذا بالكاد يمكن اعتباره استبطانًا ، وشائع الاستخدام في JavaScript.
  2. أرسل العلاقات العامة لتصحيح الوثائق إذا كنت تعتقد أنها غامضة للغاية.

عملت الإزالة كعامل بارام بالنسبة لي!

قبل:

image

بعد (يعمل!)

image

QauseenMZ ، لا ترى فرقًا بين الكود "قبل" و "بعد".

أنت أيضًا لا ترد على وعدك. ألا يجب أن يكون مثل ما يلي؟

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

ملاحظة. ترتيب وسيطة رد نداء العقدة الخاص بك مضروب ... error _always_ يذهب أولاً.

plroebuck شكرا لذكر! لقد قمت للتو بتحرير ذلك.

أنا لا أتعامل مع الوعد لأن وظيفة getResponse تعيد الاتصال. كانت الطريقة الوحيدة التي عملت بها. وظيفة getResponse كما يلي:

image

هنا الخطأ هو المعلمة الثانية فقط بسبب عودة وظيفة getResponse لرد الاتصال. واسمحوا لي أن أعرف أفكارك حول هذا الموضوع. شكر!

بعض الأجزاء تبدو غير منطقية نوعًا ما. للتوضيح فقط ، أي حزمة request تستخدمها؟
لماذا تحتاج إلى إرجاع الكائن options (ما أسميته unitData

  • من أين يأتي obj ؟
  • لماذا سيكون لديك res.body.error مع res.statusCode === 200 ؟

ملاحظة. يرجى فقط لصق الكود نفسه بدلاً من صور الكود ...

لأي شخص آخر كان مجنونًا بهذا ...

هذا يعمل:

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

لم يحدث ذلك:

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

يجب عليك إزالة done كمعامل.

tamoyal لقد أنقذت حياتي <3

هذا يكسر وينتج عنه نفس الخطأ:

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

لمنحك حلاً سريعًا لهذه المشكلة ، اختتم اختبارك بالكامل بوعد ، واستخدم resolve كما تفعل مع done .

اقلب هذا:

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}`);
    });
});

في هذا:

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;
});

وهي تعمل كما تريد.

لقد فاجأتني الحاجة إلى إزالة done ، لأنه تم استدعاء الاختبار الذي تم تشغيله حتى الانتهاء. لجعل الأمر أكثر وضوحًا أنه لا يجب استخدام "تم" مع غير متزامن ، يجب أن تفشل الوظائف غير المتزامنة _ على الفور _ إذا تم تمرير "تم". يجب أن يبدأ موكا الاختبار من خلال:

  1. معرفة ما إذا كانت الوظيفة غير متزامنة ، و
  2. كشف الوسيطة done .

إذا كان كلاهما حاضرًا ، فيجب أن يتم طرحه بدلاً من السماح بإجراء اختباراتي حتى يتم استدعاء done ومن ثم التلاعب بي. ثم يجب أن يوحي الخطأ بأن تقوم بتغليف الكود الخاص بك بوعد آخر ، واستخدام حل كما تفعل done .

أعلم أنه يمكنك استخدام function.prototype.name === "AsyncFunction" . ثم انها

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

لتنفيذ هذا.

كنت على وشك SOL على هذا. أحتاج إلى تنفيذ تعليمات برمجية غير متزامنة ومن المتوقع ألا ينتهي تنفيذ الكود (على الإطلاق) ، لذا يتعين عليّ استخدامه. كان الاختراق المزعج هو التفاف رمز الاختبار غير المتزامن الخاص بي في وظيفة غير متزامنة ذاتي الاستدعاء ولكن ترك وظيفة func it كعملية مزامنة.

المحلول:

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);
    })();
});

مثال على الوظائف غير المتزامنة مع كسر التفعيل.

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();
        });
    });

حالة النجاح

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();
        });
    });

@ haisum92

لا تحتاج إلى done ولا async في اختبارك. يعود Chai http بوعد. تعمل اختبارات Mocha مع رمز غير متزامن إذا أعدت وعدًا.

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");
    });
});

تضمين التغريدة

الحل المنشور هو تحديد مهلة الاختبار إذا استغرق وقتًا طويلاً. يحتوي Mocha

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

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

يبدو أيضًا أن مثالك لا يفشل في الاختبار إذا تم تجاوز المهلة ، لذا كن حذرًا في الاعتماد على صحتها

باستخدام دالات Express / js و firebase السحابية ، تحقق من استجابات خطأ API ، ثم احصل على رمز وحاول مرة أخرى. تأكد من أنك إذا كنت تستخدم التعبير السريع تعيد.
تأكد من استخدام عدم التزامن والانبعاث تمامًا

  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
        }   
    })

})
"

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات