Bagaimana cara menulis tes yang mengharapkan Kesalahan dilemparkan ke Jasmine?

490

Saya mencoba menulis tes untuk Jasmine Test Framework yang mengharapkan kesalahan. Saat ini saya menggunakan integrasi Jasmine Node.js dari GitHub .

Dalam modul Node saya, saya memiliki kode berikut:

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

Sekarang saya mencoba menulis tes yang mengharapkan kesalahan ini:

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

Saya juga mencoba Error()dan beberapa varian lainnya dan tidak tahu cara membuatnya bekerja.

echox
sumber
4
Untuk meneruskan argumen ke fungsi yang sedang diuji, tanpa menggunakan fungsi anonim, coba Function.bind: stackoverflow.com/a/13233194/294855
Danyal Aytekin

Jawaban:

802

Anda harus menyampaikan fungsi ke dalam expect(...)panggilan. Kode yang Anda miliki di sini:

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

sedang mencoba untuk benar-benar memanggil parser.parse(raw) upaya untuk meneruskan hasilnya expect(...),

Coba gunakan fungsi anonim:

expect( function(){ parser.parse(raw); } ).toThrow(new Error("Parsing is not possible"));
Pete Hodgson
sumber
28
Jika Anda tidak perlu meneruskan argumen juga, Anda juga dapat hanya melewati fungsi untuk mengharapkan:expect(parser.parse).toThrow(...)
Diserahkan
60
Kiat bermanfaat: Anda cukup menelepon expect(blah).toThrow(). Tidak ada argumen yang berarti memeriksa untuk melihat apakah ia melempar sama sekali Tidak diperlukan pencocokan string. Lihat juga: stackoverflow.com/a/9525172/1804678
Jess
1
Menurut pendapat saya, lebih jelas maksud tes ketika membungkus dalam fungsi anonim. Selain itu, tetap konsisten di antara semua tes ketika, misalnya, Anda harus meneruskan parameter ke fungsi target untuk membuatnya melempar.
Beez
10
@SubmittedDenied: Ini tidak berfungsi secara umum! Jika parser.parsedigunakan this, melewatinya tanpa konteks akan menghasilkan hasil yang tidak terduga. Anda bisa lulus parser.parse.bind(parser), tapi jujur ​​... fungsi anonim akan lebih elegan.
mhelvens
2
@ LanceKind minta maaf untuk necro, tetapi, alasan Anda harus melewati fungsi adalah bahwa nilai akan dievaluasi dan melemparkan pengecualian sebelum bahkan dimasukkan ke dalam harapan.
1gLassitude
68

Anda menggunakan:

expect(fn).toThrow(e)

Tetapi jika Anda akan melihat komentar fungsi (yang diharapkan adalah string):

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

Saya kira Anda mungkin harus menulis seperti ini (menggunakan lambda - fungsi anonim):

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

Ini dikonfirmasi dalam contoh berikut:

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

Douglas Crockford sangat merekomendasikan pendekatan ini, daripada menggunakan "throw new Error ()" (cara prototyping):

throw {
   name: "Error",
   message: "Parsing is not possible"
}
Andrzej Śliwa
sumber
3
Sebenarnya melihat kode toThrow akan dengan senang hati mengambil objek pengecualian / atau / string. Lihat panggilan yang dibuatnya seperti yang diharapkan. Pesan misalnya.
Pete Hodgson
1
Lapisan ini memungkinkan string sebagai efek samping dari string yang tidak memiliki properti pesan
mpapis
1
Terima kasih banyak, ini berhasil. Saya masih menerima jawaban Pete, karena jawabannya membuatnya lebih jelas bagi saya, bahwa saya harus menggunakan lambda. Masih memberi +1 :-) Terima kasih!
echox
16
Jika Anda melempar objek daripada Galat (seperti pada contoh Anda di bagian bawah), maka Anda tidak akan mendapatkan jejak tumpukan di browser yang mendukungnya.
kybernetikos
2
@kybernetikos secara mengejutkan, tidak sepenuhnya benar; Anda masih akan mendapatkan jejak tumpukan yang dicetak di konsol Chrome jika Anda melempar non- Error( jsfiddle.net/k1mxey8j ). Namun, objek yang Anda lempar tentu saja tidak akan memiliki .stackproperti, yang mungkin penting jika Anda ingin mengatur pelaporan kesalahan otomatis .
Mark Amery
24

