是否有可能扩展Jest/Expect Matcher

Und*_*ion 5 javascript testing matcher jasmine jestjs

我想扩展Jest的isEqual匹配器,以便在比较之前转换期望值(这允许我在测试中使用多行字符串).我需要做的就是通过indentToFirstLinelib中的函数运行期望值:indent-to-first-line在传递给它之前isEqual.显然我不希望在我需要的任何地方都这样做,所以将它折叠成匹配器是有意义的,因为我想要与Jest/Expect的isEqual匹配器相同的功能,所以使用它是有意义的.

我尝试过以下方法:

import indentToFirstLine from 'indent-to-first-line'
import expect from 'expect'

const toEqualMultiline = (received, expectedTemplateString) => {
  const expected = indentToFirstLine(expectedTemplateString)
  return expect(received).toEqual(expected)
}

export default toEqualMultiline
Run Code Online (Sandbox Code Playgroud)

但是expect(received).toEqual(expected)不返回值,所以从我的匹配器返回的值undefined,导致Jest错误:

来自匹配器功能的意外返回.Matcher函数应该按以下格式返回一个对象:{message?:string | function,pass:boolean}'undefined'被返回

我可以toEqual在我自己的匹配器中使用吗?

Nea*_*arl 6

你可以expect.extend()用来做到这一点。如果您正在使用,您可以将此示例代码放在下面,setupTests.ts以便它可以应用于您运行的所有测试:

expect.extend({
  toBeWithinRange(received, min, max) {
    const pass = received >= min && received <= ceiling
    return {
      message: () =>
        `expected ${received} to be in range ${floor} - ${ceiling}`,
      pass,
    }
  },
})
Run Code Online (Sandbox Code Playgroud)

用法

it('should fail', () => {
  expect(13).toBeWithinRange(1, 10)
})
Run Code Online (Sandbox Code Playgroud)

运行上面的测试时,这是输出:

测试1

但我们可以做得更好。查看内置匹配器如何显示错误消息:

测试2

正如您所看到的,错误更容易阅读,因为预期值和接收值具有不同的颜色,并且上面有一个匹配器提示来表示哪个是哪个。

为此,我们需要安装这个包jest-matcher-utils并导入几个方法来漂亮地打印匹配器提示和值:

import { printExpected, printReceived, matcherHint } from "jest-matcher-utils"

const failMessage = (received, min, max) => () => `${matcherHint(
  ".toBeWithinRange",
  "received",
  "min, max"
)}

Expected value to be in range:
  min: ${printExpected(min)}
  max: ${printExpected(max)}
Received: ${printReceived(received)}`

expect.extend({
  toBeWithinRange(received, min, max) {
    const pass = received >= min && received <= max

    return {
      pass,
      message: failMessage(received, min, max),
    }
  },
})
Run Code Online (Sandbox Code Playgroud)

现在看起来好多了,可以帮助您更快地识别问题

测试3

但是上面的代码中有一个小错误,当您否定断言时

expect(3).not.toBeWithinRange(1, 10)
Run Code Online (Sandbox Code Playgroud)

输出是.toBeWithinRange而不是.not.toBeWithinRange

expect(received).toBeWithinRange(min, max)

Expected value to be in range:
  min: 1
  max: 10
Received: 3
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,您可以根据pass值有条件地添加否定词

const failMessage = (received, min, max, not) => () => `${matcherHint(
  `${not ? ".not" : ""}.toBeWithinRange`,
  "received",
  "min, max"
)}

Expected value${not ? " not " : " "}to be in range:
  min: ${printExpected(min)}
  max: ${printExpected(max)}
Received: ${printReceived(received)}`
Run Code Online (Sandbox Code Playgroud)
toBeWithinRange(received, min, max) {
  const pass = received >= min && received <= max

  return {
    pass,
    message: failMessage(received, min, max, pass),
  }
},
Run Code Online (Sandbox Code Playgroud)

现在再次运行测试,您将看到:

通过如果 false

expect(3).not.toBeWithinRange(1, 10)
Run Code Online (Sandbox Code Playgroud)
expect(received).not.toBeWithinRange(min, max)

Expected value not to be in range:
  min: 1
  max: 10
Received: 3
Run Code Online (Sandbox Code Playgroud)

通过如果 true

expect(13).toBeWithinRange(1, 10)
Run Code Online (Sandbox Code Playgroud)
expect(received).toBeWithinRange(min, max)

Expected value to be in range:
  min: 1
  max: 10
Received: 13
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这并没有_完全_回答这个问题。它留下了如何增强现有 Jest 匹配器的主题没有答案。例如,我想添加一个自定义匹配器,通过断言存在预期参数来增强 `toHaveBeenCalledWith` 内置匹配器,例如 `return Expect(fn).toHaveBeenCalledWith(a, b, { foo: "bar “ })` ...但这会导致OP指出的错误消息。我宁愿避免完全重新实现“expect(fn).toHaveBeenCalledWith”。 (2认同)

mga*_*cia 3

如果您使用 jest 并将该匹配器传递给Expect.extend,则可以使用提供的执行上下文来执行 jest equals方法,如下所示:

import indentToFirstLine from 'indent-to-first-line'

export default function toEqualMultiline(received, expectedTemplateString) {
    const expected = indentToFirstLine(expectedTemplateString);
    return {
        message: () => `expected ${received} to equals multiline ${expected}`,
        pass: this.equals(received, expected)
    };
}
Run Code Online (Sandbox Code Playgroud)