-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance test.each message format #6413
Comments
I like option A as Option B is too verbose IMO and you'd probably be better off just using A middle ground could be to add argument swapping support with What do you think @SimenB ? |
I'm not sure of a nice way to solve this. We could easily prefix/suffix a test with the index. This again could be considered a break in change as it would break any tests that are using snapshots. I generally don't consider parameterised tests as being a part of an array even though they are defined that way. I tend to think of them as independent tests and the interpolated values are enough to identify the failing tests. That said I think if there was enough interest in this then it might be a worthwhile addition, its just not a problem I've ran into yet 😄 EDIT: I’m open to adding a placeholder for the index see #6414 |
Another idea I’ve had around this could be to ditch printf all together and just interpolate test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])(
'$2 created using .add($0, $1)',
(a, b, expected) => {
expect(a + b).toBe(expected);
},
) |
@mattphillips Is it possible to add something like test.each([
// Big dataset.
])('%index- some test', () => {
}) |
Alright 2., i.e. row index is almost done - great. 1. Honestly, any of these options is fine by me. The only workaround I could think of right now is to swap values in the data table rows - cumbersome. Using And now the hardest part - which option is the best and is going to be implemented? :) |
A different question on I have a question for the same on Stackoverflow. |
@malhotraashna you can use variables in |
@mattphillips I have several The structure is somewhat like this:
When you say
|
Sorry for chiming in with something slightly unrelated, but I felt like it fits under this Issue as another example use case rather than deserving another issue asking for about the same thing: I wish test.each supported accessing a key from arrays of objects in the test name, without having to use tagged template literals. With Jasmine long ago I used https://github.com/MortalFlesh/jasmine-data-provider and it's method of arrays of objects and keyed object with the key being the test name partial I found worked far more intuitively than array positionals or funky Cucumber tables with value interpolations scattered everywhere.. For example, this currently works, but I can't help but feel keeping the whitespace and aligning pipes is annoying and not as friendly to common tools or IDE refactorings as JSON objects would be:
results in:
This seems like it should work, but I can only get to print the ENTIRE row's object:
results in:
From what I gather, this is really due to using Node's util.format under the hood. Earlier in this thread
Could you go into more detail on the negative aspects of how %o changes? Like, does its default case format the entire object differently than Node's util.format does? I only ask because if the breaking change is not so severe, it may be warranted for the extra benefits it brings. Above the array positional was mentioned, and here I believe sprintf-js would allow grabbing specific keys from objects with For what its worth, trying things like that currently just pass it through as-is:
But would in theory result in exactly what I am looking for. Hope this was useful input and my apologies if this is just adding noise to this thread. In the meantime, most of these issues can be worked around with manual loopings
results in
Also please forgive the non-sensical expectation in those example tests, it is only there to illustrate how the test is handed each object in the array the same way the table format is handed them - only the name reference is limiting. |
Super solution from @r4j4h thanks! Just including a slightly simplified/integrated version to match the for (const [invalidPassword, expectedError] of [
['duck', 'short'],
['password', 'weak'],
['pineapple', 'weak'],
]) {
test(`Cannot register with ${expectedError} password: "${invalidPassword}"`, () => {
//...
});
} With TS, use a tuple to avoid type errors: for (const [invalidPassword, expectedError] of [
['duck', 'short'],
['password', 'weak'],
['pineapple', 'weak'],
] as const) { // "as const" creates the array as a typed tuple
test(`Cannot register with ${expectedError} password: "${invalidPassword}"`, () => {
//...
});
} |
@SimenB @mattphillips is manual looping a valid (aka approved) use-case? It also has one more advantage because you can define the table like so: for (const {name, main_identity, another_argument, expected} of [
{
name: 'given an expected thing',
main_identity: 1,
another_argument: 'example',
expected: true
}, {
name: 'given an expected thing with example',
main_identity: 1,
another_argument: 'example',
expected: true
}, {
name: 'given cool unexpected thing',
the_argument: "cool",
another_argument: 'example',
expected: false
}, {
name: 'given cool unexpected thing with example',
the_argument: "cool",
another_argument: 'example',
expected: false
}
]) {
test(`Testing with input ${name}`, () => {
expect(name).toBe(expected);
});
} The only problem with this solution is integration with IDE: WebStorm won't launch the test since there will be no such control (which is good because it would fail to find a test): |
@kirill-konshin as you noticed some integrations with editors, lint rules, etc. might not like this, but Jest itself totally supports this kind of looping officially, as long as it's all synchronous. |
@jeysal so are there any plans to support objects in |
How is this as a workaround?
|
@murphyke your code gave me the idea, what about this: test.each([['foo', {a: 'a', b: 'b'}], ['bar', {a: 'a1', b: 'b1'}], ['baz', {a: 'a1', b: 'b1'}]])(
'test for %s',
(testName, {a, b}) => {
// do something
},
) E.g. use strings directly to include them in test name and object for the rest. |
@kirill-konshin I dig it; that's the best adaptation yet. |
@kirill-konshin gets it! 😁 Most clean way to do it without having to make your own implementations and loops that look less clean. In typescript that's the way I did it and it's the most clean I've seen to do the job interface Test {
args: PageItemsArgs
expected: Expected
}
test.each<[string, Test]>([
['no items', {
args: { items: [], page: 0 },
expected: { totalPages: 0 },
}],
])('%s', (_, test) => { /* Test */}) |
So far, I solved it very similar to @kirill-konshin and @exapsy. Pushing it even further and incorporating the suggestion by @murphyke, you can use some utility functions, if you don't like this mixed array/object syntax for defining test cases. In the following example, I show two examples of how to handle it: Generate a test name from a name property in test data, or generating a test name just from the data, which then is very similar to the The Typesinterface NamedTest {
name: string;
}
interface RawTestData {
input: string;
expected: string;
}
type TestData = NamedTest & RawTestData; Test Data and Method Under Testconst data: TestData[] = [
{
name: "First",
input: "in",
expected: "out",
},
{
name: "Second",
input: "out",
expected: "in",
},
];
const underTest = (val: string): string => {
if (val === "in") {
return "out";
}
return "in";
}; Utility Functions as workaroundThis is similar as already suggested by @murphyke: // Uses the name property for a test name.
const fromNamed = <T extends NamedTest>(data: T[]): [string, T][] => {
return data.map((d) => [d.name, d]);
};
// Uses just the data for generating a name, similar to Jest Table Syntax
const fromData = <T extends RawTestData>(data: T[]): [string, T][] => {
return data.map((d) => [`On input '${d.input}' expecting '${d.expected}'`, d]);
}; Using Utility Methodstest.each<[string, TestData]>(fromNamed(data))("[%#] %s", (name, data) => {
expect(underTest(data.input)).toStrictEqual(data.expected);
});
test.each<[string, TestData]>(fromData(data))("[%#] %s", (name, data) => {
expect(underTest(data.input)).toStrictEqual(data.expected);
}); |
This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days. |
This issue was closed because it has been stalled for 30 days with no activity. Please open a new issue if the issue is still relevant, linking to this one. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Enhance
test.each
message format:%2$s
Motivation
Example
There are a few ways I can think of
A/ smarter message format like in
sprintf-js
packageStill not sure how to specify optional test set (table row) index
B/ message as a function - more customizable, and in my opinion easier to implement
Is this something that could be considered as a helpful feature and implemented in jest?
The text was updated successfully, but these errors were encountered: