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

IT技术 javascript testing node.js jasmine
2021-03-11 22:33:46

我正在尝试为Jasmine 测试框架编写一个预计会出错的测试目前我正在使用来自 GitHubJasmine Node.js 集成

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

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()和其他一些变体,但无法弄清楚如何使其工作。

6个回答

尝试改用匿名函数:

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

您应该将一个函数传递给expect(...)调用。您的错误代码:

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

正在尝试实际调用 parser.parse(raw)以尝试将结果传递到expect(...),

在我看来,当包装在匿名函数中时,测试的意图更加明显。此外,它在所有测试中保持一致,例如,当您必须将参数传递给目标函数以使其抛出时。
2021-04-20 22:33:46
如果您也不需要传递参数,您也可以将函数传递给期望: expect(parser.parse).toThrow(...)
2021-04-30 22:33:46
@SubmittedDenied:这通常不起作用!如果parser.parse使用this,在没有上下文的情况下传递它会产生意想不到的结果。你可以通过parser.parse.bind(parser),但老实说......匿名函数会更优雅。
2021-05-11 22:33:46
有用的提示:您可以简单地调用expect(blah).toThrow(). 没有参数意味着检查它是否抛出。不需要字符串匹配。另见:stackoverflow.com/a/9525172/1804678
2021-05-12 22:33:46

您正在使用:

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 强烈推荐这种方法,而不是使用“throw new Error()”(原型方法):

throw {
   name: "Error",
   message: "Parsing is not possible"
}
如果您抛出一个对象而不是一个错误(如底部的示例所示),那么您将不会在支持它的浏览器中获得堆栈跟踪。
2021-04-15 22:33:46
它接缝允许字符串作为没有消息属性的字符串的副作用
2021-04-18 22:33:46
@kybernetikos 令人惊讶,并非完全正确;如果您抛出非Errorjsfiddle.net/k1mxey8j),您仍然会在 Chrome 控制台中打印堆栈跟踪但是,您抛出的对象当然不会具有该.stack属性,如果您想设置自动错误报告,这可能很重要
2021-04-30 22:33:46
实际上查看 toThrow 的代码会很高兴地接受一个异常对象 / 或 / 一个字符串。例如,查看它对 expected.message 进行的调用。
2021-05-05 22:33:46

如前所述,需要传递一个函数,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);
expect(foo).toThrowError(TypeError);在 Jasmine 2.5 中:jasmine.github.io/2.5/introduction
2021-05-01 22:33:46

比创建一个匿名函数更优雅的解决方案是使用 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");

bind 语法允许您测试具有不同this值的函数,并且在我看来使测试更具可读性。另见:https : //stackoverflow.com/a/13233194/1248889

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

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