如何将自定义消息添加到 Jest 期望?

Posted

技术标签:

【中文标题】如何将自定义消息添加到 Jest 期望?【英文标题】:How to add custom message to Jest expect? 【发布时间】:2018-01-03 00:33:51 【问题描述】:

图片如下测试用例:

it('valid emails checks', () => 
  ['abc@y.com', 'a@b.nz'/*, ...*/].map(mail => 
    expect(isValid(mail)).toBe(true);
  );
);

我想为每封电子邮件添加自动生成的消息,例如 Email 'f@f.com' should be valid,以便轻松找到失败的测试用例。

类似:

// .map(email =>
expect(isValid(email), `Email $email should be valid`).toBe(true);

Jest有可能吗?

在 Chai 中可以使用 expect(value, 'custom fail message').to.be... 之类的第二个参数,而在 Jasmine 中似乎可以使用 .because 子句。但在 Jest 中找不到解决方案。

【问题讨论】:

【参考方案1】:

你试试这个扩展 jest 的库:https://github.com/mattphillips/jest-expect-message

test('returns 2 when adding 1 and 1', () => 
  expect(1 + 1, 'Woah this should be 2!').toBe(3);
);

【讨论】:

这是一种非常干净的方法,应该优先于尝试和捕获解决方案。 这似乎与 TS 不准确 我正在使用这个库和打字稿,它完美地工作 @Marc 确保您已按照Setup 的jest-expect-message 说明进行操作。 Jest 需要配置为使用该模块。 不适用于 jest 27+。不过有一个open issue。【参考方案2】:

我认为不可能提供这样的信息。但是你可以定义自己的matcher。

例如,您可以创建一个toBeValid(validator) 匹配器:

expect.extend(
  toBeValid(received, validator) 
    if (validator(received)) 
      return 
        message: () => `Email $received should NOT be valid`,
        pass: true
      ;
     else 
      return 
        message: () => `Email $received should be valid`,
        pass: false
      ;
    
  
);

然后你像这样使用它:

expect(mail).toBeValid(isValid);

注意:toBeValid 会为两种情况(成功和失败)返回一条消息,因为它允许您使用.not。根据您是否希望它通过验证,测试将失败并显示相应的消息。

expect(mail).toBeValid(isValid);
// pass === true: Test passes
// pass === false: Failure: Email ... should be valid

expect(mail).not.toBeValid(isValid);
// pass === true: Failure: Email ... should NOT be valid
// pass === false: Test passes

【讨论】:

非常感谢,一个问题 - 在某个文件中使用它时,它是该测试文件的本地文件,对吗?如果我想在某个全局范围内使用该功能,我应该使用beforeAll 吗? 我不完全确定它是否仅适用于文件,但如果它在整个测试运行期间都可用,它可能取决于首先执行哪个文件以及何时并行运行测试,这成为问题。但是您可以做的是,将toBeValid 函数导出到帮助文件中,然后将其导入并在您需要的任何地方使用expect.extend( toBeValid ) 注册。 伙计,我不会敲你的答案,但我不敢相信开玩笑的匹配器缺少这个。这是一个基本概念。【参考方案3】:

你可以使用try-catch:

try 
    expect(methodThatReturnsBoolean(inputValue)).toBeTruthy();

catch (e) 
    throw new Error(`Something went wrong with value $JSON.stringify(inputValue)`, e);

【讨论】:

这个解决方案是个坏主意,当测试失败时,您无法做出改变,因为返回为 false 或 methodThatReturnsBoolean 抛出异常。 @dave008,是的,这两种情况都没有通过测试,但是错误消息非常容易解释并且取决于出了什么问题。 @Marc 你的代码一定有问题——在示例中,expect 函数只有一个参数/值。【参考方案4】:

虽然这不是一个通用的解决方案,但是对于想要自定义异常消息来区分循环中的项目的常见情况,您可以改用 Jest 的test.each。

例如,您的示例代码:

