จะเขียนแบบทดสอบที่คาดว่าจะเกิดข้อผิดพลาดในจัสมินได้อย่างไร?


490

ฉันพยายามเขียนการทดสอบสำหรับกรอบการทดสอบจัสมินซึ่งคาดว่าจะเกิดข้อผิดพลาด ในขณะที่ฉันใช้บูรณาจัสมิน 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()และตัวแปรอื่น ๆ และไม่สามารถหาวิธีทำให้มันใช้งานได้


4
หากต้องการส่งผ่านข้อโต้แย้งไปยังฟังก์ชันที่กำลังทดสอบโดยไม่ใช้ฟังก์ชั่นนิรนามลองFunction.bind: stackoverflow.com/a/13233194/294855
Danyal Aytekin

คำตอบ:


802

คุณควรจะผ่านฟังก์ชั่นในการ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"));

28
หากคุณไม่จำเป็นต้องผ่านการขัดแย้งด้วยคุณสามารถส่งผ่านฟังก์ชั่นเพื่อคาดหวัง:expect(parser.parse).toThrow(...)
SubmittedDenied

60
เคล็ดลับที่มีประโยชน์: คุณสามารถโทรexpect(blah).toThrow()ได้ ไม่มีข้อโต้แย้งหมายถึงการตรวจสอบเพื่อดูว่ามันจะพ่นเลย ไม่จำเป็นต้องมีการจับคู่สตริง ดูเพิ่มเติมที่: stackoverflow.com/a/9525172/1804678
Jess

1
ในความคิดของฉันมันชัดเจนมากขึ้นเป็นความตั้งใจของการทดสอบเมื่อห่อในฟังก์ชั่นที่ไม่ระบุชื่อ นอกจากนี้ยังคงสอดคล้องระหว่างการทดสอบทั้งหมดเมื่อตัวอย่างเช่นคุณต้องส่งพารามิเตอร์ไปยังฟังก์ชันเป้าหมายเพื่อให้มีการโยน
Beez

10
@SubmittedDenied: วิธีนี้ใช้ไม่ได้! หากparser.parseใช้การthisส่งผ่านโดยไม่มีบริบทจะให้ผลลัพธ์ที่ไม่คาดคิด คุณสามารถผ่านไปparser.parse.bind(parser)ได้ แต่โดยสุจริต ... ฟังก์ชั่นนิรนามจะดูสง่างามกว่า
mhelvens

2
@ LanceKind ขออภัยที่ necro แต่เหตุผลที่คุณต้องผ่านฟังก์ชั่นก็คือค่าจะถูกประเมินและส่งข้อยกเว้นก่อนที่มันจะถูกส่งผ่านไปยังที่คาดหวัง
1gLassitude

68

คุณกำลังใช้:

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

ฉันคิดว่าคุณน่าจะเขียนแบบนี้ (ใช้แลมบ์ดา - ฟังก์ชั่นนิรนาม):

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

นี่คือการยืนยันในตัวอย่างต่อไปนี้:

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

Douglas Crockford แนะนำวิธีนี้อย่างยิ่งแทนที่จะใช้ "throw new Error ()" (วิธีการสร้างต้นแบบ):

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

3
การดูรหัสจริงโยนอย่างมีความสุขจะใช้วัตถุยกเว้น / หรือ / สตริง ตรวจสอบการโทรที่กำลังดำเนินการกับข้อความที่คาดไว้ตัวอย่างเช่น
Pete Hodgson

1
มันตะเข็บเพื่อให้สตริงเป็นผลข้างเคียงของสตริงที่ไม่มีคุณสมบัติข้อความ
mpapis

1
ขอบคุณมากที่ทำงาน ฉันยังคงยอมรับคำตอบของ Pete เพราะคำตอบของเขาทำให้ฉันชัดเจนยิ่งขึ้นว่าฉันต้องใช้แลมบ์ดา ยัง +1 :-) ขอบคุณ!
echox

16
หากคุณโยนวัตถุมากกว่าข้อผิดพลาด (เช่นในตัวอย่างที่ด้านล่าง) คุณจะไม่ได้รับการติดตามสแต็กในเบราว์เซอร์ที่สนับสนุน
kybernetikos

2
@kybernetikos แปลกใจไม่เป็นความจริงเลย คุณจะยังคงได้รับการติดตามสแต็กในคอนโซล Chrome หากคุณไม่ใส่Error( jsfiddle.net/k1mxey8j ) อย่างไรก็ตามวัตถุที่ถูกโยนทิ้งของคุณจะไม่มี.stackคุณสมบัติซึ่งอาจมีความสำคัญหากคุณต้องการตั้งค่าการรายงานข้อผิดพลาดอัตโนมัติ
Mark Amery

