From 25e4b2e42776c8c83acdc675f38e4977e6c14542 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 09:14:48 +0000 Subject: [PATCH 1/8] docs: say when `vi.mock()` updates an already loaded package in place `docs/test/mocks.mdx` said that a `vi.mock()` call in a statically imported module always updates a loaded package in place. Bun does that only while one of the project's own modules that imports the package with a static import has not finished running. Otherwise a module that already ran keeps the real package. State that rule in "Mocking a Package", with the modules that do not count and the ways to give such a module the mock. Limit the same claim in "When to Use Preload" and in two bullets of "Differences from Vitest". --- docs/test/mocks.mdx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 61f5b4ec0993..41edaeb311aa 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -465,7 +465,17 @@ jest.mock("dep", () => fake); // the library gets the fake too If the package is already loaded, what imported it keeps the real one, and the next `import()` or `require()` from your own code calls the factory. Modules that an earlier test file loaded are loaded again and get the mock, because in Vitest each file starts with nothing loaded. The real package is never loaded a second time: `vi.importActual()` returns the loaded one, and `vi.doUnmock()` brings it back. -The exception is a `vi.mock()` call in a module that is imported with a static import (see "Hoisting & Preloading" below). By the time that module runs, its importer's other imports are already linked to the real package. Bun updates the loaded package in place, so the test file and the modules it imports get the mock. A module inside `node_modules` that imports the package gets the mock too. So it does when a `--preload` script mocks a package that an earlier preload has loaded. +The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then gets the mock, including one that has already run and one inside `node_modules`. + +For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. + +Some modules don't make Bun update the package. One is the module that calls `vi.mock()` or `vi.doMock()`. Another is a module that has reached a top-level `await`, such as a test file whose imports run one at a time. A CommonJS module doesn't either, because `require()` isn't a static import. + +If no module makes Bun update the package, a module that has already run keeps the real package, as in Vitest. That happens in a test file that doesn't import the package itself, when it imports `./cart` first and `./mocks` second. It also happens, in either order, when `./cart` is a CommonJS module that sets `module.exports` or `exports` and calls `require()` for the package at its top level. Bun runs such a module as soon as it loads it, before the ES modules that the same file imports. + +To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded. + +A `--preload` script that mocks a package that an earlier preload has loaded updates the package in place too. A mocked builtin such as `node:fs` is mocked for every importer with all of them. @@ -576,7 +586,7 @@ preload = ["./my-preload"] #### When to Use Preload -Mocking a module that's already been imported updates the module cache, so anything that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. +`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported, so anything that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. To prevent the original module from being evaluated at all, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. @@ -798,9 +808,9 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module that another one imports, that module is updated in place. +- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that only if one of your modules that imports it hasn't finished running (see [Mocking a Package](#mocking-a-package)). - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. -- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module inside `node_modules` that imports it gets the mock. In Vitest it gets the real package. +- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run gets the mock, and so does a module inside `node_modules` that imports the package. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. - Every module outside `node_modules` that imports the mocked module gets the mock. In Vitest, a module that's first loaded while the factory runs gets the original and keeps it. - If two factories import each other's modules, the one that asks last gets the original. Vitest calls one of the two factories a second time instead. From 32e2350fe64fab583b3965cbd9dd45c1ac493026 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 09:30:05 +0000 Subject: [PATCH 2/8] docs: define a finished module, and state the preload and copied-value limits Say when a module counts as finished for an in-place update of a loaded package: its code ran to the end or reached a top-level await, and modules that import each other finish together. A `vi.mock()` call that runs for a `--preload` script always updates a loaded package, so say that in "Mocking a Package" and in the "Differences from Vitest" bullet. In "When to Use Preload", say what an in-place update does not reach: a value that a module copied out before the mock, and the object that `require()` returned for a CommonJS module. --- docs/test/mocks.mdx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 41edaeb311aa..b0e94933daf2 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -469,13 +469,15 @@ The exception is when one of your own modules imports the package with a static For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. -Some modules don't make Bun update the package. One is the module that calls `vi.mock()` or `vi.doMock()`. Another is a module that has reached a top-level `await`, such as a test file whose imports run one at a time. A CommonJS module doesn't either, because `require()` isn't a static import. +A module has finished running once its code has run to the end or has reached a top-level `await`. A test file whose imports run one at a time has reached one. Modules that import each other finish together, when the last of them does. + +The module that calls `vi.mock()` or `vi.doMock()` doesn't make Bun update the package. Neither does a CommonJS module, because `require()` isn't a static import. If no module makes Bun update the package, a module that has already run keeps the real package, as in Vitest. That happens in a test file that doesn't import the package itself, when it imports `./cart` first and `./mocks` second. It also happens, in either order, when `./cart` is a CommonJS module that sets `module.exports` or `exports` and calls `require()` for the package at its top level. Bun runs such a module as soon as it loads it, before the ES modules that the same file imports. To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded. -A `--preload` script that mocks a package that an earlier preload has loaded updates the package in place too. +A `vi.mock()` call in a `--preload` script, or in a module that the script imports, always updates a loaded package in place. A mocked builtin such as `node:fs` is mocked for every importer with all of them. @@ -586,9 +588,11 @@ preload = ["./my-preload"] #### When to Use Preload -`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported, so anything that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. +`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported, so a module that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. + +An update doesn't reach a value that a module copied out of the original before the mock, as `const { send } = require("pkg")` does. It doesn't reach the object that `require("pkg")` returned either, if `pkg` is a CommonJS module, because the update replaces its `module.exports`. -To prevent the original module from being evaluated at all, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. +To prevent the original module from being evaluated at all, or to mock it for a module that copies a value out of it, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. #### Practical Module Mock Examples @@ -808,7 +812,7 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that only if one of your modules that imports it hasn't finished running (see [Mocking a Package](#mocking-a-package)). +- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, Bun always does it (see [Mocking a Package](#mocking-a-package)). - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. - Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run gets the mock, and so does a module inside `node_modules` that imports the package. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. From 6484797f451c00bf6173cc1f0f549be0f180a96b Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 09:39:02 +0000 Subject: [PATCH 3/8] docs: limit the preload rule to calls that run for the preload, and exclude copied values A `vi.mock()` call updates a loaded package without condition only when it runs while a preload loads, or in a hook that the preload registered. A function of the preload that a test calls later follows the rules for a test file. An in-place update reaches a module through its imports. A value that the module copied out earlier stays the original, so say that where the text says that a module that already ran gets the mock. --- docs/test/mocks.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index b0e94933daf2..e2d92e0a61a2 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -465,7 +465,7 @@ jest.mock("dep", () => fake); // the library gets the fake too If the package is already loaded, what imported it keeps the real one, and the next `import()` or `require()` from your own code calls the factory. Modules that an earlier test file loaded are loaded again and get the mock, because in Vitest each file starts with nothing loaded. The real package is never loaded a second time: `vi.importActual()` returns the loaded one, and `vi.doUnmock()` brings it back. -The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then gets the mock, including one that has already run and one inside `node_modules`. +The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then reads the mock through its imports, including one that has already run and one inside `node_modules`. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. @@ -477,7 +477,7 @@ If no module makes Bun update the package, a module that has already run keeps t To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded. -A `vi.mock()` call in a `--preload` script, or in a module that the script imports, always updates a loaded package in place. +A `vi.mock()` call that runs while a `--preload` script loads updates a loaded package in place, even if every module that imports the package has finished running. So does a call in a hook that the preload registered, such as `beforeAll()`. A call that a test makes later, through a function that the preload defined, follows the rules above. A mocked builtin such as `node:fs` is mocked for every importer with all of them. @@ -812,9 +812,9 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, Bun always does it (see [Mocking a Package](#mocking-a-package)). +- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, that condition doesn't apply (see [Mocking a Package](#mocking-a-package)). - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. -- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run gets the mock, and so does a module inside `node_modules` that imports the package. In Vitest both keep the real package. +- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run reads the mock through its imports from then on, and so does a module inside `node_modules` that imports the package. A value that either one copied out before the update stays the original. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. - Every module outside `node_modules` that imports the mocked module gets the mock. In Vitest, a module that's first loaded while the factory runs gets the original and keeps it. - If two factories import each other's modules, the one that asks last gets the original. Vitest calls one of the two factories a second time instead. From cd128d80b6c127fd6dd60a89eaa34e3eb938d958 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 09:49:32 +0000 Subject: [PATCH 4/8] docs: state the limits of an in-place update of a package, and name the preload hook An update in place changes the exports that the package has. It does not add an export that only the mock has, and it reads a getter once. Say so next to the rule, as the page already does for a module of the project. Name `beforeAll()` as the preload hook whose `vi.mock()` call updates a loaded package. A `beforeEach()` hook that runs for concurrent tests does not get that treatment. --- docs/test/mocks.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index e2d92e0a61a2..078945fdca0f 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -465,7 +465,9 @@ jest.mock("dep", () => fake); // the library gets the fake too If the package is already loaded, what imported it keeps the real one, and the next `import()` or `require()` from your own code calls the factory. Modules that an earlier test file loaded are loaded again and get the mock, because in Vitest each file starts with nothing loaded. The real package is never loaded a second time: `vi.importActual()` returns the loaded one, and `vi.doUnmock()` brings it back. -The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then reads the mock through its imports, including one that has already run and one inside `node_modules`. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). +The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then reads the mock through its imports, including one that has already run and one inside `node_modules`. + +An update in place changes the exports that the package has. It doesn't add an export that only the mock has, and it reads a getter once. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. @@ -477,7 +479,7 @@ If no module makes Bun update the package, a module that has already run keeps t To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded. -A `vi.mock()` call that runs while a `--preload` script loads updates a loaded package in place, even if every module that imports the package has finished running. So does a call in a hook that the preload registered, such as `beforeAll()`. A call that a test makes later, through a function that the preload defined, follows the rules above. +A `vi.mock()` call that runs while a `--preload` script loads updates a loaded package in place, even if every module that imports the package has finished running. So does a call in a `beforeAll()` hook that the preload registered. A call that a test makes later, through a function that the preload defined, follows the rules above. A mocked builtin such as `node:fs` is mocked for every importer with all of them. From 6c31bbf9b4cbdac63e60948249bd06444f0fc71c Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 10:28:15 +0000 Subject: [PATCH 5/8] docs: count a module that waits for an awaiting import as finished, and list what an update leaves real A module that waits for one of its imports, because that import uses top-level await, does not make Bun update a loaded package, although its code has not started. Say so in the definition of "finished running". An update in place changes only the exports that the factory returned. An export that the factory left out keeps its real value, `default` included. A hoisted factory that reads a variable that is not initialized yet makes no update and reports no error. Say both next to the rule and in "When to Use Preload", and say that `mock.module()` and `jest.mock()` in a mocks file do not reach a CommonJS module that holds `require()` of a CommonJS package. --- docs/test/mocks.mdx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 078945fdca0f..d403e444586d 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -467,17 +467,19 @@ If the package is already loaded, what imported it keeps the real one, and the n The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then reads the mock through its imports, including one that has already run and one inside `node_modules`. -An update in place changes the exports that the package has. It doesn't add an export that only the mock has, and it reads a getter once. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). +An update in place changes only the exports that the factory returned and that the package has. An export that the factory left out keeps its real value. That includes `default`, so a default import keeps the real value unless the factory returns `default`. An export that only the mock has isn't added, and Bun reads a getter once. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). + +To update the package, Bun calls the factory at the `vi.mock()` call. A hoisted factory that reads a top-level variable of its file can't run there if the variable isn't initialized yet (see "Hoisting & Preloading" below). Bun then leaves the package as it is and reports no error. The next `import()` or `require()` calls the factory again. Declare what the factory needs with `vi.hoisted()`. For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. -A module has finished running once its code has run to the end or has reached a top-level `await`. A test file whose imports run one at a time has reached one. Modules that import each other finish together, when the last of them does. +A module has finished running once its code has run to the end or has reached a top-level `await`. A module that waits for one of its imports, because that import uses top-level `await`, also counts as finished once Bun has gone through its imports, although its code hasn't started. A test file whose imports run one at a time has reached a top-level `await`. Modules that import each other finish together, when the last of them does. The module that calls `vi.mock()` or `vi.doMock()` doesn't make Bun update the package. Neither does a CommonJS module, because `require()` isn't a static import. If no module makes Bun update the package, a module that has already run keeps the real package, as in Vitest. That happens in a test file that doesn't import the package itself, when it imports `./cart` first and `./mocks` second. It also happens, in either order, when `./cart` is a CommonJS module that sets `module.exports` or `exports` and calls `require()` for the package at its top level. Bun runs such a module as soon as it loads it, before the ES modules that the same file imports. -To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded. +To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded, but that update doesn't reach a CommonJS module that holds what `require()` returned for a CommonJS package (see "When to Use Preload" below). A `vi.mock()` call that runs while a `--preload` script loads updates a loaded package in place, even if every module that imports the package has finished running. So does a call in a `beforeAll()` hook that the preload registered. A call that a test makes later, through a function that the preload defined, follows the rules above. @@ -590,9 +592,9 @@ preload = ["./my-preload"] #### When to Use Preload -`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported, so a module that imports it gets the mocked version. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. +`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported. A module that imports it then reads the factory's value for each of the module's exports that the factory returned. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. -An update doesn't reach a value that a module copied out of the original before the mock, as `const { send } = require("pkg")` does. It doesn't reach the object that `require("pkg")` returned either, if `pkg` is a CommonJS module, because the update replaces its `module.exports`. +An update doesn't reach a value that a module copied out of the original before the mock, as `const { send } = require("pkg")` does. It doesn't reach the object that `require("pkg")` returned either, if `pkg` is a CommonJS module, because the update replaces its `module.exports`. An export that the factory left out keeps its real value, `default` included. A hoisted `jest.mock()` factory that can't run at the call makes no update (see [Mocking a Package](#mocking-a-package)). To prevent the original module from being evaluated at all, or to mock it for a module that copies a value out of it, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. @@ -814,7 +816,8 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place. For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun does that among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, that condition doesn't apply (see [Mocking a Package](#mocking-a-package)). +- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, if the factory can run at the call (see [Mocking a Package](#mocking-a-package)). +- For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun makes that update among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, that condition doesn't apply (see [Mocking a Package](#mocking-a-package)). - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. - Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run reads the mock through its imports from then on, and so does a module inside `node_modules` that imports the package. A value that either one copied out before the update stays the original. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. From 37595dde3d9fb52561da7fdfb6eb8ba10bc6fb12 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:11:49 +0000 Subject: [PATCH 6/8] docs: say which files run their imports one at a time, and that a partial mock still evaluates the original --- docs/test/mocks.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index d403e444586d..47a5ea042e8e 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -596,7 +596,7 @@ preload = ["./my-preload"] An update doesn't reach a value that a module copied out of the original before the mock, as `const { send } = require("pkg")` does. It doesn't reach the object that `require("pkg")` returned either, if `pkg` is a CommonJS module, because the update replaces its `module.exports`. An export that the factory left out keeps its real value, `default` included. A hoisted `jest.mock()` factory that can't run at the call makes no update (see [Mocking a Package](#mocking-a-package)). -To prevent the original module from being evaluated at all, or to mock it for a module that copies a value out of it, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. +To prevent the original module from being evaluated at all, or to mock it for a module that copies a value out of it, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. A factory that loads the original, with `importOriginal` or `jest.requireActual()`, still evaluates it. #### Practical Module Mock Examples @@ -816,7 +816,7 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- Only test files and `--preload` scripts run their imports one at a time. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, if the factory can run at the call (see [Mocking a Package](#mocking-a-package)). +- In Vitest every module runs its imports one at a time. In Bun only these do: a file with a hoisted call, and a test file or `--preload` script that imports `vitest` or runs with `globals = "vitest"`. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, if the factory can run at the call (see [Mocking a Package](#mocking-a-package)). - For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun makes that update among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, that condition doesn't apply (see [Mocking a Package](#mocking-a-package)). - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. - Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run reads the mock through its imports from then on, and so does a module inside `node_modules` that imports the package. A value that either one copied out before the update stays the original. In Vitest both keep the real package. From 556db71c2985a171b990d808a8e12cef6709bebe Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:49:12 +0000 Subject: [PATCH 7/8] docs: scope the package update rule to one example with stated assumptions A sentence-by-sentence check of the longer text found that almost every general statement about the moment of the update has an exception one step away: top-level await, CommonJS files, import cycles, earlier test files of the run, factories that read their own file. Describe one example instead (a mocks file imported before and after the module under test), list what the example assumes, and tell the reader not to rely on import order for a mock that a test needs. Keep the limits of an update in place that hold for a module that imports the package with `import`, and say that a workspace package that `node_modules` links to counts as a module of the project. --- docs/test/mocks.mdx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 47a5ea042e8e..90d54d3d1f1d 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -465,23 +465,24 @@ jest.mock("dep", () => fake); // the library gets the fake too If the package is already loaded, what imported it keeps the real one, and the next `import()` or `require()` from your own code calls the factory. Modules that an earlier test file loaded are loaded again and get the mock, because in Vitest each file starts with nothing loaded. The real package is never loaded a second time: `vi.importActual()` returns the loaded one, and `vi.doUnmock()` brings it back. -The exception is when one of your own modules imports the package with a static `import` and hasn't finished running. Bun links static imports before it runs the modules (see "Hoisting & Preloading" below), so that module is already linked to the real package, and only an update reaches it. Bun updates the loaded package in place. Every module that imports the package then reads the mock through its imports, including one that has already run and one inside `node_modules`. +The exception is a package that a module of yours is linked to, when that module has yet to run. For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks` and then `./cart`. `./mocks` calls `vi.mock()` with a factory, and `./cart` imports the package. Bun links both imports before it runs `./mocks` (see "Hoisting & Preloading" below), so `./cart` is already linked to the real package, and only an update of the loaded package reaches it. Bun updates the package in place, and `./cart` gets the mock. So does a module that has already run, and a module inside `node_modules` that imports the package. -An update in place changes only the exports that the factory returned and that the package has. An export that the factory left out keeps its real value. That includes `default`, so a default import keeps the real value unless the factory returns `default`. An export that only the mock has isn't added, and Bun reads a getter once. A value that a module copied out of the package earlier stays the original (see "When to Use Preload" below). +With the two imports in the other order, `./cart` has already run when `./mocks` calls `vi.mock()`. If no other module of yours is linked to the package and has yet to run, Bun updates nothing, and `./cart` keeps the real package, as in Vitest. -To update the package, Bun calls the factory at the `vi.mock()` call. A hoisted factory that reads a top-level variable of its file can't run there if the variable isn't initialized yet (see "Hoisting & Preloading" below). Bun then leaves the package as it is and reports no error. The next `import()` or `require()` calls the factory again. Declare what the factory needs with `vi.hoisted()`. +The example assumes all of these: -For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks`, which calls `vi.mock()`, and then `./cart`, which imports the package. `./cart` hasn't run when `./mocks` runs, so Bun updates the package. Bun also updates the package if the test file imports the package itself, because the file hasn't finished running while its imports run. +- `./mocks` and `./cart` are ES modules. +- No module in it waits at a top-level `await`. +- The factory doesn't read a variable or an import of `./mocks` that isn't initialized yet (see "Hoisting & Preloading" below). +- No earlier test file of the same run has loaded `./cart`. -A module has finished running once its code has run to the end or has reached a top-level `await`. A module that waits for one of its imports, because that import uses top-level `await`, also counts as finished once Bun has gone through its imports, although its code hasn't started. A test file whose imports run one at a time has reached a top-level `await`. Modules that import each other finish together, when the last of them does. +If one of them is false, `./cart` can get the real package even when `./mocks` comes first, and Bun reports no error. So don't rely on the order of two imports for a mock that a test needs. Call `vi.mock()` at the top level of the test file, or mock the package in a `--preload` script. -The module that calls `vi.mock()` or `vi.doMock()` doesn't make Bun update the package. Neither does a CommonJS module, because `require()` isn't a static import. +An update in place writes each value that the factory returned to the package's export of the same name. For a module that imports the package with `import`, an export that the factory left out keeps the value it has, `default` included, and an export that only the mock has isn't added. -If no module makes Bun update the package, a module that has already run keeps the real package, as in Vitest. That happens in a test file that doesn't import the package itself, when it imports `./cart` first and `./mocks` second. It also happens, in either order, when `./cart` is a CommonJS module that sets `module.exports` or `exports` and calls `require()` for the package at its top level. Bun runs such a module as soon as it loads it, before the ES modules that the same file imports. +A workspace package that `node_modules` links to counts as a module of your own, not as a package, because Bun goes by its real path. -To give such a module the mock, call `vi.mock()` at the top level of the test file or in a `--preload` script. Both calls run before the file's imports. `mock.module()` and `jest.mock()` also update a package that's already loaded, but that update doesn't reach a CommonJS module that holds what `require()` returned for a CommonJS package (see "When to Use Preload" below). - -A `vi.mock()` call that runs while a `--preload` script loads updates a loaded package in place, even if every module that imports the package has finished running. So does a call in a `beforeAll()` hook that the preload registered. A call that a test makes later, through a function that the preload defined, follows the rules above. +A `--preload` script that mocks a package that an earlier preload has loaded also updates the package in place. A mocked builtin such as `node:fs` is mocked for every importer with all of them. @@ -592,11 +593,11 @@ preload = ["./my-preload"] #### When to Use Preload -`mock.module()`, `jest.mock()` and `jest.doMock()` update a module that's already been imported. A module that imports it then reads the factory's value for each of the module's exports that the factory returned. Bun has already evaluated the original module, though, so its side effects have already happened. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported. See [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above. +With `mock.module()`, `jest.mock()` and `jest.doMock()`, mocking a module that's already been imported updates the module cache, so a module that imports it reads the mocked exports. `vi.mock()` and `vi.doMock()` don't always update a module that's already been imported (see [Mocking a Package](#mocking-a-package) and "Hoisting & Preloading" above). Bun has already evaluated the original module, though, so its side effects have already happened. -An update doesn't reach a value that a module copied out of the original before the mock, as `const { send } = require("pkg")` does. It doesn't reach the object that `require("pkg")` returned either, if `pkg` is a CommonJS module, because the update replaces its `module.exports`. An export that the factory left out keeps its real value, `default` included. A hoisted `jest.mock()` factory that can't run at the call makes no update (see [Mocking a Package](#mocking-a-package)). +A value that a module copied out of the original before the mock stays the original, as with `const { send } = require("pkg")`. So does the object that `require("pkg")` returned for a CommonJS module, because the update replaces its `module.exports`. -To prevent the original module from being evaluated at all, or to mock it for a module that copies a value out of it, mock it before it's imported. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. A factory that loads the original, with `importOriginal` or `jest.requireActual()`, still evaluates it. +To prevent the original module from being evaluated at all, mock it before it's imported, with a factory that doesn't load the original. Use `vi.mock()` or `jest.mock()`, which are hoisted, or mock it in a `--preload` script. #### Practical Module Mock Examples @@ -816,10 +817,9 @@ test("lazy import", async () => { - To update a module in place, Bun calls the factory when you mock the module, not at the next import. If the factory returns a promise, an `import()` of the module doesn't wait for it. Await what `vi.mock()` returns instead. `vi.doMock()` does the same for a builtin module such as `node:fs`. - After `vi.doUnmock()`, a module that imported the mock is evaluated again at its next import, and gets the real module. After a second `vi.doMock()` of the same module, it gets the new mock. In Vitest it stays cached with the first mock until `vi.resetModules()`. - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. -- In Vitest every module runs its imports one at a time. In Bun only these do: a file with a hoisted call, and a test file or `--preload` script that imports `vitest` or runs with `globals = "vitest"`. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, if the factory can run at the call (see [Mocking a Package](#mocking-a-package)). -- For a package, a module that a `--preload` script loaded, or a module that only `vi.importActual()` has loaded, Bun makes that update among a test file's imports only if one of your modules that imports it hasn't finished running. Among a `--preload` script's imports, that condition doesn't apply (see [Mocking a Package](#mocking-a-package)). +- Only some files run their imports one at a time (see "Hoisting & Preloading" above). In Vitest every module of your own does. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, with the assumptions of the example in [Mocking a Package](#mocking-a-package). A package, a module that a `--preload` script loaded, and a module that only `vi.importActual()` has loaded follow the rules for a package there. - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. -- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run reads the mock through its imports from then on, and so does a module inside `node_modules` that imports the package. A value that either one copied out before the update stays the original. In Vitest both keep the real package. +- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run gets the mock, and so does a module inside `node_modules` that imports the package. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. - Every module outside `node_modules` that imports the mocked module gets the mock. In Vitest, a module that's first loaded while the factory runs gets the original and keeps it. - If two factories import each other's modules, the one that asks last gets the original. Vitest calls one of the two factories a second time instead. From c491ebc455642d50ea42327faa6f0f6cfd23d05f Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 9 Nov 2026 12:01:08 +0000 Subject: [PATCH 8/8] docs: say that the update reaches modules that use `import`, and point to the imports that run first A CommonJS library that holds what `require()` returned for a CommonJS package keeps the real object after an update in place, so name `import` where the text says which modules get the mock. A test file can have imports that stay static and run before a hoisted `vi.mock()` call. Point the advice at the section that lists them. --- docs/test/mocks.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx index 90d54d3d1f1d..791db279b484 100644 --- a/docs/test/mocks.mdx +++ b/docs/test/mocks.mdx @@ -465,7 +465,7 @@ jest.mock("dep", () => fake); // the library gets the fake too If the package is already loaded, what imported it keeps the real one, and the next `import()` or `require()` from your own code calls the factory. Modules that an earlier test file loaded are loaded again and get the mock, because in Vitest each file starts with nothing loaded. The real package is never loaded a second time: `vi.importActual()` returns the loaded one, and `vi.doUnmock()` brings it back. -The exception is a package that a module of yours is linked to, when that module has yet to run. For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks` and then `./cart`. `./mocks` calls `vi.mock()` with a factory, and `./cart` imports the package. Bun links both imports before it runs `./mocks` (see "Hoisting & Preloading" below), so `./cart` is already linked to the real package, and only an update of the loaded package reaches it. Bun updates the package in place, and `./cart` gets the mock. So does a module that has already run, and a module inside `node_modules` that imports the package. +The exception is a package that a module of yours is linked to, when that module has yet to run. For example, a test file that uses `bun:test` and has no hoisted call imports `./mocks` and then `./cart`. `./mocks` calls `vi.mock()` with a factory, and `./cart` imports the package. Bun links both imports before it runs `./mocks` (see "Hoisting & Preloading" below), so `./cart` is already linked to the real package, and only an update of the loaded package reaches it. Bun updates the package in place, and `./cart` gets the mock. So do the other modules that import the package with `import`, including one that has already run and one inside `node_modules`. With the two imports in the other order, `./cart` has already run when `./mocks` calls `vi.mock()`. If no other module of yours is linked to the package and has yet to run, Bun updates nothing, and `./cart` keeps the real package, as in Vitest. @@ -476,7 +476,7 @@ The example assumes all of these: - The factory doesn't read a variable or an import of `./mocks` that isn't initialized yet (see "Hoisting & Preloading" below). - No earlier test file of the same run has loaded `./cart`. -If one of them is false, `./cart` can get the real package even when `./mocks` comes first, and Bun reports no error. So don't rely on the order of two imports for a mock that a test needs. Call `vi.mock()` at the top level of the test file, or mock the package in a `--preload` script. +If one of them is false, `./cart` can get the real package even when `./mocks` comes first, and Bun reports no error. So don't rely on the order of two imports for a mock that a test needs. Call `vi.mock()` at the top level of the test file (see "Hoisting & Preloading" below for the imports that still run first), or mock the package in a `--preload` script. An update in place writes each value that the factory returned to the package's export of the same name. For a module that imports the package with `import`, an export that the factory left out keeps the value it has, `default` included, and an export that only the mock has isn't added. @@ -819,7 +819,7 @@ test("lazy import", async () => { - Without `--isolate`, test files share the modules they load. For `vi.doMock()`, a module that an earlier file loaded counts as not loaded, even if this file imports it too. `vi.importActual()` and `importOriginal` evaluate it again. - Only some files run their imports one at a time (see "Hoisting & Preloading" above). In Vitest every module of your own does. Any other module's imports are all linked before the first one runs, as are those of a test file that uses `bun:test` and has no hoisted call. If one of them calls `vi.mock()` for a module of your own that another one imports, Bun updates that module in place, with the assumptions of the example in [Mocking a Package](#mocking-a-package). A package, a module that a `--preload` script loaded, and a module that only `vi.importActual()` has loaded follow the rules for a package there. - If a module that is imported with a static import calls `vi.mock()` with an async factory, a module that is imported after it can run before the factory settles. What that module reads from the mocked module while it loads is the original. What it reads later is the mock. -- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), a module that has already run gets the mock, and so does a module inside `node_modules` that imports the package. In Vitest both keep the real package. +- Where a loaded package is updated in place (see [Mocking a Package](#mocking-a-package)), the modules that import it with `import` get the mock, including one that has already run and one inside `node_modules`. In Vitest both keep the real package. - If a module that is imported with a static import calls `vi.mock()` for a module that is waiting for an import with an async factory, the original still runs, and assigns its `const`, `let` and `class` exports. A module that reads one of those while it loads gets the original. What it reads later is the mock, and so are the `function` exports all along. - Every module outside `node_modules` that imports the mocked module gets the mock. In Vitest, a module that's first loaded while the factory runs gets the original and keeps it. - If two factories import each other's modules, the one that asks last gets the original. Vitest calls one of the two factories a second time instead.