Mocha / Chai mong đợi.to.throw không bắt lỗi ném


257

Tôi đang gặp sự cố khiến Chai expect.to.throwphải hoạt động trong thử nghiệm cho ứng dụng node.js của tôi. Thử nghiệm tiếp tục thất bại đối với lỗi ném, nhưng nếu tôi bọc trường hợp thử trong thử và bắt và xác nhận lỗi đã bắt, nó sẽ hoạt động.

Không expect.to.throwhoạt động như tôi nghĩ nó nên hay cái gì đó?

it('should throw an error if you try to get an undefined property', function (done) {
  var params = { a: 'test', b: 'test', c: 'test' };
  var model = new TestModel(MOCK_REQUEST, params);

  // neither of these work
  expect(model.get('z')).to.throw('Property does not exist in model schema.');
  expect(model.get('z')).to.throw(new Error('Property does not exist in model schema.'));

  // this works
  try { 
    model.get('z'); 
  }
  catch(err) {
    expect(err).to.eql(new Error('Property does not exist in model schema.'));
  }

  done();
});

Sự thất bại:

19 passing (25ms)
  1 failing

  1) Model Base should throw an error if you try to get an undefined property:
     Error: Property does not exist in model schema.

Câu trả lời:


339

Bạn phải vượt qua một chức năng để expect. Như thế này:

expect(model.get.bind(model, 'z')).to.throw('Property does not exist in model schema.');
expect(model.get.bind(model, 'z')).to.throw(new Error('Property does not exist in model schema.'));

Cách bạn đang làm việc đó, bạn đang đi qua để expectcác kết quả của cuộc gọi model.get('z'). Nhưng để kiểm tra xem có thứ gì đó bị ném hay không, bạn phải chuyển một hàm tới expect, hàm này expectsẽ tự gọi. Các bindphương pháp sử dụng ở trên tạo ra một chức năng mới mà khi gọi sẽ gọi model.getvới thiscác thiết lập để giá trị của modelvà đối số set đầu tiên'z' .

Một lời giải thích tốt bindcó thể được tìm thấy ở đây .


Tôi đã vượt qua một chức năng phải không? modeldụ có một hàm gọi là get mà tôi đã vượt qua / được gọi trong mong đợi.
doremi

Không, xem lời giải thích tôi đã thêm trong khi bạn đang viết bình luận của bạn.
Louis