it('valid emails checks', () => 
  ['abc@y.com', 'a@b.nz'/*, ...*/].map(mail => 
    expect(isValid(mail)).toBe(true);
  );
);

可以改为

test.each(
    ['abc@y.com', 'a@b.nz'/*, ...*/],
    'checks that email %s is valid',
    mail => 
        expect(isValid(mail)).toBe(true);
    
);

【讨论】:

【参考方案5】:

2021年答案

我在为Mintbean 编写的一些代码中做到了这一点,将我的it 块放在forEach 中。

通过这样做,我能够很好地近似您所描述的内容。

优点:

出色的“本机”错误报告 将断言计为自己的测试 无需插件。

下面是你的代码在我的方法中的样子:


// you can't nest "it" blocks within each other,
// so this needs to be inside a describe block. 
describe('valid emails checks', () => 
  ['abc@y.com', 'a@b.nz'/*, ...*/].forEach(mail => 
    // here is where the magic happens
    it(`accepts $mail as a valid email`, () => 
      expect(isValid(mail)).toBe(true);
    )
  );
);

然后错误会像这样显示。

注意这些有多好!

 FAIL  path/to/your.test.js
  ● valid emails checks › accepts abc@y.com as a valid email

    expect(received).toBe(expected)

    Expected: "abc@y.com"
    Received: "xyz@y.com"

      19 |    // here is where the magic happens
      20 |    it(`accepts $mail as a valid email`, () => 
    > 21 |      expect(isValid(mail)).toBe(true);
                                       ^
      22 |    )

【讨论】:

我记得在 Ruby 中也有类似的东西,很高兴发现 Jest 也支持它。 我发现这个结构非常强大,奇怪的是这个答案被忽视了:) expected和received是怎么变成email的?预期不应该是“真实的”吗?【参考方案6】:

我只需要自己处理这个问题,我想我可能会对其进行公关:但这可以适用于您想要的任何东西。基本上,您创建一个自定义方法,允许 curried 函数将自定义消息作为第三个参数。

