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

图片来自测试案例:

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

开玩笑的有可能吗?

在 Chai 中可以使用第二个参数,比如 expect(value, 'custom fail message').to.be...,在 Jasmine 中似乎使用了 .because子句。但是在玩笑中找不到解决的办法。

55102 次浏览

我认为提供这样的消息是不可能的,但是您可以定义自己的 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

只是必须自己解决这个问题,我想我可能会做一个公关: 但这可以与任何你想要的工作。基本上,您可以创建一个自定义方法,该方法允许 curried 函数将自定义消息作为第三个参数。

一定要记住,Expect 将设置您的第一个参数(作为定制函数的第一个参数进入 expect(akaThisThing)的那个参数)。

对于一个通用的 Jest Message 扩展程序,它可以适合任何 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 :(

为了获得对用例有帮助的预期内部的具体外观(realalObject) . 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 :(')

你可以使用 try-catch:

try {
expect(methodThatReturnsBoolean(inputValue)).toBeTruthy();
}
catch (e) {
throw new Error(`Something went wrong with value ${JSON.stringify(inputValue)}`, e);
}

您可以尝试这个扩展 jest: https://github.com/mattphillips/jest-expect-message的 lib

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

您可以使用: (您可以在测试中定义它)

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

虽然这不是一个通用的解决方案,但是对于需要自定义异常消息来区分循环中的项的常见情况,您可以使用 Jest 的 测试,每个人

例如,示例代码:

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

可以使用 toThrow()not.toThrow()重写 expect断言。然后抛出自定义文本的错误。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()


添加自定义错误消息的另一种方法是使用 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()
}
})
})

我最后只是用逻辑测试条件,然后用字符串模板使用 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.`)
}
}

@ Zargold 的回答上扩展:

有关下面的 comment之类的更多选项,请参见 MatcherHintOptions 文档

// 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)}`,
};
}

enter image description here

// 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)}`,
};
}

enter image description here

2021年的答案

我把我的 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 |    })

我通常会用

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

产生: enter image description here

我没有使用该值,而是传入一个带有描述性标签的元组。例如,在断言表单验证状态时,我迭代希望被标记为无效的标签,如下所示:

errorFields.forEach((label) => {
const field = getByLabelText(label);


expect(field.getAttribute('aria-invalid')).toStrictEqual('true');
});

它提供以下错误消息:

expect(received).toStrictEqual(expected) // deep equality


- Expected  - 1
+ Received  + 1


Array [
"Day",
-   "false",
+   "true",
]