47
Ôi. Tại sao các tài liệu ( chaijs.com/api/bdd/#throw ) không thể hiện cách sử dụng liên kết này? Có vẻ như kịch bản thử nghiệm phổ biến nhất to.throwđang thử nghiệm một điều kiện cụ thể trong một hàm, yêu cầu gọi hàm đó với trạng thái / đối số không hợp lệ. (Đối với vấn đề đó .... tại sao không liên kết sâu của chaijs.com thực sự liên kết sâu?)
ericsoco

Khi bạn vượt qua một số tham số không nên ném, mặc dù vậy bài kiểm tra vẫn là vượt qua.
Alexandros Spyropoulos

6
Lưu ý điều này sẽ không (kể từ tháng 9 năm 2017) hoạt động cho các chức năng không đồng bộ: xem github.com/chaijs/chai/issues/882#issuecomment-322131680 và thảo luận liên quan.
ChrisV

175

Như câu trả lời này nói , bạn cũng có thể chỉ cần bọc mã của mình trong một hàm ẩn danh như thế này:

expect(function(){
    model.get('z');
}).to.throw('Property does not exist in model schema.');

7
Điều này không làm việc cho các cuộc gọi chức năng không đồng bộ. Giả sử model.get là async trả về lời hứa. Tuy nhiên, nó ném một lỗi. Nếu tôi thử cách tiếp cận trên, đó là "Hết giờ" vì chúng tôi phải thông báo "đã hoàn thành" cho mocha. Đồng thời, tôi không thể thử expect(function(){ model.get('z'); }).to.throw('Property does not exist in model schema.').notify(done); Vì không có phương thức thông báo.
Anand N

@AnandN Nếu tôi hiểu vấn đề của bạn, điều này có vẻ như bạn chỉ cần cấu trúc lại mã của mình để xử lý lỗi. Lỗi không được xử lý trong chức năng async có phải là một vấn đề trong ứng dụng thực tế của bạn không?
twiz

2
Cảm ơn twiz đã trả lời của bạn. Chúng tôi đang làm việc trong một môi trường tích hợp, mô-đun sử dụng đảm nhiệm việc nắm bắt các ngoại lệ. Vì vậy, vấn đề là khi chúng tôi cố gắng chạy các trường hợp thử nghiệm đơn vị. Cuối cùng, chúng tôi đã sử dụng cách tiếp cận dưới đây để làm cho nó hoạt động catch (err) { expect(err).equal('Error message to be checked'); done(); }
Anand N

1
Giải pháp tốt trừ khi bạn đang sử dụng thisbên trong hàm được gọi. Sau đó .bindlà cách đúng đắn để đi.
rabbitco

@AnandN Chức năng gọi không đồng bộ không ném , nó từ chối s. Để tham khảo trong tương lai, chai-as-hứa xử lý này khá độc đáo.
dùng5532169

85

Và nếu bạn đã sử dụng ES6 / ES2015 thì bạn cũng có thể sử dụng chức năng mũi tên. Về cơ bản nó giống như sử dụng một hàm ẩn danh bình thường nhưng ngắn hơn.

expect(() => model.get('z')).to.throw('Property does not exist in model schema.');

Có thể có vấn đề với điều này bởi vì các hàm mũi tên có phạm vi xung quanh của chúngthis
Eric Hodonsky

1
@Relic Vâng, rất đúng. Đây cũng có thể là một lợi thế lớn của các chức năng mũi tên. Các hàm mũi tên 'kế thừa' thistừ phạm vi mà chúng được tạo. Thường thì đây có thể là một lợi thế, vì nó tránh sự cần thiết phải nhập các bindhàm vào thisđối tượng của chúng theo cách thủ công.
Stijn de Witt

@StijndeWitt đây không phải là một lợi thế hay bất lợi, đó là kiểm soát phạm vi và chủ ý. Đó thực sự là cú pháp đường để sử dụngbind và luôn liên kết với thisphạm vi cha. Ý định của tôi trong bình luận chỉ là để đảm bảo độc giả nhận thức được sự sụp đổ tiềm năng.
Eric Hodonsky

1
@Relic Có tôi đồng ý với bạn. Nó có thể được sử dụng cho một lợi thế và có thể là một lý do tốt để sử dụng chức năng mũi tên.
Stijn de Witt

75

Câu hỏi này có rất nhiều, rất nhiều bản sao, bao gồm cả những câu hỏi không đề cập đến thư viện khẳng định Chai. Dưới đây là những điều cơ bản được thu thập cùng nhau:

Khẳng định phải gọi hàm, thay vì đánh giá ngay lập tức.

assert.throws(x.y.z);      
   // FAIL.  x.y.z throws an exception, which immediately exits the
   // enclosing block, so assert.throw() not called.
assert.throws(()=>x.y.z);  
   // assert.throw() is called with a function, which only throws
   // when assert.throw executes the function.
assert.throws(function () { x.y.z });   
   // if you cannot use ES6 at work
function badReference() { x.y.z }; assert.throws(badReference);  
   // for the verbose
assert.throws(()=>model.get(z));  
   // the specific example given.
homegrownAssertThrows(model.get, z);
   //  a style common in Python, but not in JavaScript

Bạn có thể kiểm tra các lỗi cụ thể bằng bất kỳ thư viện xác nhận nào:

Nút

  assert.throws(() => x.y.z);
  assert.throws(() => x.y.z, ReferenceError);
  assert.throws(() => x.y.z, ReferenceError, /is not defined/);
  assert.throws(() => x.y.z, /is not defined/);
  assert.doesNotThrow(() => 42);
  assert.throws(() => x.y.z, Error);
  assert.throws(() => model.get.z, /Property does not exist in model schema./)

Nên

  should.throws(() => x.y.z);
  should.throws(() => x.y.z, ReferenceError);
  should.throws(() => x.y.z, ReferenceError, /is not defined/);
  should.throws(() => x.y.z, /is not defined/);
  should.doesNotThrow(() => 42);
  should.throws(() => x.y.z, Error);
  should.throws(() => model.get.z, /Property does not exist in model schema./)

Chai mong

  expect(() => x.y.z).to.throw();
  expect(() => x.y.z).to.throw(ReferenceError);
  expect(() => x.y.z).to.throw(ReferenceError, /is not defined/);
  expect(() => x.y.z).to.throw(/is not defined/);
  expect(() => 42).not.to.throw();
  expect(() => x.y.z).to.throw(Error);
  expect(() => model.get.z).to.throw(/Property does not exist in model schema./);

Bạn phải xử lý các trường hợp ngoại lệ 'thoát' bài kiểm tra

it('should handle escaped errors', function () {
  try {
    expect(() => x.y.z).not.to.throw(RangeError);
  } catch (err) {
    expect(err).to.be.a(ReferenceError);
  }
});

Điều này có thể trông khó hiểu lúc đầu. Giống như đi xe đạp, nó chỉ 'nhấp chuột' mãi mãi sau khi nhấp.


14

ví dụ từ doc ...;)

bởi vì bạn dựa vào thisngữ cảnh:

  • bị mất khi hàm được gọi bởi .throw
  • không có cách nào để nó biết cái này được cho là gì

bạn phải sử dụng một trong các tùy chọn sau:

  • bọc phương thức hoặc hàm gọi bên trong hàm khác
  • ràng buộc bối cảnh

    // wrap the method or function call inside of another function
    expect(function () { cat.meow(); }).to.throw();  // Function expression
    expect(() => cat.meow()).to.throw();             // ES6 arrow function
    
    // bind the context
    expect(cat.meow.bind(cat)).to.throw();           // Bind

Đây là cách tôi làm điều đó là tốt. Tôi thấy rằng, việc triển khai ES6 cho đến nay là cách dễ đọc nhất
cứu trợ.melone

1

Một cách triển khai có thể khác, cồng kềnh hơn so với giải pháp .bind (), nhưng một giải pháp giúp đưa ra điểm mong đợi () yêu cầu một hàm cung cấp thisngữ cảnh cho hàm được bao phủ, bạn có thể sử dụng call(), ví dụ:

expect(function() {model.get.call(model, 'z');}).to.throw('...');


0

Tôi đã tìm thấy một cách tốt đẹp xung quanh nó:

// The test, BDD style
it ("unsupported site", () => {
    The.function(myFunc)
    .with.arguments({url:"https://www.ebay.com/"})
    .should.throw(/unsupported/);
});


// The function that does the magic: (lang:TypeScript)
export const The = {
    'function': (func:Function) => ({
        'with': ({
            'arguments': function (...args:any) {
                return () => func(...args);
            }
        })
    })
};

Phiên bản cũ của tôi dễ đọc hơn nhiều:

it ("unsupported site", () => {
    const args = {url:"https://www.ebay.com/"}; //Arrange
    function check_unsupported_site() { myFunc(args) } //Act
    check_unsupported_site.should.throw(/unsupported/) //Assert
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.