Solusi yang lebih elegan daripada membuat fungsi anonim yang tujuan utamanya adalah untuk membungkus yang lain, adalah menggunakan bindfungsi es5 . Fungsi bind menciptakan fungsi baru yang, ketika dipanggil, memiliki thiskata kunci yang disetel ke nilai yang disediakan, dengan urutan argumen tertentu sebelum yang disediakan ketika fungsi baru dipanggil.

Dari pada:

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

Mempertimbangkan:

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

Sintaks mengikat memungkinkan Anda untuk menguji fungsi dengan thisnilai yang berbeda , dan menurut saya membuat tes lebih mudah dibaca. Lihat juga: https://stackoverflow.com/a/13233194/1248889

Jonathan Gawrych
sumber
23

Saya mengganti korek api toThrow Jasmine dengan yang berikut, yang memungkinkan Anda mencocokkan pada properti nama pengecualian atau properti pesannya. Bagi saya ini membuat tes lebih mudah untuk ditulis dan kurang rapuh, karena saya dapat melakukan hal berikut:

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

dan kemudian uji dengan yang berikut ini:

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

Ini memungkinkan saya mengubah pesan pengecualian nanti tanpa melanggar tes, ketika yang penting adalah bahwa itu melemparkan jenis pengecualian yang diharapkan.

Ini adalah pengganti toThrow yang memungkinkan ini:

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;
};
Jake
sumber
4
Pendekatan yang bagus tetapi apakah {name: '...', message: '...'} objek Galat yang tepat dalam JavaScript?
Marc
1
Komentar bagus @Marc. Anda benar, nama properti tidak standar. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , tetapi apakah itu salah?
Jess
4
@Jake! Saya menemukan cara yang lebih baik !!!! Anda cukup menelepon expect(blah).toThrow(). Tidak ada argumen yang berarti memeriksa untuk melihat apakah ia melempar sama sekali Tidak diperlukan pencocokan string. Lihat juga: stackoverflow.com/a/9525172/1804678
Jess
5
Terima kasih Jess - itu benar, tapi kemudian mungkin ada kesalahan lain, seperti TypeError, dan pengujian saya akan salah, menutupi bug nyata.
Jake
4
Anda juga sekarang dapat menggunakan RegEx sebagai argumen untuk toThrow ().
Tony O'Hagan
21

Seperti yang disebutkan sebelumnya, fungsi harus dilewatkan toThrowkarena fungsi yang Anda jelaskan dalam pengujian Anda: "Saya berharap fungsi ini untuk membuang x"

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

Jika menggunakan Jasmine-Matchers Anda juga dapat menggunakan salah satu dari berikut ini ketika mereka sesuai dengan situasi;

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

atau

// I just want to know that an error of 
// a given type was thrown and nothing more
expect(() => parser.parse(raw))
  .toThrowErrorOfType(TypeError);
Jamie Mason
sumber
3
Ada expect(foo).toThrowError(TypeError);dalam Jasmine 2.5: jasmine.github.io/2.5/introduction
Benny Neugebauer
9

Saya tahu ini lebih banyak kode tetapi Anda juga dapat melakukannya:

try
   do something
   @fail Error("should send a Exception")
 catch e
   expect(e.name).toBe "BLA_ERROR"
   expect(e.message).toBe 'Message'
tolbard
sumber
Saya cenderung menyukai aspek 'mendokumentasikan diri' untuk ini ... membuatnya sangat jelas bahwa Anda sedang menguji status kesalahan
JRulle
6

Untuk pecinta kopi

expect( => someMethodCall(arg1, arg2)).toThrow()
fernandohur
sumber
3

Bagi siapa pun yang mungkin masih menghadapi masalah ini, bagi saya solusi yang diposting tidak bekerja dan terus melempar kesalahan ini: Error: Expected function to throw an exception. Saya kemudian menyadari bahwa fungsi yang saya harapkan untuk melakukan kesalahan adalah fungsi async dan mengharapkan janji untuk ditolak dan kemudian buang kesalahan dan itulah yang saya lakukan dalam kode saya:

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

dan itulah yang saya lakukan dalam pengujian dan berhasil:

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');
                });
            }));
arifaBatool
sumber
Terima kasih untuk ini. Saya sangat bingung, tetapi komentar Anda masuk akal. Saya memperbaiki masalah ini dengan menggunakan expectAsync jasmine.github.io/api/3.3/async-matchers.html
Benjamin