سؤال

أحاول كتابة اختبار ل إطار اختبار ياسمين الذي يتوقع خطأ. في الوقت الحالي ، أستخدم أ Jasmine Node.js تكامل من Github.

في وحدة العقدة الخاصة بي ، لدي الرمز التالي:

throw new Error("Parsing is not possible");

الآن أحاول كتابة اختبار يتوقع هذا الخطأ:

describe('my suite...', function() {
    [..]
    it('should not parse foo', function() {
    [..]
        expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));
    });
});

حاولت أيضا Error() وبعض المتغيرات الأخرى ولا يمكنها معرفة كيفية جعلها تعمل.

هل كانت مفيدة؟

المحلول

يجب أن تمرر وظيفة إلى expect(...) مكالمة. الرمز لديك هنا:

// incorrect:
expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));

يحاول في الواقع مكالمة parser.parse(raw) في محاولة لتمرير النتيجة إلى expect(...),

حاول استخدام دالة مجهولة بدلاً من ذلك:

expect( function(){ parser.parse(raw); } ).toThrow(new Error("Parsing is not possible"));

نصائح أخرى

انت تستخدم:

expect(fn).toThrow(e)

ولكن إذا كان لديك نظرة على تعليق الوظيفة (المتوقع هو سلسلة):

294 /**
295  * Matcher that checks that the expected exception was thrown by the actual.
296  *
297  * @param {String} expected
298  */
299 jasmine.Matchers.prototype.toThrow = function(expected) {

أفترض أنه من المحتمل أن تكتبه مثل هذا (باستخدام وظيفة Lambda - مجهول):

expect(function() { parser.parse(raw); } ).toThrow("Parsing is not possible");

تم تأكيد هذا في المثال التالي:

expect(function () {throw new Error("Parsing is not possible")}).toThrow("Parsing is not possible");

يوصي دوغلاس كروكفورد بشدة بهذا النهج ، بدلاً من استخدام "رمي خطأ جديد ()" (طريقة النماذج الأولية):

throw {
   name: "Error",
   message: "Parsing is not possible"
}

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

throw {
   name: "NoActionProvided",
   message: "Please specify an 'action' property when configuring the action map."
}

ثم اختبر ما يلي:

expect (function () {
   .. do something
}).toThrow ("NoActionProvided");

هذا يتيح لي تعديل رسالة الاستثناء لاحقًا دون كسر الاختبارات ، عندما يكون الشيء المهم هو أنه ألقى النوع المتوقع من الاستثناء.

هذا هو بديل tothrow الذي يسمح بذلك:

jasmine.Matchers.prototype.toThrow = function(expected) {
  var result = false;
  var exception;
  if (typeof this.actual != 'function') {
    throw new Error('Actual is not a function');
  }
  try {
    this.actual();
  } catch (e) {
    exception = e;
  }
  if (exception) {
      result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected) || this.env.equals_(exception.name, expected));
  }

  var not = this.isNot ? "not " : "";

  this.message = function() {
    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
      return ["Expected function " + not + "to throw", expected ? expected.name || expected.message || expected : " an exception", ", but it threw", exception.name || exception.message || exception].join(' ');
    } else {
      return "Expected function to throw an exception.";
    }
  };

  return result;
};

حل أكثر أناقة من إنشاء وظيفة مجهولة الهوية التي تهدف إلى التفاف آخر ، هو استخدام ES5 bind وظيفة. تقوم وظيفة BIND بإنشاء وظيفة جديدة ، عند استدعائها ، لها this تم تعيين الكلمات الرئيسية على القيمة المقدمة ، مع تسلسل معين من الوسائط التي تسبق أي مقدمة عند استدعاء الوظيفة الجديدة.

بدلاً من:

expect(function () { parser.parse(raw, config); } ).toThrow("Parsing is not possible");

انصح:

expect(parser.parse.bind(parser, raw, config)).toThrow("Parsing is not possible");

يتيح لك بناء جملة BIND اختبار الوظائف بمختلف this القيم ، وفي رأيي يجعل الاختبار أكثر قابلية للقراءة. أنظر أيضا: https://stackoverflow.com/a/13233194/1248889

كما ذكرنا سابقًا ، يجب نقل الوظيفة إلى toThrow نظرًا لأن الوظيفة التي تصفها في اختبارك: "أتوقع أن ترمي هذه الوظيفة X"

expect(() => parser.parse(raw))
  .toThrow(new Error('Parsing is not possible'));

إذا كنت تستخدم ياسمين المطورين يمكنك أيضًا استخدام أحد ما يلي عندما يتناسب مع الموقف ؛

// I just want to know that an error was
// thrown and nothing more about it
expect(() => parser.parse(raw))
  .toThrowAnyError();

أو

// I just want to know that an error of 
// a given type was thrown and nothing more
expect(() => parser.parse(raw))
  .toThrowErrorOfType(TypeError);

أعلم أن هذا هو المزيد من الكود ولكن يمكنك أيضًا القيام بذلك:

try
   do something
   @fail Error("should send a Exception")
 catch e
   expect(e.name).toBe "BLA_ERROR"
   expect(e.message).toBe 'Message'

لمحبي القهوة

expect( => someMethodCall(arg1, arg2)).toThrow()

بالنسبة لأي شخص لا يزال يواجه هذه المشكلة ، فإن الحل المنشور لم ينجح بالنسبة لي واستمر في إلقاء هذا الخطأ: Error: Expected function to throw an exception.أدركت فيما بعد أن الوظيفة التي كنت أتوقع أن ترميها خطأ كانت وظيفة غير متزامنة وكنت أتوقع أن يتم رفض الوعد ثم رمي الخطأ وهذا ما كنت أفعله في الكود الخاص بي:

throw new Error('REQUEST ID NOT FOUND');

وهذا ما فعلته في اختباري وعمل:

it('Test should throw error if request not found', willResolve(() => {
         const promise = service.getRequestStatus('request-id');
                return expectToReject(promise).then((err) => {
                    expect(err.message).toEqual('REQUEST NOT FOUND');
                });
            }));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top