-
-
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
feat(vitest): add return type and promisable mockFactory #6139
feat(vitest): add return type and promisable mockFactory #6139
Conversation
I removed generic in importOriginal because of a type error, which could be a breaking change. I guess it's a typescript problem, but let's look at it more. |
f7f2bdd
to
c30f0fc
Compare
packages/vitest/src/types/mocker.ts
Outdated
importOriginal: <T extends M>() => Promise<T> | ||
) => any | ||
importOriginal: <T extends M = M>() => Promise<T> | ||
) => Promisable<Record<keyof M, any> | null> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My apologies if my initial issue wasn't clear, but what I meant with a "compatible" mock type was not only regarding the keys in the export map, but their values as well.
Ensuring compatible top-level keys would already be a welcome improvement of course, but this doesn't address the correctness of the exports themselves. I'd still be able to provide a completely false (type-wise) mock from the factory, and TypeScript wouldn't tell me anything about it. I would still need to manually add the Awaited<ReturnType<typeof importOriginal>>
return type to each of my factories to ensure my mocks are correct.
I reckon being this strict would probably break some people's current types in tests, because they actually have no idea that their mocks are incomplete or plain wrong, but that's the point. I'd understand that some people would want wildly different mocks than their runtime version, even if it breaks their types and the initial module's signature, but in that case they can still escape type validation with a manual any
, while the default could be strict adherence to the original module's signature. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a change to keep it as compatible as possible. For typescript users, I thought maybe it could be a breaking change.
I think there are advantages to both from different perspectives. If necessary, I will revise it according to the opinions of other users or maintainers.
I think this is a good change. Another related issue is that Vitest may not provide correct mock implementations for functions out of the box. So while you technically don't have to provide any factory function, the default Vitest behavior will result in a broken module. Example: // api.js
export const foo = {
bar: async () => 123
} Here, vi.doMock(import('./api.js'))
const { api } = await import('./api.js')
api.bar() // returns nothing, and is NOT an async function. Vitest converts all functions to Since the mock function is synchronous, assertions like this will fail: await expect(foo.bar()).resolves.toBeUndefined() |
So, do you think users should be required to return every export even if it's not used?
I'd say it is an intentionally broken module, this is how automocking works (and it's even documented!)
it is not possible to know if the function returns a promise without calling it which defeats the purpose of automocking. (You can look at the function type for AsyncFunction, but regular functions can also return Promises) |
Yes and no. Yes, because I like being explicit. I can do I'm leaning toward No here, on the assumption that Vitest also provides the correct mock implementations for my exports (see the async function issue above). If it doesn't, I'd actually expect my factory function to type-error, letting me know that certain exports need my attention.
It's a nice gotcha!
I presumed that was the reason. Thanks for clarifying! |
This is not true, Vitest doesn't automock anything returned from the factory. That would require importing the original module. Vitest returns a proxy that throws an error if you try to access a property that you didn't return from the factory. |
Sorry if I phrased it wrong. Here's what I meant: calling |
Looks like you have type errors now. You need to add
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the documentation should reflect the change. Both in type and description
I've drafted the document. |
✅ Deploy Preview for vitest-dev ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
✅ Deploy Preview for vitest-dev ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Description
Adjust MockFactoryWithHelper type and add PromiseMockFactoryWithHelper.
screenshot
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
pnpm-lock.yaml
unless you introduce a new test example.Tests
pnpm test:ci
.Documentation
pnpm run docs
command.Changesets
feat:
,fix:
,perf:
,docs:
, orchore:
.Resolve: #6089