วิธีทดสอบประเภทของข้อยกเว้นการโยนใน Jest


161

ฉันกำลังทำงานกับโค้ดบางอย่างที่ฉันต้องการทดสอบประเภทของข้อยกเว้นที่เกิดจากฟังก์ชัน (เป็น TypeError, ReferenceError ฯลฯ )

กรอบการทดสอบปัจจุบันของฉันคือ AVA และฉันสามารถทดสอบเป็นt.throwsวิธีการโต้แย้งที่สองได้เช่นที่นี่:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  t.is(error.message, 'UNKNOWN ERROR');
});

ฉันเริ่มเขียนการทดสอบของฉันใหม่เป็น Jest และไม่พบวิธีการทำแบบนั้นง่ายๆ เป็นไปได้หรือไม่?

คำตอบ:


225

ใน Jest คุณต้องส่งผ่านฟังก์ชันไปยัง expect (function) .toThrow (blank หรือ type of error)

ตัวอย่าง:

test("Test description", () => {
  const t = () => {
    throw new TypeError();
  };
  expect(t).toThrow(TypeError);
});

หากคุณต้องการทดสอบฟังก์ชันที่มีอยู่ว่าจะพ่นด้วยชุดของอาร์กิวเมนต์หรือไม่คุณต้องรวมฟังก์ชันนั้นไว้ในฟังก์ชันที่ไม่ระบุตัวตนโดยคาดหวัง ()

ตัวอย่าง:

test("Test description", () => {
  expect(() => {http.get(yourUrl, yourCallbackFn)}).toThrow(TypeError);
});

79

