In this question that I asked here:
I'm asking about the nature of module mutation.
However as it it turns out, ES6 modules can't actually be mutated - all of their properties are treated as constants. (See this answer)
But somehow - when Jest tests modules - they can be mutated, and that's how Jest allows for mocking.
How is this happening?
I imagine that it's a babel plugin that that's running - transpiling the module to CommonJS modules? Is there any documentation about this?
Is there a way to view the transpiled code?
Interesting. You're right, even something as simple as this:
...technically shouldn't be allowed since
jest.spyOn
replaces the method on the object with a spy andlib.someFunc
should be a binding tosomeFunc
in the ES6 module.They can only be mutated because
Jest
isn't actually using ES6 modules.(I guess for completeness it might be possible to run
Jest
using actual ES6 modules by usingNode
's experimental support for ES6 Modules but I haven't tried)."
babel-jest
is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset thetransform
configuration option".So by default
Jest
will usebabel-jest
which transpiles the source code usingbabel
(and does a few other things like hoisting calls tojest.mock
).Note that
Jest
can be also be configured usingtransform
which maps "regular expressions to paths to transformers".Yes. Transformations are done in
jest-runtime
here and the output is saved to a cache here.The easiest way to look at the transpiled code is to view the cache.
You can do that by running
Jest
with the--showConfig
option which will output theconfig
used when runningJest
. The cache location can be found by looking at the value of "cacheDirectory".Then run
Jest
with the--clearCache
option to clear out the cache.Finally, run
Jest
normally and the cache directory will contain the transpiled code for your project.Example
The latest
Jest
(v24) will transpile this code:...to this:
The
import * as lib from 'lib';
line gets handled by_interopRequireWildcard
which usesrequire
under the hood.Every call to
require
"will get exactly the same object returned, if it would resolve to the same file" socode.js
andcode.test.js
are getting the same object fromrequire('./lib')
.someFunc
is exported asexports.someFunc
which allows it to be reassigned.So yes, you're exactly right. Spying (or mocking) like this only works because the ES6 modules are getting transpiled by
babel
intoNode
modules in a way that allows them to be mutated.