Mock Network Requests in Vue Test Utils with Mirage
Use your Mirage server to test your Vue application under different server scenarios using Vue Test Utils.
This is a quickstart guide for people already using Vue Test Utils in their Vue apps.
Step 1: Install Mirage
First, make sure you have Mirage installed:
# Using npm
npm install --save-dev miragejs
# Using Yarn
yarn add --dev miragejs
Step 2: Define your server
Create a new server.js
file and define your mock server.
Here's a basic example:
// src/server.js
import { createServer, Model } from "miragejs"
export function makeServer({ environment = "development" } = {}) {
let server = createServer({
environment,
models: {
user: Model,
},
seeds(server) {
server.create("user", { name: "Bob" })
server.create("user", { name: "Alice" })
},
routes() {
this.namespace = "api"
this.get("/users", (schema) => {
return schema.users.all()
})
},
})
return server
}
In a Vue CLI, put this file in
src/server.js
so that changes to it trigger rebuilds.
Step 3: Create a test file that uses Mirage
Here's the Vue component we'll be testing.
<!-- src/App.vue -->
<template>
<div v-if="serverError" data-testid="server-error">{{ serverError }}</div>
<div v-else-if="users.length === 0" data-testid="no-users">No users!</div>
<div v-else>
<ul id="users">
<li
v-for="user in users"
v-bind:key="user.id"
:data-testid="'user-' + user.id"
>
{{ user.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
users: [],
serverError: null,
}
},
created() {
fetch("/api/users")
.then((res) => res.json())
.then((json) => {
if (json.error) {
this.serverError = json.error
} else {
this.users = json.users
}
})
},
}
</script>
Create a new src/App.spec.js
file, import your makeServer
function, and start and shutdown Mirage using beforeEach
and afterEach
, making sure to pass the test
environment to Mirage:
// src/App.spec.js
import { mount } from "@vue/test-utils"
import { makeServer } from "./server"
import App from "./App.vue"
let server
beforeEach(() => {
server = makeServer({ environment: "test" })
})
afterEach(() => {
server.shutdown()
})
Step 4: Write tests using your Mirage server
Use your tests to seed Mirage with different data scenarios, then assert against the state of your UI.
In the
test
environment, Mirage doesn't load its databaseseeds
, so that the server starts out empty for each test run.
it("shows the users from our server", async () => {
server.create("user", { id: 1, name: "Luke" })
server.create("user", { id: 2, name: "Leia" })
const wrapper = mount(App)
// let's wait for our vue component to finish loading data
// we know it's done when the data-testid enters the dom.
await waitFor(wrapper, '[data-testid="user-1"]')
await waitFor(wrapper, '[data-testid="user-2"]')
expect(wrapper.find('[data-testid="user-1"]').text()).toBe("Luke")
expect(wrapper.find('[data-testid="user-2"]').text()).toBe("Leia")
})
it("shows a message if there are no users", async () => {
// Don't create any users
const wrapper = mount(App)
await waitFor(wrapper, '[data-testid="no-users"]')
expect(wrapper.find('[data-testid="no-users"]').text()).toBe("No users!")
})
// This helper method returns a promise that resolves
// once the selector enters the wrapper's dom.
const waitFor = function (wrapper, selector) {
return new Promise((resolve) => {
const timer = setInterval(() => {
const userEl = wrapper.findAll(selector)
if (userEl.length > 0) {
clearInterval(timer)
resolve()
}
}, 100)
})
}
Step 5: Alter your Mirage server to test different server states
In addition to different data scenarios, you can use your tests to reconfigure your Mirage server to test new situations.
For example, you can test an error state like this:
// src/App.spec.js
import { Response } from "miragejs"
it("handles error responses from the server", async () => {
// Override Mirage's route handler for /users, just for this test
server.get("/users", () => {
return new Response(
500,
{},
{
error: "The database is on vacation.",
}
)
})
const wrapper = mount(App)
await waitFor(wrapper, '[data-testid="server-error"]')
expect(wrapper.find('[data-testid="server-error"]').text()).toBe(
"The database is on vacation."
)
})
Because of the way we setup Mirage using beforeEach
and afterEach
, each test will get a fresh Mirage server based on your main server definition. Any overrides you make within a test will be isolated to that test.