---id: mocksandspiestitle: Request Mocks and Spies---WebdriverIO comes with built-in support for modifying network responses that allows you to focus testing your frontend application without having to setup your backend or a mock server. You can define custom responses for web resources like REST API requests in your test and modify them dynamically.:::infoNote that using the `mock` command requires support for Chrome DevTools protocol. That support is given if you run tests locally in a Chromium-based browser, via a Selenium Grid v4 or higher, or through a cloud vendor with support for the Chrome DevTools protocol (e.g. SauceLabs, BrowserStack, LambdaTest). Full cross-browser support will be available once the required primitives land in [Webdriver Bidi](https://wpt.fyi/results/webdriver/tests/bidi/network?label=experimental&label=master&aligned) and get implemented in the respective browser.:::## Creating a mockBefore you can modify any responses you have define a mock first. This mock is described by the resource url and can be filtered by the [request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) or [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers). The resource supports glob expressions by [minimatch](https://www.npmjs.com/package/minimatch):```js// mock all resources ending with "/users/list"const userListMock = await browser.mock('**/users/list')// or you can specify the mock by filtering resources by headers or// status code, only mock successful requests to json resourcesconst strictMock = await browser.mock('**', {// mock all json responsesrequestHeaders: { 'Content-Type': 'application/json' },// that were successfulstatusCode: 200})```## Specifying custom responsesOnce you have defined a mock you can define custom responses for it. Those custom responses can be either an object to respond a JSON, a local file to respond with a custom fixture or a web resource to replace the response with a resource from the internet.### Mocking API RequestsIn order to mock API requests where you expect a JSON response all you need to do is to call `respond` on the mock object with an arbitrary object you want to return, e.g.:```jsconst mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/')mock.respond([{title: 'Injected (non) completed Todo',order: null,completed: false}, {title: 'Injected completed Todo',order: null,completed: true}], {headers: {'Access-Control-Allow-Origin': '*'},fetchResponse: false})await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')await $('#todo-list li').waitForExist()console.log(await $$('#todo-list li').map(el => el.getText()))// outputs: "[ 'Injected (non) completed Todo', 'Injected completed Todo' ]"```You can also modify the response headers as well as the status code by passing in some mock response params as follows:```jsmock.respond({ ... }, {// respond with status code 404statusCode: 404,// merge response headers with following headersheaders: { 'x-custom-header': 'foobar' }})```If you want the mock not to call the backend at all, you can pass `false` for the `fetchResponse` flag.```jsmock.respond({ ... }, {// do not call the actual backendfetchResponse: false})```It is recommend to store custom responses in fixture files so you can just require them in your test as follows:```js// requires Node.js v16.14.0 or higher to support JSON import assertionsimport responseFixture from './__fixtures__/apiResponse.json' assert { type: 'json' }mock.respond(responseFixture)```### Mocking text resourcesIf you like to modify text resources like JavaScript, CSS files or other text based resources you can just pass in a file path and WebdriverIO will replaces the original resource with it, e.g.:```jsconst scriptMock = await browser.mock('**/script.min.js')scriptMock.respond('./tests/fixtures/script.js')// or respond with your custom JSscriptMock.respond('alert("I am a mocked resource")')```### Redirect web resourcesYou can also just replace a web resource with another web resource if your desired response is already hosted on the web. This works with individual page resources as well as with a webpage itself, e.g.:```jsconst pageMock = await browser.mock('https://google.com/')await pageMock.respond('https://webdriver.io')await browser.url('https://google.com')console.log(await browser.getTitle()) // returns "WebdriverIO ยท Next-gen browser and mobile automation test framework for Node.js"```### Dynamic responsesIf your mock response depends on the original resource response you can also dynamically modify the resource by passing in a function receives the original response as parameter and sets the mock based on the return value, e.g.:```jsconst mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/', {method: 'get'})mock.respond((req) => {// replace todo content with their list numberreturn req.body.map((item, i) => ({ ...item, title: i }))})await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')await $('#todo-list li').waitForExist()console.log(await $$('#todo-list li label').map((el) => el.getText()))// returns// [// '0', '1', '2', '19', '20',// '21', '3', '4', '5', '6',// '7', '8', '9', '10', '11',// '12', '13', '14', '15', '16',// '17', '18', '22'// ]```## Aborting mocksInstead of returning a custom response you can also just abort the request with one of the following HTTP errors:- Failed- Aborted- TimedOut- AccessDenied- ConnectionClosed- ConnectionReset- ConnectionRefused- ConnectionAborted- ConnectionFailed- NameNotResolved- InternetDisconnected- AddressUnreachable- BlockedByClient- BlockedByResponseThis is very useful if you want to block 3rd party script from your page that have a negative influence on your functional test. You can abort a mock by just calling `abort` or `abortOnce`, e.g.:```jsconst mock = await browser.mock('https://www.google-analytics.com/**')mock.abort('Failed')```## SpiesEvery mock is automatically a spy that counts the amount of requests the browser made to that resource. If you don't apply a custom response or abort reason to the mock it continues with the default response you would normally receive. This allows you to check how many times the browser made the request, e.g. to a certain API endpoint.```jsconst mock = await browser.mock('**/user', { method: 'post' })console.log(mock.calls.length) // returns 0// register userawait $('#username').setValue('randomUser')await $('password').setValue('password123')await $('password_repeat').setValue('password123')await $('button[type="submit"]').click()// check if API request was madeexpect(mock.calls.length).toBe(1)// assert responseexpect(mock.calls[0].body).toEqual({ success: true })```