Skip to content

How to save and restore mock state

Use mock.snapshot() and mock.restore() to checkpoint the global mock registry and roll it back to that checkpoint later. This is useful when a test or a setup block installs global state that should not outlast a particular scope.


Save a snapshot and restore it after a test

Take a snapshot before the test body and restore it afterwards. Any mock.global.patch() call made inside the test — including a missed unpatch() — is rolled back automatically:

import { describe, it, assert, mock } from 'utest';
import * as fs from 'fs';

describe('my module', () => {
    it('does not require manual cleanup', () => {
        const snap = mock.snapshot();

        mock.global.patch('fs', { data: { '/etc/myapp.conf': 'debug=0' } });

        assert.match('debug=0', fs.readfile('/etc/myapp.conf'));

        mock.restore(snap);

        assert.match(null, fs.readfile('/etc/myapp.conf'), 'global patch is gone');
    });
});

Use beforeEach and afterEach to bracket an entire describe block

Take the snapshot once in beforeEach and restore it in afterEach. Every test in the block starts from the same clean baseline and any patch it forgets to clean up is silently recovered:

import { describe, it, beforeEach, afterEach, assert, mock } from 'utest';
import * as fs from 'fs';

describe('suite with shared baseline', () => {
    let snap;

    beforeEach(() => {
        // Establish a known starting state before each test.
        mock.global.patch('fs', { data: { '/etc/base.conf': 'v=1' } });
        snap = mock.snapshot();
    });

    afterEach(() => {
        mock.restore(snap);
    });

    it('can override the baseline in the test body', () => {
        mock.global.patch('fs', { data: { '/etc/base.conf': 'v=2' } });
        assert.match('v=2', fs.readfile('/etc/base.conf'));
        // afterEach restores to the pre-test snapshot regardless
    });

    it('starts clean even though the previous test patched', () => {
        assert.match('v=1', fs.readfile('/etc/base.conf'), 'previous override is gone');
    });
});

What a snapshot captures

mock.snapshot() saves the global state of every module that has been registered with the mock engine at the moment of the call:

Captured Not captured
Data from mock.global.patch() Transient layers from mock.inject()
Behavior overrides from mock.global.patch()
The strict flag
The proxy object

Scoped mock.inject() layers are not included in a snapshot because they are already guaranteed to be popped when their own callback exits. A snapshot taken inside a mock.inject() callback will not capture that layer.


Difference between restore() and reset()

Both functions clear all active mock.inject() layers, but they differ in what they do to global state:

mock.restore(snap) mock.reset()
Clears inject() layers Yes Yes
Resets global.patch() state Yes — reverts to the snapshot No — global patches remain active

Use restore() when you want to return to a known checkpoint.
Use reset() only when you want to clear transient layers mid-test without touching global state — for example, to reset inner injection state without unpatching a module installed at suite scope.


Restore is always safe to call with the same snapshot twice

Each mock.restore() call deep-clones the snapshot's channel data before writing it into the registry. Calling restore(snap) a second time after modifying state produces exactly the same result as the first call — the snapshot itself is never mutated:

const snap = mock.snapshot();

mock.global.patch('fs', { data: { '/a': '1' } });
mock.restore(snap);    // state is clean

mock.global.patch('fs', { data: { '/b': '2' } });
mock.restore(snap);    // state is clean again — snap is unchanged

Next steps