---
id: mocking
title: Mocking
---
When writing tests it's only a matter of time before you need to create a "fake" version of an internal — or external — service. This is commonly referred to as mocking. WebdriverIO provides utility functions to help you out. You can `import { fn, spyOn, mock, unmock } from '@wdio/browser-runner'` to access it. See more information about the available mocking utilities in the [API docs](/docs/api/modules#wdiobrowser-runner).
## Functions
In order to validate whether certain function handler are called as part of your component tests, the `@wdio/browser-runner` module exports mocking primitives you can use to test, if these functions have been called. You can import these methods via:
```js
import { fn, spyOn } from '@wdio/browser-runner'
```
By importing `fn` you can create a spy function (mock) to track its execution and with `spyOn` track a method on an already created object.
The full example can be found in the [Component Testing Example](https://github.com/webdriverio/component-testing-examples/blob/main/react-typescript-vite/src/tests/LoginForm.test.tsx) repository.
```ts
import React from 'react'
import { $, expect } from '@wdio/globals'
import { fn } from '@wdio/browser-runner'
import { Key } from 'webdriverio'
import { render } from '@testing-library/react'
import LoginForm from '../components/LoginForm'
describe('LoginForm', () => {
it('should call onLogin handler if username and password was provided', async () => {
const onLogin = fn()
render()
await $('input[name="username"]').setValue('testuser123')
await $('input[name="password"]').setValue('s3cret')
await browser.keys(Key.Enter)
/**
* verify the handler was called
*/
expect(onLogin).toBeCalledTimes(1)
expect(onLogin).toBeCalledWith(expect.equal({
username: 'testuser123',
password: 's3cret'
}))
})
})
```
The full example can be found in the [examples](https://github.com/webdriverio/webdriverio/blob/main/examples/wdio/browser-runner/lit.test.js) directory.
```js
import { expect, $ } from '@wdio/globals'
import { spyOn } from '@wdio/browser-runner'
import { html, render } from 'lit'
import { SimpleGreeting } from './components/LitComponent.ts'
const getQuestionFn = spyOn(SimpleGreeting.prototype, 'getQuestion')
describe('Lit Component testing', () => {
it('should render component', async () => {
render(
html``,
document.body
)
const innerElem = await $('simple-greeting').$('p')
expect(await innerElem.getText()).toBe('Hello, WebdriverIO! How are you today?')
})
it('should render with mocked component function', async () => {
getQuestionFn.mockReturnValue('Does this work?')
render(
html``,
document.body
)
const innerElem = await $('simple-greeting').$('p')
expect(await innerElem.getText()).toBe('Hello, WebdriverIO! Does this work?')
})
})
```
WebdriverIO just re-exports [`@vitest/spy`](https://www.npmjs.com/package/@vitest/spy) here which is a lightweight Jest compatible spy implementation that can be used with WebdriverIOs [`expect`](/docs/api/expect-webdriverio) matchers. You can find more documentation on these mock functions on the [Vitest project page](https://vitest.dev/api/mock.html).
Of course, you can also install and import any other spy framework, e.g. [SinonJS](https://sinonjs.org/), as long as it supports the browser environment.
## Modules
Mock local modules or observe third-party-libraries, that are invoked in some other code, allowing you to test arguments, output or even redeclare its implementation.
There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.
### Mocking File Imports
Let's imagine our component is importing a utility method from a file to handle a click.
```js title=utils.js
export function handleClick () {
// handler implementation
}
```
In our component the click handler is used as following:
```ts title=LitComponent.js
import { handleClick } from './utils.js'
@customElement('simple-button')
export class SimpleButton extends LitElement {
render() {
return html``
}
}
```
To mock the `handleClick` from `utils.js` we can use the `mock` method in our test as following:
```js title=LitComponent.test.js
import { expect, $ } from '@wdio/globals'
import { mock, fn } from '@wdio/browser-runner'
import { html, render } from 'lit'
import { SimpleButton } from './LitComponent.ts'
import { handleClick } from './utils.js'
/**
* mock named export "handleClick" of `utils.ts` file
*/
mock('./utils.ts', () => ({
handleClick: fn()
}))
describe('Simple Button Component Test', () => {
it('call click handler', async () => {
render(html``, document.body)
await $('simple-button').$('button').click()
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
```
### Mocking Dependencies
Suppose we have a class that fetches users from our API. The class uses [`axios`](https://github.com/axios/axios) to call the API then returns the data attribute which contains all the users:
```js title=users.js
import axios from 'axios';
class Users {
static all() {
return axios.get('/users.json').then(resp => resp.data)
}
}
export default Users
```
Now, in order to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the `mock(...)` function to automatically mock the axios module.
Once we mock the module we can provide a [`mockResolvedValue`](https://vitest.dev/api/mock.html#mockresolvedvalue) for `.get` that returns the data we want our test to assert against. In effect, we are saying that we want `axios.get('/users.json')` to return a fake response.
```js title=users.test.js
import axios from 'axios'; // imports defined mock
import { mock, fn } from '@wdio/browser-runner'
import Users from './users.js'
/**
* mock default export of `axios` dependency
*/
mock('axios', () => ({
default: {
get: fn()
}
}))
describe('User API', () => {
it('should fetch users', async () => {
const users = [{name: 'Bob'}]
const resp = {data: users}
axios.get.mockResolvedValue(resp)
// or you could use the following depending on your use case:
// axios.get.mockImplementation(() => Promise.resolve(resp))
const data = await Users.all()
expect(data).toEqual(users)
})
})
```
## Partials
Subsets of a module can be mocked and the rest of the module can keep their actual implementation:
```js title=foo-bar-baz.js
export const foo = 'foo';
export const bar = () => 'bar';
export default () => 'baz';
```
The original module will be passed into the mock factory which you can use to e.g. partially mock a dependency:
```js
import { mock, fn } from '@wdio/browser-runner'
import defaultExport, { bar, foo } from './foo-bar-baz.js';
mock('./foo-bar-baz.js', async (originalModule) => {
// Mock the default export and named export 'foo'
// and propagate named export from the original module
return {
__esModule: true,
...originalModule,
default: fn(() => 'mocked baz'),
foo: 'mocked foo',
}
})
describe('partial mock', () => {
it('should do a partial mock', () => {
const defaultExportResult = defaultExport();
expect(defaultExportResult).toBe('mocked baz');
expect(defaultExport).toHaveBeenCalled();
expect(foo).toBe('mocked foo');
expect(bar()).toBe('bar');
})
})
```
## Manual Mocks
Manual mocks are defined by writing a module in a `__mocks__/` (see also `automockDir` option) subdirectory. If the module you are mocking is a Node module (e.g.: `lodash`), the mock should be placed in the `__mocks__` directory and will be automatically mocked. There's no need to explicitly call `mock('module_name')`.
Scoped modules (also known as scoped packages) can be mocked by creating a file in a directory structure that matches the name of the scoped module. For example, to mock a scoped module called `@scope/project-name`, create a file at `__mocks__/@scope/project-name.js`, creating the `@scope/` directory accordingly.
```
.
├── config
├── __mocks__
│ ├── axios.js
│ ├── lodash.js
│ └── @scope
│ └── project-name.js
├── node_modules
└── views
```
When a manual mock exists for a given module, WebdriverIO will use that module when explicitly calling `mock('moduleName')`. However, when automock is set to true, the manual mock implementation will be used instead of the automatically created mock, even if `mock('moduleName')` is not called. To opt out of this behavior you will need to explicitly call `unmock('moduleName')` in tests that should use the actual module implementation, e.g.:
```js
import { unmock } from '@wdio/browser-runner'
unmock('lodash')
```
## Hoisting
In order to get mocking to work in the browser, WebdriverIO rewrites the test files and hoists the mock calls above everything else (see also [this blog post](https://www.coolcomputerclub.com/posts/jest-hoist-await/) on the hoisting problem in Jest). This limits the way you can pass in variables into the mock resolver, e.g.:
```js title=component.test.js
import dep from 'dependency'
const variable = 'foobar'
/**
* ❌ this fails as `dep` and `variable` are not defined inside the mock resolver
*/
mock('./some/module.ts', () => ({
exportA: dep,
exportB: variable
}))
```
To fix this you have to define all used variables inside the resolver, e.g.:
```js title=component.test.js
/**
* ✔️ this works as all variables are defined within the resolver
*/
mock('./some/module.ts', async () => {
const dep = await import('dependency')
const variable = 'foobar'
return {
exportA: dep,
exportB: variable
}
})
```
## Requests
If you are looking for mocking browser requests, e.g. API calls, head over to the [Request Mock and Spies](/docs/mocksandspies) section.