แปลกนิดหน่อย แต่ใช้งานได้และ imho อ่านได้ดี:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  try {
      throwError();
      // Fail test if above expression doesn't throw anything.
      expect(true).toBe(false);
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

Catchบล็อกจับข้อยกเว้นของคุณจากนั้นคุณสามารถทดสอบErrorได้ ต้องใช้ Strange expect(true).toBe(false);เพื่อไม่ให้การทดสอบของคุณล้มเหลวหากคาดว่าErrorจะไม่ถูกโยนทิ้ง มิฉะนั้นสายนี้จะไม่สามารถเข้าถึงได้ ( Errorควรจะยกขึ้นก่อนพวกเขา)

แก้ไข: @Kenny Body แนะนำวิธีแก้ปัญหาที่ดีกว่าซึ่งจะปรับปรุงคุณภาพโค้ดหากคุณใช้ expect.assertions()

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  expect.assertions(1);
  try {
      throwError();
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

ดูคำตอบเดิมพร้อมคำอธิบายเพิ่มเติม: วิธีทดสอบประเภทของข้อยกเว้นการโยนใน Jest


18
นี่เป็นวิธีการทดสอบอย่างละเอียดมากสำหรับข้อยกเว้นเมื่อ Jest มีวิธีการตรวจสอบข้อยกเว้น expect.toThrow () อยู่แล้ว: jestjs.io/docs/en/expect.html#tothrowerror
gomisha

4
ใช่ แต่จะทดสอบเฉพาะประเภทไม่ใช่ข้อความหรือเนื้อหาอื่น ๆ และคำถามเกี่ยวกับข้อความทดสอบไม่ใช่ประเภท
bodolsog

2
ฮะ ชอบอันนี้มากเพราะโค้ดของฉันต้องการทดสอบค่าของข้อผิดพลาดที่เกิดขึ้นดังนั้นฉันจึงต้องการอินสแตนซ์ ฉันจะเขียนความคาดหวังที่ผิดพลาดexpect('here').not.toBe('here');เพื่อความสนุก :-)
Valery

4
@ Valery หรือ: expect('to be').not.toBe('to be')ในสไตล์เช็คสเปียร์
Michiel van der Blonk

2
คำตอบน้อยที่สุด!
Edwin Ikechukwu Okonkwo

42

ฉันใช้เวอร์ชันที่กระชับกว่านี้เล็กน้อย:

expect(() => {
  //code block that should throw error
}).toThrow(TypeError) //or .toThrow('expectedErrorMessage')

2
สั้นและแม่นยำ
flapjack

33

จากการสัมผัส (แม้ว่าจะ จำกัด ) ต่อ Jest ฉันพบว่าexpect().toThrow()เหมาะสำหรับกรณีที่คุณต้องการทดสอบเท่านั้นข้อผิดพลาดจะถูกโยนทิ้งไปบางประเภท:

expect(() => functionUnderTest()).toThrow(TypeError);

หรือเกิดข้อผิดพลาดพร้อมข้อความเฉพาะ:

expect(() => functionUnderTest()).toThrow('Something bad happened!');

หากคุณพยายามทำทั้งสองอย่างคุณจะได้รับผลบวกลวง ตัวอย่างเช่นหากโค้ดของคุณพ่นRangeError('Something bad happened!')การทดสอบนี้จะผ่าน:

expect(() => functionUnderTest()).toThrow(new TypeError('Something bad happened!'));

คำตอบโดย bodolsog ซึ่งแนะนำให้ใช้ try / catch นั้นใกล้เคียง แต่แทนที่จะคาดหวังว่าจริงจะเป็นเท็จเพื่อให้แน่ใจว่าการยืนยันที่คาดหวังในการจับจะถูกตีคุณสามารถใช้แทนได้expect.assertions(2)เมื่อเริ่มการทดสอบของคุณโดยที่2จำนวนการยืนยันที่คาดไว้คือ . ฉันรู้สึกว่านี่อธิบายจุดประสงค์ของการทดสอบได้ถูกต้องมากขึ้น

ตัวอย่างทั้งหมดของการทดสอบประเภท AND ข้อความของข้อผิดพลาด:

describe('functionUnderTest', () => {
    it('should throw a specific type of error.', () => {
        expect.assertions(2);

        try {
            functionUnderTest();
        } catch (error) {
            expect(error).toBeInstanceOf(TypeError);
            expect(error).toHaveProperty('message', 'Something bad happened!');
        }
    }); 
});

หากfunctionUnderTest()ไม่เกิดข้อผิดพลาดการยืนยันจะถูกตี แต่expect.assertions(2)จะล้มเหลวและการทดสอบจะล้มเหลว


D'โอ้ ฉันมักจะลืมเกี่ยวกับคุณสมบัติการยืนยันหลาย ๆ อย่างของ Jest (บางทีฉันอาจจะไม่พบว่ามันเป็นเรื่องที่ซับซ้อนที่สุด แต่มันก็ใช้ได้กับกรณีเช่นนี้อย่างแน่นอน!) ไชโย!
kpollock

สิ่งนี้ใช้ได้ดีสำหรับฉัน สิ่งนี้ควรใช้
Ankit Tanna

expect.hasAssertions()อาจเป็นทางเลือกที่ดีกว่าเมื่อการทดสอบไม่มีการยืนยันใด ๆ จากภายนอกcatchเพราะคุณไม่จำเป็นต้องอัปเดตหมายเลขหากคุณเพิ่ม / ลบการยืนยัน
André Sassi

12

ยังไม่ได้ลองด้วยตัวเอง แต่ขอแนะนำให้ใช้ Jest's toThrow assertion ดังนั้นฉันเดาว่าตัวอย่างของคุณจะมีลักษณะดังนี้:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  expect(t).toThrowError('UNKNOWN ERROR');
  //or
  expect(t).toThrowError(TypeError);
});

อีกครั้งยังไม่ได้ทดสอบ แต่คิดว่าน่าจะใช้ได้


8

Jest มีวิธีการ toThrow(error)ทดสอบว่าฟังก์ชันพ่นเมื่อมีการเรียกใช้

ดังนั้นในกรณีของคุณคุณควรเรียกมันว่า:

expect(t).toThrowError(TypeError);

เอกสาร


1
มันจะไม่ทำงานสำหรับกรณี: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });ถ้าวิธีการเยาะเย้ยไม่create async
Sergey

7

Modern jest ช่วยให้คุณตรวจสอบมูลค่าที่ถูกปฏิเสธได้มากขึ้น ตัวอย่างเช่น:

const request = Promise.reject({statusCode: 404})
await expect(request).rejects.toMatchObject({ statusCode: 500 });

จะล้มเหลวด้วยข้อผิดพลาด

Error: expect(received).rejects.toMatchObject(expected)

- Expected
+ Received

  Object {
-   "statusCode": 500,
+   "statusCode": 404,
  }

6

เอกสารมีความชัดเจนเกี่ยวกับวิธีการทำเช่นนี้ nullสมมติว่าผมมีฟังก์ชั่นที่ใช้เวลาสองพารามิเตอร์และมันจะโยนความผิดพลาดถ้าหนึ่งในนั้นคือ

function concatStr(str1, str2) {
  const isStr1 = str1 === null
  const isStr2 = str2 === null
  if(isStr1 || isStr2) {
    throw "Parameters can't be null"
  }
  ... // continue your code

การทดสอบของคุณ

describe("errors", () => {
  it("should error if any is null", () => {
    // notice that the expect has a function that returns the function under test
    expect(() => concatStr(null, "test")).toThrow()
  })
})

5

ในกรณีที่คุณกำลังทำงานกับPromises:

await expect(Promise.reject(new HttpException('Error message', 402)))
  .rejects.toThrowError(HttpException);

นี่เป็นประโยชน์มากขอบคุณที่ช่วยประหยัดเวลาของฉัน !!
Aditya Kresna Permana

3

ฉันลงเอยด้วยการเขียนวิธีอำนวยความสะดวกสำหรับห้องสมุดเครื่องมือทดสอบของเรา

/**
 *  Utility method to test for a specific error class and message in Jest
 * @param {fn, expectedErrorClass, expectedErrorMessage }
 * @example   failTest({
      fn: () => {
        return new MyObject({
          param: 'stuff'
        })
      },
      expectedErrorClass: MyError,
      expectedErrorMessage: 'stuff not yet implemented'
    })
 */
  failTest: ({ fn, expectedErrorClass, expectedErrorMessage }) => {
    try {
      fn()
      expect(true).toBeFalsy()
    } catch (err) {
      let isExpectedErr = err instanceof expectedErrorClass
      expect(isExpectedErr).toBeTruthy()
      expect(err.message).toBe(expectedErrorMessage)
    }
  }

สามารถทำได้โดยใช้คุณสมบัติของ Jests ดูคำตอบของฉันว่าสามารถทำได้อย่างไร - stackoverflow.com/a/58103698/3361387
Kenny Body

3

นอกจากโพสต์ของ Peter Danis เพียงแค่ต้องการเน้นย้ำถึงส่วนของการแก้ปัญหาของเขาที่เกี่ยวข้องกับ "[ผ่าน] ฟังก์ชันไปสู่ความคาดหวัง (ฟังก์ชัน) .toThrow (ว่างเปล่าหรือประเภทของข้อผิดพลาด)"

ใน Jest เมื่อคุณทดสอบกรณีที่ควรเกิดข้อผิดพลาดภายในการคาดหวัง () การตัดฟังก์ชันภายใต้การทดสอบคุณจะต้องจัดเตรียมเลเยอร์การตัดฟังก์ชันลูกศรเพิ่มเติมหนึ่งชั้นเพื่อให้ทำงานได้ กล่าวคือ

ผิด (แต่แนวทางเชิงตรรกะของคนส่วนใหญ่):

expect(functionUnderTesting();).toThrow(ErrorTypeOrErrorMessage);

ขวา:

expect(() => { functionUnderTesting(); }).toThrow(ErrorTypeOrErrorMessage);

มันแปลกมาก แต่น่าจะทำให้การทดสอบทำงานสำเร็จ


1

ลอง
expect(t).rejects.toThrow()


4
ทำไมtry? ไม่มีความพยายาม - แต่ตอบ หากนี่คือคำตอบโปรดอธิบายเพิ่มเติม สิ่งที่คุณเพิ่มในคำตอบที่มีอยู่?
dWinder

7
ฉันคิดว่า @Razim กำลังบอกว่าคุณควรลองใช้วิธีแก้ปัญหาไม่ใช่ใช้ try catch
ทอม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.