Reputation: 7767
Image following test case:
it('valid emails checks', () => {
['[email protected]', '[email protected]'/*, ...*/].map(mail => {
expect(isValid(mail)).toBe(true);
});
});
I would like to add auto-generated message for each email like Email '[email protected]' should be valid
so that it's easy to find failing test cases.
Something like:
// .map(email =>
expect(isValid(email), `Email ${email} should be valid`).toBe(true);
Is it possible in Jest ?
In Chai it was possible to do with second parameter like expect(value, 'custom fail message').to.be...
and in Jasmine seems like it's done with .because
clause. But cannot find solution in Jest.
Upvotes: 191
Views: 91984
Reputation: 1236
Jest supports this behavior out of the box with it.each
or test.each
docs. As per API docs, it seems to be available since v23.
Your code would look as follows:
it.each([
'[email protected]',
'[email protected]',
/* ... */
])('Email %s should be valid', (email) => {
expect(isValid(email)).toBe(true);
});
This will produce the following output:
✓ Email [email protected] should be valid
✓ Email [email protected] should be valid
Note that this approach applies to your specific case. If you'd need an assertion with custom message in the middle of various assertions (more complex test setup/case), then you should go with some of the other solutions.
Upvotes: 0
Reputation: 96
Following @Monarch Wadia answer I created this function which takes a data object and a callback containing tests and then iterates this callback for each object item.
function testDataSet(title, obj, func) {
describe(title, () => {
for (const k in obj) {
test(`With data set: ${k}`, () => {
func(obj[k]);
});
}
});
}
Usage
testDataSet('My test case', { a: 'a1', b: 'b1' }, (data) => {
expect(data).toEqual('a1');
});
Results
FAIL file.test.js
My test case
√ With data set: a (8 ms)
× With data set: b (9 ms)
● My test case › With data set: b
expect(received).toEqual(expected) // deep equality
Expected: "a1"
Received: "b1"
102 |
103 | testDataSet('My test case', { a: 'a1', b: 'b1' }, (data) => {
> 104 | expect(data).toEqual('a1');
| ^
105 | });
106 |
Upvotes: 1
Reputation: 4976
I did this in some code I was writing by putting my it
blocks inside forEach
.
By doing this, I was able to achieve a very good approximation of what you're describing.
Pros:
Here's what your code would look like with my method:
// you can't nest "it" blocks within each other,
// so this needs to be inside a describe block.
describe('valid emails checks', () => {
['[email protected]', '[email protected]'/*, ...*/].forEach(mail => {
// here is where the magic happens
it(`accepts ${mail} as a valid email`, () => {
expect(isValid(mail)).toBe(true);
})
});
});
Errors then show up like this.
Notice how nice these are!
FAIL path/to/your.test.js
● valid emails checks › accepts [email protected] as a valid email
expect(received).toBe(expected)
Expected: "[email protected]"
Received: "[email protected]"
19 | // here is where the magic happens
20 | it(`accepts ${mail} as a valid email`, () => {
> 21 | expect(isValid(mail)).toBe(true);
^
22 | })
Upvotes: 48
Reputation: 611
Instead of using the value, I pass in a tuple with a descriptive label. For example, when asserting form validation state, I iterate over the labels I want to be marked as invalid like so:
errorFields.forEach((label) => {
const field = getByLabelText(label);
expect(field.getAttribute('aria-invalid')).toStrictEqual('true');
});
Which gives the following error message:
expect(received).toStrictEqual(expected) // deep equality
- Expected - 1
+ Received + 1
Array [
"Day",
- "false",
+ "true",
]
Upvotes: 1
Reputation: 58362
Although it's not a general solution, for the common case of wanting a custom exception message to distinguish items in a loop, you can instead use Jest's test.each.
For example, your sample code:
it('valid emails checks', () => {
['[email protected]', '[email protected]'/*, ...*/].map(mail => {
expect(isValid(mail)).toBe(true);
});
});
Could instead become
test.each(['[email protected]', '[email protected]'/*, ...*/])(
'checks that email %s is valid',
mail => {
expect(isValid(mail)).toBe(true);
}
);
Upvotes: 46
Reputation: 32982
I don't think it's possible to provide a message like that. But you could define your own matcher.
For example you could create a toBeValid(validator)
matcher:
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
};
}
}
});
And then you use it like this:
expect(mail).toBeValid(isValid);
Note: toBeValid
returns a message for both cases (success and failure), because it allows you to use .not
. The test will fail with the corresponding message depending on whether you want it to pass the validation.
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
Upvotes: 61
Reputation: 99
I'm usually using something like
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');
}
}
});
Upvotes: 0
Reputation: 2092
Just had to deal with this myself I think I'll make a PR to it possibly: But this could work with whatever you'd like. Basically, you make a custom method that allows the curried function to have a custom message as a third parameter.
It's important to remember that expect will set your first parameter (the one that goes into expect(akaThisThing)
as the first parameter of your custom function.
For a generic Jest Message extender which can fit whatever Jest matching you'd already be able to use and then add a little bit of flourish:
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;
}
}
}
Will show up like:
Error: expect(received).toEqual(expected) // deep equality
Expected: 26
Received: 13
Custom Message: Sad Message Indicating failure :(
For specific look inside the expect(actualObject).toBe() in case that helps your use case:
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 :(')
Upvotes: 8
Reputation: 4555
To expand on @Zargold's answer:
For more options like the
comment
below, see 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)}`,
};
}
Upvotes: 1
Reputation: 646
I end up just testing the condition with logic and then using the fail()
with a string template.
i.e.
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.`)
}
}
Upvotes: 2
Reputation: 1082
You try this lib that extends 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);
});
Upvotes: 87
Reputation: 5776
Another way to add a custom error message is by using the fail()
method:
it('valid emails checks', (done) => {
['[email protected]', '[email protected]'/*, ...*/].map(mail => {
if (!isValid(mail)) {
done.fail(`Email '${mail}' should be valid`)
} else {
done()
}
})
})
Upvotes: 9
Reputation: 13381
You can rewrite the expect
assertion to use toThrow()
or not.toThrow()
. Then throw an Error with your custom text. jest
will include the custom text in the output.
// Closure which returns function which may throw
function isValid (email) {
return () => {
// replace with a real test!
if (email !== '[email protected]') {
throw new Error(`Email ${email} not valid`)
}
}
}
expect(isValid(email)).not.toThrow()
Upvotes: 0
Reputation: 571
You can use try-catch:
try {
expect(methodThatReturnsBoolean(inputValue)).toBeTruthy();
}
catch (e) {
throw new Error(`Something went wrong with value ${JSON.stringify(inputValue)}`, e);
}
Upvotes: 19
Reputation: 11
you can use this: (you can define it inside the test)
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+')',
};
}
}, });
and use it like this
let z = 'TheMassageYouWantWhenErrror';
expect(first.name).ToBeMatch(second.name,z);
Upvotes: 1