请务必记住,expect 将设置您的第一个参数(进入 expect(akaThisThing) 的参数作为您自定义函数的第一个参数。

对于一个通用的 Jest 消息扩展器,它可以适合您已经能够使用的任何 Jest 匹配,然后添加一点点华丽:

expect.extend(
  toEqualMessage(received, expected, custom) 
    let pass = true;
    let message = '';
    try 
      // use the method from Jest that you want to extend
      // in a try block
      expect(received).toEqual(expected);
     catch (e) 
      pass = false;
      message = `$e\nCustom Message: $custom`;
    
    return 
      pass,
      message: () => message,
      expected,
      received
    ;
  
);

declare global 
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace jest 
    // eslint-disable-next-line @typescript-eslint/naming-convention
    interface Matchers<R> 
      toEqualMessage(a: unknown, b: string): R;
    
  

会显示如下:

    Error: expect(received).toEqual(expected) // deep equality

    Expected: 26
    Received: 13
    Custom Message: Sad Message Indicating failure :(

具体查看 expect(actualObject).toBe() 以防万一有助于您的用例:

import diff from 'jest-diff'

expect.extend(
toBeMessage (received, expected, msg) 
  const pass = expected === received
  const message = pass
? () => `$this.utils.matcherHint('.not.toBe')\n\n` +
        `Expected value to not be (using ===):\n` +
        `  $this.utils.printExpected(expected)\n` +
        `Received:\n` +
        `  $this.utils.printReceived(received)`
      : () => 
        const diffString = diff(expected, received, 
          expand: this.expand
        )
        return `$this.utils.matcherHint('.toBe')\n\n` +
        `Expected value to be (using ===):\n` +
        `  $this.utils.printExpected(expected)\n` +
        `Received:\n` +
        `  $this.utils.printReceived(received)` +
        `$(diffString ? `\n\nDifference:\n\n$diffString` : '')\n` +
        `$(msg ? `Custom:\n  $msg` : '')`
      

    return  actual: received, message, pass 
  
)

// usage:
expect(myThing).toBeMessage(expectedArray, ' was not actually the expected array :(')

【讨论】:

干得好;我为我的 Create-React-App 创建的应用程序将它添加到我的 setupTests.js 中,它解决了我所有的麻烦......【参考方案7】:

另一种添加自定义错误消息的方法是使用fail() 方法:

it('valid emails checks', (done) => 
  ['abc@y.com', 'a@b.nz'/*, ...*/].map(mail => 
    if (!isValid(mail)) 
      done.fail(`Email '$mail' should be valid`)
     else 
      done()
    
  )
)

【讨论】:

【参考方案8】:

我最终只是用逻辑测试条件,然后将fail() 与字符串模板一起使用。

it('key should not be found in object', () => 
    for (const key in object) 
      if (Object.prototype.hasOwnProperty.call(object, key)) 
        const element = object[key];
        if (element["someKeyName"] === false) 
          if (someCheckerSet.includes(key) === false) 
            fail(`$key was not found in someCheckerSet.`)
          
        

【讨论】:

【参考方案9】:

扩展@Zargold's answer:

如需更多选项,如下方comment,请参阅MatcherHintOptions doc

// custom matcher - omit expected
expect.extend(
  toBeAccessible(received) 
    if (pass) return  pass ;
    return 
      pass,
      message: () =>
        `$this.utils.matcherHint('toBeAccessible', 'received', '', 
          comment: 'visible to screen readers',
        )\n
Expected: $this.utils.printExpected(true)
Received: $this.utils.printReceived(false)`,
    ;
  

// custom matcher - include expected
expect.extend(
  toBeAccessible(received) 
    if (pass) return  pass ;
    return 
      pass,
      message: () =>
        `$this.utils.matcherHint('toBeAccessible', 'received', 'expected',  // <--
          comment: 'visible to screen readers',
        )\n
Expected: $this.utils.printExpected(true)
Received: $this.utils.printReceived(false)`,
    ;
  

【讨论】:

【参考方案10】:

你可以使用这个:(你可以在测试中定义它)

      expect.extend(
ToBeMatch(expect, toBe, Msg)   //Msg is the message you pass as parameter
    const pass = expect === toBe;
    if(pass)//pass = true its ok
        return 
            pass: pass,
            message: () => 'No ERRORS ',
          ;
    else//not pass
        return 
            pass: pass,
            message: () => 'Error in Field   '+Msg + '  expect  ' +  '  ('+expect+') ' + 'recived '+'('+toBe+')',
          ;
    
,  );

并像这样使用它

     let z = 'TheMassageYouWantWhenErrror';
    expect(first.name).ToBeMatch(second.name,z);

【讨论】:

【参考方案11】:

您可以重写expect 断言以使用toThrow()not.toThrow()。然后使用您的自定义文本引发错误。 jest 将在输出中包含自定义文本。

// Closure which returns function which may throw
function isValid (email) 
  return () => 
     // replace with a real test!
     if (email !== 'some@example.com') 
       throw new Error(`Email $email not valid`)
     
  


expect(isValid(email)).not.toThrow()

【讨论】:

【参考方案12】:

我通常使用类似的东西

it('all numbers should be in the 0-60 or 180-360 range', async () => 
    const numbers = [0, 30, 180, 120];
    for (const number of numbers) 
        if ((number >= 0 && number <= 60) || (number >= 180 && number <= 360)) 
            console.log('All good');
         else 
            expect(number).toBe('number between 0-60 or 180-360');
        
    
);

产生:

【讨论】:

以上是关于如何将自定义消息添加到 Jest 期望?的主要内容,如果未能解决你的问题,请参考以下文章

使用stanza.io将自定义属性添加到不在服务器上的存档表中存储消息的消息

将自定义消息头添加到从 Windows Mobile 6 客户端使用的 WCF 服务

如何将自定义消息处理程序与 Strophe 连接

如何将消息添加到数据表分页行

我们可以通过 mqtt 桥将自定义消息属性发布设置为 pubsub

如何使用 AJV 创建自定义错误消息?