-
Notifications
You must be signed in to change notification settings - Fork 9.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
tests: dynamically import all modules when using mocks #14468
Conversation
// All mocks must come first, then we can load the "original" version of asset-saver (which will | ||
// contain references to all the correct mocked modules, and have the same LighthouseError class | ||
// that the test file uses). | ||
const assetSaver = await import('../lib/asset-saver.js?__quibbleoriginal'); |
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.
Can this just dynamic import the module normally now? (no query param)
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.
Tests still fail. I tried moving this dynamic before asset saver is mocked but one of the tests still fails. I think we may need to keep it for the time being.
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.
In general my hesitation here was rooted in an ideal that one day, we would be able to go back to the idealized jest mock setup where mocks are 1) within the same test file and 2) get hoisted up above the es module imports .... somehow. It's unclear if (2) will ever materialize, but if it did, we would be able to just paste the mock code into the test file, and rely on the magic hoisting to work it out–and get back to test code similar to how jest mocking commonjs modules looked.
But there's no reason to think that'll ever be possible, at least not without some heavy jest-like machinery that pre-processes test files. So this proposal has the benefit of being in a reasonable end-state, unlike the other one (separate mock files aren't perfect, but the hope was one day the transition would be trivial).
I think the approach here can be:
If a test file mocks a module, it should first statically import test modules; then configure the mocks; then dynamically import all non-test modules.
We shouldn't attempt to dynamically import just the set of modules necessary, because when a module is mocked that happens to be a transitive dependency of a module statically imported that you thought wasn't related to the module being mocked, the errors that occur are incredibly hard to debug. So let's just dynamic import all the (non-test) things.
Can we have a singular comment before each block of dynamic modules that refers the reader to a longer explanation in a markdown file?
politely, I wonder if this package I published might be useful here https://www.npmjs.com/package/esmock |
@iambumblehead The fundamental problem this PR is trying to solve is that mocks cannot be applied to anything in the static imports, because static imports will always be resolved before the mock is applied: https://jestjs.io/docs/ecmascript-modules#module-mocking-in-esm Ideally our mocks would be hoisted before the static imports like jest does with cjs tests, but it doesn't look like that's possible with |
@adamraine |
This issue contains an example of the problem we face: |
import assert from 'assert';
import 'quibble';
import {LighthouseError} from '../lib/lh-error.js';
assert( (await import('../lib/lh-error.js')).LighthouseError === LighthouseError ); |
Unfortunately, our tests were designed with jest, jest mocking, and cjs in mind. The tests expect mocks to be applied globally in isolated test enironments. Modules we mock repeatedly have there own separate mocking functions (example). We could redesign our mocking patterns so we can use esmock, but it's not a drop in replacement for testdouble. Some tests would be easy to convert to esmock: Others are more complicated because more than 1 imported module depend on mocked modules: That being said, our tests may benefit from using the locally contained tree provided by esmock, and the ts chaining support would let use use mocks in our flow report tests. It's worth considering for the future IMO, but would be non-trivial to adopt. |
de496fc
to
6c8312d
Compare
Co-authored-by: Connor Clark <cjamcl@google.com>
Alternative to #14216. Instead of moving all our mocks to a separate file, we can err on the side of dynamic imports for everything that might depend on our mocked modules.
Basing this off of #14202 to see if this fixes the unit test issues there.