如何编写一个期望在Jasmine中引发错误的测试?


490

我正在尝试为Jasmine测试框架编写测试,该测试会出现错误。目前,我正在使用来自GitHubJasmine Node.js集成

在我的Node模块中,我有以下代码:

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.bindstackoverflow.com/a/13233194/294855
Danyal Aytekin 2012年

Answers:


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 2015年

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

我想您可能应该这样写(使用lambda-匿名函数):

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)强烈建议采用这种方法,而不要使用“抛出新的Error()”(原型方法):

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

3
实际上,查看代码toThrow会很高兴地使用一个异常对象或一个字符串。举例来说,查看它正在拨打的对Expected.message的呼叫。
彼得·霍奇森

1
它接缝允许字符串作为没有消息属性的字符串的副作用
mpapis,2010年

1
非常感谢。我仍然接受Pete的回答,因为他的回答使我更清楚地知道我必须使用lambda。还是+1 :-)谢谢!
echox

16
如果抛出一个对象而不是一个Error(如底部的示例所示),那么在支持该对象的浏览器中将不会得到堆栈跟踪。
kybernetikos 2012年

2
令人惊讶的是,@ kybernetikos并不完全正确;如果抛出非Errorjsfiddle.net/k1mxey8j),您仍然会在Chrome控制台中打印出堆栈跟踪。但是,您抛出的对象当然将没有该.stack属性,如果您要设置自动错误报告,这可能很重要。
Mark Amery

24

比创建匿名函数(其唯一目的是包装另一个函数)更优雅的解决方案是使用es5 bind函数。bind函数创建一个新函数,该新函数在被调用时将其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

我将Jasmine的toThrow匹配器替换为以下内容,该匹配器使您可以匹配异常的name属性或message属性。对我来说,这使测试更容易编写且不那么脆弱,因为我可以执行以下操作:

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中正确的Error对象吗?
2013年

1
尼斯评论@马克。没错,name属性不是标准的。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…,但这是错误的吗?
Jess

4
@杰克!我找到了更好的方法!您可以简单地致电expect(blah).toThrow()。没有参数意味着检查它是否抛出。不需要字符串匹配。另请参阅:stackoverflow.com/a/9525172/1804678
Jess 2013年

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
班尼纽格鲍尔

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. 我后来意识到,我原本希望引发错误的函数是一个异步函数,并期望可以实现被拒绝然后引发错误,这就是我在代码中所做的事情:

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
Benjamin,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.