24

โซลูชันที่สง่างามกว่าการสร้างฟังก์ชั่นนิรนามซึ่งมีจุดประสงค์เดียวคือห่อคำอื่นให้ใช้bindฟังก์ชั่นของ es5 ฟังก์ชั่นผูกสร้างฟังก์ชั่นใหม่ที่เมื่อเรียกว่ามีการthisตั้งค่าคำหลักเป็นค่าที่ให้ไว้กับลำดับของข้อโต้แย้งที่กำหนดก่อนหน้าใด ๆ ที่มีให้เมื่อมีการเรียกใช้ฟังก์ชั่นใหม่

แทน:

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

พิจารณา:

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

ไวยากรณ์ผูกช่วยให้คุณสามารถทดสอบฟังก์ชั่นที่มีthisค่าที่แตกต่างกันและในความคิดของฉันทำให้การทดสอบอ่านง่ายขึ้น ดูเพิ่มเติมที่: https://stackoverflow.com/a/13233194/1248889


23

ฉันแทนที่การจับคู่ toThrow ของจัสมินด้วยสิ่งต่อไปนี้ซึ่งช่วยให้คุณจับคู่กับคุณสมบัติชื่อข้อยกเว้นหรือคุณสมบัติข้อความ สำหรับฉันนี่ทำให้การทดสอบง่ายขึ้นในการเขียนและเปราะน้อยลงเนื่องจากฉันสามารถทำสิ่งต่อไปนี้:

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

4
วิธีการที่ดี แต่เป็น {name: '... ', message: '... '} เป็นข้อผิดพลาดที่เหมาะสมใน JavaScript?
Marc

1
ความคิดเห็นที่ดี @ Marc คุณพูดถูกแล้วคุณสมบัติชื่อนั้นไม่ได้มาตรฐาน developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ......แต่นั่นมันผิดใช่ไหม
Jess

4
@Jake! ฉันพบวิธีที่ดีกว่า !!!! expect(blah).toThrow()คุณก็สามารถโทร ไม่มีข้อโต้แย้งหมายถึงการตรวจสอบเพื่อดูว่ามันจะพ่นเลย ไม่จำเป็นต้องมีการจับคู่สตริง ดูเพิ่มเติมที่: stackoverflow.com/a/9525172/1804678
Jess

5
ขอบคุณ Jess - จริง แต่ก็อาจจะมีข้อผิดพลาดอื่น ๆ เช่น TypeError และการทดสอบของฉันจะผ่านไปอย่างไม่ถูกต้องปิดบังข้อผิดพลาดจริง
เจค

4
ตอนนี้คุณสามารถใช้ RegEx เป็นอาร์กิวเมนต์สำหรับ toThrow ()
Tony O'Hagan

21

ดังกล่าวก่อนหน้านี้ฟังก์ชั่นจะต้องผ่านไปtoThrowเพราะมันเป็นฟังก์ชั่นที่คุณอธิบายในการทดสอบของคุณ: "ฉันคาดหวังว่าฟังก์ชั่นนี้จะโยน x"

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

หากใช้Jasmine-Matchersคุณสามารถใช้ข้อใดข้อหนึ่งต่อไปนี้เมื่อพวกเขาเหมาะสมกับสถานการณ์

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

3
มันexpect(foo).toThrowError(TypeError);อยู่ในจัสมิน 2.5: jasmine.github.io/2.5/introduction
Benny Neugebauer

9

ฉันรู้ว่าเป็นรหัสเพิ่มเติม แต่คุณสามารถทำได้:

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

ฉันมักจะชอบด้าน 'การจัดทำเอกสารด้วยตนเอง' ในเรื่องนี้ ... ทำให้เห็นได้ชัดว่าคุณกำลังทดสอบข้อผิดพลาดในหน่วย
JRulle


3

สำหรับทุกคนที่ยังอาจประสบปัญหานี้สำหรับฉันวิธีการโพสต์ไม่ทำงานและมันยังคงทิ้งข้อผิดพลาดนี้: Error: Expected function to throw an exception. ฉันรู้ในภายหลังว่าฟังก์ชั่นที่ฉันคาดว่าจะโยนข้อผิดพลาดเป็นฟังก์ชั่น async และคาดว่าจะสัญญา ถูกปฏิเสธแล้วโยนข้อผิดพลาดและนั่นคือสิ่งที่ฉันทำในรหัสของฉัน:

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

ขอบคุณสำหรับสิ่งนี้. ฉันสับสนมาก แต่ความคิดเห็นของคุณสมเหตุสมผลดี ผมแก้ไขปัญหาโดยใช้ใหม่expectAsync jasmine.github.io/api/3.3/async-matchers.html
เบนจามิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.