Mocking and Stubbing API calls in Vue Apps with Cypress and Jest

Almost all single page applications will make many calls to external services. I will discuss how to test API calls, specifically:
- Unit testing Vuex actions that use axios
- End to end (e2e) testing using Cypress
The source code for this project is available here.
We will start at the bottom of the test pyramid with some unit tests, and finish up with some e2e tests.

Setup
Install the vue-cli
with npm install -g @vue/cli
, and then run vue create api-tests
. Select "Manually select features" and choose the following:
- Babel
- Vuex
- Unit Testing
- E2E Testing
For unit testing, we want jest
, and for e2e select cypress
. After the installation finishes, cd api-tests
and install Axios with npm install axios
.
Unit Testing Axios with Jest
We will be using jsonplaceholder
, a service which simulates a REST api. The endpoint is https://jsonplaceholder.typicode.com/posts/1
and the response looks like this:
We will be doing TDD: write the test, watch it fails, and then make it pass.
We will see how to mock axios
in two situations:
- A Vuex action, which makes an API call and commits the result
- An e2e test, which displays the result in a UI
Let’s start with the action test. Create the test file by running touch tests/unit/actions.spec.js
. Before writing any code, run the test and watch it fail with npm run test:unit
:
You should get:
Let’s add a test. In actions.spec.js
add the following:
Running this with npm run test:unit
yields:
As expected, the tests fails. We haven’t even created getPost
yet, so let's do so in src/store.js
. We will also export it seperately to the default export new Vuex.Store
:
Now we can import { actions }
in the spec:
This gives us a new error:
store
is not defined. The goal of this test is simply to make the API call, and commit whatever response comes back, so we will we mock store.commit
, and use Jest's .toHaveBeenCalledWith
matcher to make sure the response was committed with the correct mutation
handler. We pass store
as the first argument to getPost
, to simulate how Vuex
passes a reference to the store
as the first argument to all actions. Update the test:
jest.fn
is just a mock function - it doesn't actually do anything, but records useful data like how many times it was called, and with what arguments. The test now fails with different error:
This is what we want. The test is failing for the right reason — a SET_POST
mutation should have been committed, but was not. Update store.js
to actually make the API call:
Note we added async
to the function, we we can use await
on the axios API call. The test still fails with same error - we also need to prepend the action call in the test with await
:
Now we have two passing tests, including the default HelloWorld
spec included in the project:
This is not ideal, though — we are hitting a real network, which makes the unit test slow and prone to failure. Luckily, Jest let’s us mock dependencies, like axios
, in a number of ways. Let's see how to do so with jest.mock
.
Mocking Axios in the Action spec
Jest provides no less that four different ways to mock classes and modules, In large projects, I use manual mocks by creating a __mocks__
folder on the same level as node_modules
and exporting a mock axios module, however for the simple example I will use an ES6 class mock. I think both are fine, and have been tending towards this style as of late.
To mock axios
using an ES6 class mock, all you need to do is call jest.mock('axios')
and return a function with the desired implentation (since ES6 classes are really just functions under the hood). In this case, we want a get
function that returns a userId: 1
object. Update actions.spec.js
:
Easy. The test still passes, but now we are using a mock axios instead of a real network call. We should watch the test fail again, though, just to be should, so update the mock to return { userId: 2 }
instead:
Looks good — the test is failing for the right reason. Revert the test, and let’s move on to writing an e2e test.
Stubbing Axios in a component lifecycle
Now we know how to test an action uses axios
- how about in a component? In preparation for writing an e2e using Cypress, let's see an example of a component that makes an API call in its created
hook.
Open src/components/HelloWorld.vue
, and delete all the existing markup - you should be left with this:
We want to import axios
, and make an API request. The code will be similar to the code in getPost
. Lastly, we will render the title
of the post.
Run the application with npm run serve
. Visiting localhost:8080
should show the post title on the screen:
Let’s update the default test vue-cli
gave us in tests/e2e/specs/test.js
:
Run the test with npm run e2e
. Cypress has a great interface and is really easy to use. You should see:

Click ‘test.js’. A Chrome browser should open and if everything went well, you should see:

It works! However, this test suffers from the original problem we had in the unit test we wrote — it is using a real network call. We want to stub the network call, with a fake one, so we can consistently reproduce the same results without relying on a potentially flakey external API. To stub a response in Cypress, you need to do two things:
- Start a
cy.server
- Provide a
cy.route
cy.route
takes several forms. The one we will use is
cy.route(url, response)
Update the test to use a stubbed response:
If you still have the Cypress server running, saving should automatically rerun the specs. Now we have a failure:

We can see on the right hand side that the stubbed response was rendered! Simply update the spec to assert the stubbed title is rendered and everything should be green again:

Conclusion and Improvements
We saw how to mock axios
in a Vuex action spec, and how to stub the response using Cypress. With the advent of tools like Jest and Cypress, testing is extremely simple and actually makes development a lot more smooth one you are in the habit of writing tests.
Some improvements can be made, an are left an exercise:
- Write some tests using Cypress against a real server, to test critical paths in your application, such as sign up and login. Not stubbing, but against a real server
- Mock
axios
using Jest's manual mocks, where you create a__mocks__
folder with a mock implementation ofaxios
for use in your unit tests - Write a unit tests for
HelloWorld.vue
that mocksaxios
in the same way asactions.spec.js
The source code for this project is available here.
Originally published on Lachlan Miller’s blog.
✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.