-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Provide test context to test.each()
#4642
Comments
This is kind of expected. No context is passed down there at the moment. What is your expectation? How do you expect to access context in test.each? |
Yes, I expect each case to behave like a regular test. |
How would you access it? Give me code example. In a regular test function the argument is the context, but in test.each the argument is whatever it is you passed down to each function. |
It's on the stackblitz : just add a second argument. const cases = [
['config0', 'expected0'],
['config1', 'expected1'],
];
myTest.each(cases)(
'fixtures should be available as part of the second argument',
([config, expected], { myFixture }) => {
expect(myFixture.doSomething(config)).toEqual(expected);
}); |
This is not how it works. You control the second argument - with your example |
1.0.0 is still in beta right ? The API can still be changed. We agree this is a design flaw, right ? |
What is a design flaw? Can you just give an example of what you expect without breaking changes? We definitely will not make it so the first argument in |
The design flaw is that one cannot use The alternate solution would then be to expose a static function that returns the fixtures of the current test. edit: or add a new function other than « each », specific to vitest. |
This is not a flaw, this is literally a design of this method - it's intentionally different. If you don't like it, you can always use Your proposed solution does not benefit existing users. But what we can do is add a context as the last argument instead of the second, so it doesn't break existing code and makes sense with the proposal. |
If you add the context as the last parameter it’s a breaking change because one could use a rest argument or simply iterate through the array. Yes indeed, I could simply use a loop. |
By that logic any usage of any API is a breaking change, so any new feature requires a major which to me is absurd.
Well, it's in the name, is it? It needs to pass down data somehow. It was also designed before fixtures were introduced. |
If you change the value of the argument received, it is a breaking change because it will break existing code. it was before the fixtures, ok. |
Now that I'm rested I see your point. You would only have the extra value in the array if you opt-in for fixtures, so it would not be a breaking change indeed. |
I guess it could work if you add an optional
But yeah, even if the loop would do the trick for the moment, as a user, it seems quite off that |
I do like this solution. I think we can just pass down But I would make a small change - move fixtures to be the latest argument instead of the first one. |
If the fixtures are the last parameter, then you cannot use a rest parameter for the values of the case. |
#4963 was closed as a dupe of this one, as it also comes from the fact that the test context is not passed down into My suggestion there was that when Maybe the above can be done with that second parameter, like: test.concurrent.each([
// ...
])('someName %o', { args, expect } => {
// ...
}, { withContext: true }); Additionally, can these cases be turned into runtime errors today? E.g. if (Also, if this is the place to collect "I need the context in |
I don't understand this request. Why can't you use them both? You don't have to use context/fixtures to run tests. |
My thinking is that a test without |
You can (and should) use global We already throw an error for |
Thanks, I didn't realize that it was only snapshots and not the entire thing. |
test.each()
test.each()
Slightly off topic but you poked my curiosity. Is there any downside at using a local expect instead of the global one ? |
I think currently there are few more For example,
(also I made an example of such usage https://stackblitz.com/edit/vitest-dev-vitest-gyudct?file=test%2Frepro.test.ts) |
(EDIT: Ah, sorry, I just realized this doesn't make inner tests to run in parallel...) Though it's ugly (both API and also test logs), I think wrapping with https://stackblitz.com/edit/vitest-dev-vitest-mnmqgv?file=test%2Frepro.test.ts |
I guess you are right. Over time the usage of this increased. Generally speaking, assertions are just functions that throw an error - you don’t really need to create a local expect (which is a bit expensive to do for every test) just to check if one value is equal to another. |
Thanks for all the suggestions! I made some prototype on my PR #4989 for potential |
@hi-ogawa thanks for pulling this out. I'm not sure what I like more API-wise, but Jest compatibility will continue to be important for the next 3-5 years I would say, so I would rule out my own proposition of having it as the first argument. // No arrow function
test.each(cases)('many cases', function (...caseArgs) => {
const { myFixture } = this.getFixtures(); // Or maybe vi.getFixtures() ? But then it doesn't work in concurrent mode :/
});
// No array case
test.each(cases)('fixtures are available though a non-enumerable symbol', function (caseObj) => {
const { myFixture } = vi.getFixtures(caseObj);
}); |
If the goal is specifically jest compat, then from reading the API examples in the PR, only
|
Hey, thanks for the quick feedback. Yeah, from the candidate so far, it's very tricky to think about API for jest compatibility while test context/fixture is vitest-only features. Also template string usage I just wanted to share what I saw while prototyping it, but I'll still keep looking as we might be still missing some ideas. @JesusTheHun Thanks for the idea. About "No arrow function" API approach |
@hi-ogawa so you parse the file to detect fixture usage 🧐 |
Not the file, only the function body. This is how it works in Playwright as far as I know. A proxy would trigger too late (when the test starts), we need to prepare all fixtures before the test has started. |
Can you elaborate on why it must be done before the test starts ? @hi-ogawa we can require strict rules on how to use it, like |
I'm not sure but technically proxy approach might be possible to employ in some way, but API would be probably affected by this. For example, |
I came back to this feature and it looks like I missed the one more suggestion by @jakebailey #4642 (comment) about where to put This one actually supports template literal usages and typescript inference still works alright. I added it to myTest.concurrent.each7`
a | b
${'hello'} | ${2}
${'hi'} | ${3}
`(
'myTest.each7-context-template-true $a $b',
async (arg, { expect, myFixture }) => {
// arg = { a: "hello", b: 2 }
// arg = { a: "hi", b: 3 }
expect({ arg, myFixture }).toMatchSnapshot()
},
{ context: true },
) While adding more tests, I found a tricky case where https://stackblitz.com/edit/vitest-dev-vitest-vnzqwz?file=test%2Frepro.test.ts import { expect, test } from 'vitest';
test.each([
[0, 1],
[2, 3, 4],
])('each %s %s', (...args) => {
// args = [0, 1]
// args = [2, 3, 4]
expect(args).toMatchSnapshot();
}); |
We are deprecating the option in the last argument of myTest.concurrent.each7`
a | b
${'hello'} | ${2}
${'hi'} | ${3}
`(
'myTest.each7-context-template-true $a $b',
{ context: true },
async (arg, { expect, myFixture }) => {
// arg = { a: "hello", b: 2 }
// arg = { a: "hi", b: 3 }
expect({ arg, myFixture }).toMatchSnapshot()
},
) |
We discussed this issue within the team, and we think it might be nicer to introduce a separate function for this, so what about having a separate API for this feature? Like test.concurrent.for([
[1, 2, 3]
])('example', (first, second, third, context) => {
context.task.name // example
}) |
If we can introduce a new API (and thus no need to worry about Jest compat or breaking change), then maybe we should consider supporting this exotic (but currently valid) usages: // it's still not possible to type-inference nor extract fixture at the last argument
myTest.for([
[0, 1],
[2, 3, 4],
])('each %s %s', (...args) => {
// args = [0, 1, { expect, someFixture }]
// args = [2, 3, 4, { expect, someFixture }]
}); There are already some suggestions to support this: // regardless of each case is array or not, put each case as fixed first argument
myTest.for([
[0, 1],
[2, 3, 4],
])('each %s %s', (arg, { expect, someFixture }) => {
// arg = [0, 1]
// arg = [2, 3, 4]
});
myTest.for([
{ x: 0 }
{ x: 1 },
])('each %s %s', (arg, { expect, someFixture }) => {
// arg = { x: 0 }
// arg = { x: 1 }
});
// still easy to destruct array case
myTest.for([
[0, 1],
[2, 3],
])('each %s %s', ([a, b], { expect, someFixture }) => {
// [a, b] = [0, 1]
// [a, b] = [2, 3]
}); // provide each case as a part of context in the first argument.
// not sure if it should be named `arg`, `args`, or something else...
myTest.for([
[0, 1],
[2, 3, 4],
])('each %s %s', ({ arg, expect, someFixture }) => {
// arg = [0, 1]
// arg = [2, 3, 4]
});
myTest.for([
{ x: 0 }
{ x: 1 },
])('each %s %s', ({ arg, expect, someFixture }) => {
// arg = { x: 0 }
// arg = { x: 1 }
}); Personally, the first suggestion #issuecomment-1837525748 looks okay to me. In terms of implementation, I think both typescript overload and fixture extraction becomes simpler with either of the approach. |
I found that the context is provided if a name is not supplied eg: myTest.each([
[0, 1],
[2, 3, 4],
])((arg, { someFixture }) => {
// arg = [0, 1]
// arg = [2, 3, 4]
}) Is not ideal as the results list the tests as I'm not familiar with Jest so this might be expected. |
When I try this it appears that the tests never actually run. So while there is no error about the wrapper not existing it will pass regardless of what you put in the body of the test.
shows all of the tests passing. If you try to console.log something in the test it never gets called. |
I was able to resolve the above (test.each not running at all) by upgrading:
Update: More precisely, upgrading gave me a new error message:
and fixing that mistake by moving |
I like this second approach. |
Describe the bug
In the same vein as #4585, fixtures are unavailable when using
test.each()
.Reproduction
https://stackblitz.com/edit/vitest-dev-vitest-el13eq?file=test%2Fbasic.test.ts
System Info
Used Package Manager
npm
Validations
The text was updated successfully, but these errors were encountered: