Làm thế nào để viết một bài kiểm tra dự kiến ​​Lỗi sẽ được ném vào Jasmine?


490

Tôi đang cố gắng viết một bài kiểm tra cho Khung kiểm tra Jasmine có lỗi. Hiện tại tôi đang sử dụng tích hợp Jasmine Node.js từ GitHub .

Trong mô-đun Node của tôi, tôi có đoạn mã sau:

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

Bây giờ tôi cố gắng viết một bài kiểm tra có lỗi này:

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

Tôi cũng đã thử Error()và một số biến thể khác và không thể tìm ra cách làm cho nó hoạt động.


4
Để truyền đối số cho hàm đang được kiểm tra mà không cần sử dụng hàm ẩn danh, hãy thử Function.bind: stackoverflow.com/a/13233194/294855
Danyal Aytekin

Câu trả lời:


802

bạn sẽ chuyển một chức năng vào expect(...)cuộc gọi. Mã bạn có ở đây:

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

đang cố gắng thực sự kêu gọi parser.parse(raw) một nỗ lực để chuyển kết quả vào expect(...),

Thay vào đó, hãy thử sử dụng một hàm ẩn danh:

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

28
Nếu bạn cũng không cần truyền đối số, bạn cũng có thể chuyển hàm để mong đợi:expect(parser.parse).toThrow(...)
gửi

60
Lời khuyên hữu ích: Bạn chỉ cần gọi expect(blah).toThrow(). Không có đối số có nghĩa là kiểm tra để xem rằng nó ném tất cả. Không yêu cầu kết hợp chuỗi. Xem thêm: stackoverflow.com/a/9525172/1804678
Jess

1
Theo tôi, rõ ràng hơn là mục đích của bài kiểm tra khi gói trong một hàm ẩn danh. Ngoài ra, nó vẫn nhất quán trong số tất cả các thử nghiệm khi, ví dụ, khi bạn phải truyền tham số cho hàm mục tiêu để làm cho nó ném.
Beez

10
@SubmitDenied: Điều này không hoạt động nói chung! Nếu parser.parsesử dụng this, vượt qua nó mà không có ngữ cảnh sẽ tạo ra kết quả bất ngờ. Bạn có thể vượt qua parser.parse.bind(parser), nhưng thành thật mà nói ... một chức năng ẩn danh sẽ thanh lịch hơn.
mrcvens

2
@LanceKind xin lỗi với necro, nhưng, lý do bạn phải vượt qua một hàm là giá trị sẽ được đánh giá và đưa ra một ngoại lệ trước khi nó được chuyển đến kỳ vọng.
1gLassitude

68

Bạn đang sử dụng:

expect(fn).toThrow(e)

Nhưng nếu bạn có một cái nhìn về nhận xét chức năng (dự kiến ​​là chuỗi):

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

Tôi cho rằng có lẽ bạn nên viết nó như thế này (sử dụng lambda - hàm ẩn danh):

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

Điều này được xác nhận trong ví dụ sau:

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

Douglas Crockford khuyến nghị mạnh mẽ phương pháp này, thay vì sử dụng "throw new Error ()" (cách tạo mẫu):

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

3
Trên thực tế, nhìn vào mã toThrow sẽ vui vẻ nhận một đối tượng ngoại lệ / hoặc / một chuỗi. Kiểm tra các cuộc gọi mà nó đang thực hiện để dự kiến.message chẳng hạn.
Pete Hodgson

1
Nó có đường nối để cho phép chuỗi là hiệu ứng phụ của chuỗi không có thuộc tính thông báo
mpapis

1
Cảm ơn rất nhiều mà đã làm việc. Tôi vẫn chấp nhận câu trả lời của Pete, beacuse câu trả lời của anh ấy đã nói rõ hơn với tôi, rằng tôi phải sử dụng lambda. Vẫn +1 :-) Cảm ơn!
echox

16
Nếu bạn ném một đối tượng chứ không phải là Lỗi (như trong ví dụ của bạn ở phía dưới), thì bạn sẽ không nhận được dấu vết ngăn xếp trong các trình duyệt hỗ trợ nó.
kybernetikos

2
@kybernetikos đáng ngạc nhiên, không hoàn toàn đúng; bạn vẫn sẽ nhận được dấu vết ngăn xếp được in trong bảng điều khiển Chrome nếu bạn ném không Error( jsfiddle.net/k1mxey8j ). Tuy nhiên, tất nhiên đối tượng bị ném của bạn sẽ không có thuộc .stacktính, điều này có thể quan trọng nếu bạn muốn thiết lập báo cáo lỗi tự động .
Mark Amery

24

Một giải pháp tao nhã hơn là tạo ra một chức năng ẩn danh, mục đích duy nhất của họ là bọc một bindchức năng khác, là sử dụng chức năng của es5 . Hàm liên kết tạo ra một hàm mới, khi được gọi, có thistừ khóa được đặt thành giá trị được cung cấp, với một chuỗi các đối số đã cho trước bất kỳ được cung cấp nào khi hàm mới được gọi.

Thay vì:

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

Xem xét:

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

Cú pháp liên kết cho phép bạn kiểm tra các hàm với các thisgiá trị khác nhau và theo ý kiến ​​của tôi làm cho bài kiểm tra dễ đọc hơn. Xem thêm: https://stackoverflow.com/a/13233194/1248889


23

Tôi thay thế trình so khớp toThrow của Jasmine bằng cách sau, cho phép bạn khớp với thuộc tính tên của ngoại lệ hoặc thuộc tính thông báo của nó. Đối với tôi điều này làm cho các bài kiểm tra dễ viết hơn và ít giòn hơn, vì tôi có thể làm như sau:

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

và sau đó kiểm tra với những điều sau đây:

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

Điều này cho phép tôi điều chỉnh thông báo ngoại lệ sau đó mà không phá vỡ các bài kiểm tra, khi điều quan trọng là nó đã ném loại ngoại lệ dự kiến.

Đây là sự thay thế cho toThrow cho phép điều này:

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
Một cách tiếp cận hay nhưng {name: '...', message: '...'} là một đối tượng Lỗi thích hợp trong JavaScript?
Marc

1
Bình luận tốt đẹp @Marc. Bạn nói đúng, thuộc tính tên không chuẩn. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ , nhưng điều đó có sai không?
Jess

4
@Jake! Tôi tìm thấy một cách tốt hơn !!!! Bạn chỉ có thể gọi expect(blah).toThrow(). Không có đối số có nghĩa là kiểm tra để xem rằng nó ném tất cả. Không yêu cầu kết hợp chuỗi. Xem thêm: stackoverflow.com/a/9525172/1804678
Jess

5
Cảm ơn Jess - đó là sự thật, nhưng sau đó nó có thể gây ra một số lỗi khác, như TypeError, và bài kiểm tra của tôi sẽ vượt qua không chính xác, che giấu một lỗi thực sự.
Jake

4
Bây giờ bạn cũng có thể sử dụng RegEx làm đối số cho toThrow ().
Tony O'Hagan

21

Như đã đề cập trước đây, một chức năng cần phải được chuyển đến toThrowvì đó là chức năng bạn mô tả trong bài kiểm tra của mình: "Tôi hy vọng chức năng này sẽ ném x"

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

Nếu sử dụng Jasmine-Matchers, bạn cũng có thể sử dụng một trong những cách sau đây khi chúng phù hợp với tình huống;

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

hoặc là

// 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);ở Jasmine 2.5: jasmine.github.io/2.5/int sinh sản
Benny Neugebauer

9

Tôi biết đó là nhiều mã hơn nhưng bạn cũng có thể làm:

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

Tôi có xu hướng thích khía cạnh 'tự ghi lại tài liệu' cho điều này ... làm cho nó rất rõ ràng rằng bạn đang kiểm tra đơn vị trạng thái lỗi
JRulle

6

Dành cho những người yêu thích cà phê

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

3

Đối với bất kỳ ai vẫn có thể phải đối mặt với vấn đề này, đối với tôi, giải pháp được đăng đã không hoạt động và nó vẫn tiếp tục gây ra lỗi này: Error: Expected function to throw an exception. Sau đó tôi nhận ra rằng chức năng mà tôi dự kiến ​​sẽ đưa ra lỗi là một chức năng không đồng bộ và đang mong đợi lời hứa bị từ chối và sau đó ném lỗi và đó là những gì tôi đã làm trong mã của mình:

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

và đó là những gì tôi đã làm trong bài kiểm tra của mình và nó đã hoạt động:

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

Cám ơn vì cái này. Tôi đã rất bối rối, nhưng nhận xét của bạn có ý nghĩa hoàn hảo. Tôi đã khắc phục sự cố bằng expectAsync jasmine.github.io/api/3.3/async-matchers.html
Benjamin
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.