# A Practical Guide to Mocking Svelte Stores with Vitest

> I'm using Svelte stores in this post as an example, but the principle applies to any function or variable you want to mock with `vitest`, with any js framework.

## Imagine this...

You've just refactored some complex code into a killer custom Svelte store that encapsulates some database calls and other logic. You can't wait to get this merged, but you know without tests the PR is getting sent straight back.

When it comes to unit testing some of your components that are using this new store, you only want to put the component under test, so you create a simple mock for your store.

Let's say your component uses a current project id...

```typescript
// Doesn't work 🚫
const mockStore = writable<number>(1);

vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: mockStore
}));

test('first test' => {	
	// ...
})

test('second test' => {
	// change mock
	mockStore.set(5)
	// ...
})
```

However, `vitest` gives you an error.

```plaintext
If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file.
```

All you've done is create a simple mock, right?

Before you start wondering if you can convince your teammates that there are legitimate reasons you cannot test this code, or you'll do the tests in a follow-up PR 🤨 let's look at how `vitest` is using mock and hoisting.

## `vi.mock`

The mock function has some nuances - regardless of where you have created your mock, it is always hoisted to the top of the file.

> JavaScript **Hoisting** refers to the process whereby the interpreter appears to move the *declaration* of functions, variables, classes, or imports to the top of their scope, prior to execution of the code.
> 
> [https://developer.mozilla.org/en-US/docs/Glossary/Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)

Whether your mock function is inside a `describe` block, in the `beforeAll` or inside a `test,` it doesn't matter to `vitest` - it is getting sent to the top of the file.

This has some side effects you need to be aware of...

### Re-mocking won't work

If you've not done much mocking before, you'd be forgiven for thinking something like this will work - re-calling the mock function at the beginning of each test.

```typescript
// this test may fail due to conflicting mock 🚫
test('first test' => {
	vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: writable<number>(1),
	}));
	// ...
})


test('second test' => {
	vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: writable<number>(5),
	}));
	// ...
})
```

However, now that we know these mocks get sent to the "top" of the file, this can lead to some head-scratching as we now have two conflicting mocks in the scope.

In practice, you only want to mock **once** and change the result of the mock.

### Local variables won't work

The next logical thing to do is to create a local store that we can modify before our tests run...

```typescript
const mockStore = writable<number>(1);

vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: mockStore
}));

test('first test' => {	
	// ...
})
```

Now we're hit with the error we mentioned in the intro. As the mock is being hoisted and the `mockStore` is not, when the mock runs for the first time `mockStore` does not yet exist.

Fine, let's try moving the store to another file and import it.

```typescript
import { mockStore } from './mocks'

vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: mockStore
}));

test('first test' => {	
	// ...
})
```

Nope, same problem, mock is being called before the import.

## `vi.hosting`

All is not lost though, `vi.hoisting` saves the day by allowing us to wrap anything we may want to define locally in this function to hoist above the mocks.

For local variables, this is now a very simple fix to our code...

```typescript
// Works! ✅
const mockStore = vi.hoisted(() => writable<number>(1));

vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: mockStore
}));

test('first test' => {	
	// ...
})


test('second test' => {
	// change mock
	mockStore.set(5)
	// ...
})
```

Awesome, but maybe you have a more complex mock and you prefer to have it in a separate file, how do we hoist the import above the mock?

### Dynamic imports

You will used to be importing like this:

```typescript
import { mockCurrentProjectStore } from './mockStores'
```

This is known as a static import, but there is another way known as, you probably guessed, a *dynamic* import.

```typescript
const { mockCurrentProjectStore } = await import('./mockStores')
```

There are some small differences that are not relevant to our discussion here, check the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) for more info, but for us, the key point is that we can pass this `await import()` expression to `vi.hoisted` to ensure that our import is available before our mock.

```typescript
// Works! ✅
const { mockStore } = vi.hoisted(() => await import('./mockStores'))

vi.mock('$lib/stores/projects', () => ({
	    currentProjectId: mockStore
}));

test('first test' => {	
	// ...
})
```

## Summing up

In my experience understanding what's happening behind the scenes with mock and hoisting makes testing much faster, enjoyable and intuitive. And remember, the above applies to any variables or functions you are using to mock.

### Additional Reading

Official docs - [https://vitest.dev/api/vi#vi-mock](https://vitest.dev/api/vi#vi-mock)

How does `vitest` do the hoisting? - [https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/hoistMocks.ts](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/hoistMocks.ts)

Dynamic imports - [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import)
