With Vue Test Utils, how do `config.global` defaults combine with a test's own `global` mounting options, and where does that merge surprise people?
answer
- suite-wide defaults in a setup file
- arrays append, objects merge by key
- a per-test list adds, never replaces
- one shared object across tests
basics
~20 sEvery mount merges config.global into its own global option: plugins and mixins are appended, config entries first; provide, mocks, components, directives and stubs merge by key with the test's value winning. config.global is shared, so a change inside one test persists.
solid answer
~40 s`config.global`, imported from `@vue/test-utils`, holds defaults that apply to every `mount`; it is usually filled in a test setup file with things like a `$t` mock, global components and directives. On each mount Vue Test Utils merges it with the test's `global`: **arrays concatenate** (`plugins`, `mixins`, config's entries first), **objects merge one level by key** (`provide`, `mocks`, `components`, `directives`, `stubs`), and the test's value wins on a clash. Three surprises follow. Passing `plugins: [router]` in a test **adds** to the default plugins rather than replacing them. Overriding one `provide` key keeps all the other default keys. And because `config` is one shared object, assigning to `config.global.mocks` inside a test leaks into later tests until you restore it.
code
ts · 13 linesimport { config, mount } from '@vue/test-utils'
import ThemedHeader from './ThemedHeader.vue'
// setup file defaults
config.global.provide = { theme: 'light', locale: 'en' }
config.global.mocks = { $t: (key: string) => key }
test('a per-test provide overrides one key and keeps the rest', () => {
const wrapper = mount(ThemedHeader, {
global: { provide: { theme: 'dark' } } // locale 'en' still provided
})
expect(wrapper.classes()).toContain('header--dark')
})go deeper
Recall that config.global sets defaults for every mount and that a test's own global option can override them.
Explain the merge: arrays append with defaults first, object options merge per key with the test winning, and the defaults are one shared object.
Spot order-dependent failures caused by mutating config.global inside tests, and keep stateful collaborators out of the shared defaults.
Decide what the suite's defaults should contain so that tests stay readable without hiding the dependencies each test really has.
## What `config.global` is for `@vue/test-utils` exports a `config` object. Its `global` field has the same shape as the `global` mounting option: `plugins`, `config`, `mixins`, `mocks`, `provide`, `components`, `directives`, `stubs` and `renderStubDefaultSlot`. Whatever you put there is used **by default every time you call `mount`**. The typical home is a test setup file that runs before the suite: ```ts import { config } from '@vue/test-utils' import { vFocus } from '@/directives/focus' import BaseIcon from '@/components/BaseIcon.vue' config.global.mocks = { $t: (key: string) => key } config.global.components = { BaseIcon } config.global.directives = { focus: vFocus } ``` This mirrors what `main.ts` does with `app.component(...)`, `app.directive(...)` and `app.use(...)`: anything registered globally in the real app must also be registered for tests, or Vue warns that it failed to resolve the component or directive. ## How the merge works For each mount, Vue Test Utils builds one merged `global` from the defaults and the test's own options. The rules differ by field type: | Field | Merge rule | Winner on a clash | |---|---|---| | `plugins`, `mixins` | arrays concatenated, defaults first | both are applied | | `provide`, `mocks`, `components`, `directives` | shallow object spread | the test's key | | `stubs` | merged key by key; the array form means `true` for each name | the test's key | | `config` | spread one level, with `globalProperties` merged separately | the test's key | | `renderStubDefaultSlot` | the test's value if set, else the default | the test's value | The merged result is then applied to the fresh app that `mount` creates: the mocks mixin, app config, then `provide`, then `plugins` through `app.use`, then mixins, then `app.component` for each component that is not also stubbed, then `app.directive`. ## Surprise 1: a per-test plugin list adds, it does not replace If the setup file sets `config.global.plugins = [i18n]` and a test passes `global: { plugins: [router] }`, both are installed, `i18n` first. There is no way to *remove* a default plugin with the mount option alone. If one test needs the plugin absent, that test must temporarily change `config.global.plugins` and restore it afterwards. Listing the same plugin object in both places installs it once: Vue's `app.use` remembers installed plugins per app and, in development, warns `Plugin has already been applied to target app.` for the second attempt. ## Surprise 2: object options merge per key With `config.global.provide = { theme: 'light', locale: 'en' }` and a test passing `provide: { theme: 'dark' }`, the component sees `theme: 'dark'` **and** `locale: 'en'`. That is usually what you want, but it means a test cannot make a key disappear by leaving it out. To test the missing-injection path, the default must not contain that key. ## Surprise 3: the config object is shared `config` is created once and stored on the global object under a versioned symbol, so every mount in that test environment reads the same object. Consequences: - assigning `config.global.mocks.$t = ...` inside one test changes every later test in that environment; - the Vue Test Utils docs say this outright: the behaviour is global, so you may need to enable it before and disable it after a test; - a clean pattern is to save the old value in `beforeEach`, change it, and restore it in `afterEach`, or better, pass the override through the mount's own `global` option, which is scoped to that one mount. ## A checklist for a clean setup file 1. Register everything `main.ts` registers globally that is **stateless**: components, directives, a `$t` mock. 2. Keep defaults minimal, so each test still shows the collaborators it relies on. 3. Never assign to `config.global` inside a test body without restoring it. 4. Prefer the mount's own `global` option for anything only one test needs. ## What belongs in the defaults Put **stateless** things in `config.global`: a `$t` mock that returns keys, globally registered presentational components, directives, and stubs that every test wants. Keep **stateful** collaborators, such as a router or a store instance, out of it and create them per test, because the defaults are shared while each mount's app is not.
- A component uses a globally registered `<BaseIcon>` and a `v-focus` directive from `main.ts`. What happens in a test that registers neither?Vue warns that it failed to resolve the component or directive, renders the tag as a plain unknown element and skips the directive. Register them in `config.global.components` and `config.global.directives` once, or per test in `global.components` and `global.directives`; Vue Test Utils passes them to `app.component` and `app.directive` on each fresh app.
- Where should a test that needs one default plugin absent make that change?The mount option cannot remove it, because plugin arrays are concatenated. Change `config.global.plugins` for that test only, saving the original in `beforeEach` and restoring it in `afterEach`, or move that plugin out of the defaults and have the tests that need it pass it explicitly.
saying these in an interview costs you the question
- Passing global.plugins in a test replaces the plugins from config.global.
- Leaving a key out of a test's provide removes that key's default.
- config.global is copied per test file, so changing it inside a test is harmless.
- Globally registered components from main.ts are available in tests automatically.
- A router instance belongs in config.global so every test shares one.