Header image of Testing Vue in Laravel with Jest - let's make it a little more easy

Testing Laravel is an outright joy, but simply setting up tests for Vue.js is still a dangerous fight with the NODE dragon.

But what can we do? Is the only option to Google-deep-dive and assemble your own Frankenstein's Assertion Webpack Babel Monster™?

In this post I will nerd out on explanations, but also try to simplify the setup. I wrote a npm package to automate most of it, so if you are in a hurry jump there.

JavaScript packages change all the time, most blogposts and tutorials are likely out of date, including this one.

Be cautious if it's 2030 when you are reading this, some things might have changed. I tried my best to write about the prerequisites, but there are so many things that are impossible to plan for.

By the way, I greet you future-person! Do we have jetpacks yet? 🚀👋.

Let's start: Laravel, Vue and prerequisites

There are a million ways you could have configured Laravel and Vue, so trying to foresee all of them is nearly impossible for me. Still, here are a few things that need to be given and could cause problems:

Laravel

This tutorial assumes a rather conventional frontend setup, so you most likely use Laravel Mix with a few configurations. If you toyed a lot with your build setup or replaced Laravel Mix entirely, there might by a few bombs waiting to go off.

I tested this with most of my projects though, and it worked well going back to Laravel 5.7. with Laravel Mix 2.0.

Vue

This tutorial deals with single file components and is meant only for Vue 2.x currently.

As soon as Laravel Mix in version 6.x is out of beta I will have a look at the setup for Vue 3 and hopefully make it work :)

All set? Okay, let's start:

Just install 500 NPM packages

Okay, that's a joke with a grain of truth. Most tutorials just throw a lot of npm install statements at you - and there is good reason for it.

Explaining what all these packages do is cumbersome, and most readers don't have the time for an in depth explanation anyways. I will try to explain a little more than normal here.

In a hurry? Read the summary

Still reading? Okay, let's get into the weeds, it might get quite nerdy from here!

GIF of Christina Aguilera dancing to "Let's get nerdy"

Basics, bring the basics

Let's start simple, we will need jest as our "testrunner", that's the JavaScript equivalent of PHPUnit.

There are essentially two test runners/frameworks that are recommended by the Vue Docs: Mocha and Jest. Since I also work with React I have experience with Jest. There is a lot already build in, which means less to worry about, right?

Admittedly it was also really hard for me to just get up and running with mocha and mocha webpack. There is a good Laracasts video series, but nothing seem to work with newer version of laravel-mix.

In theory, you could just use your existing Webpack configuration generated by Laravel Mix, but in practice this turned out to be configuration nightmare. Smarter people than me might have made it work though, I could not.

We also will need vue-test-utils: Think of those like special assertions for vue.

npm install jest vue-test-utils

Don't forget, that you will obviously need Vue.js in version 2 itself, and it's template compiler for the single file components. If you haven't already, run:

npm install vue@^2.0 vue-template-compiler

Into the config abyss

So we have a testrunner and Vue. It would be awesome if we could simply write our first test now right? 🙄

Bad Little Britain Joke about "Javascript says no"

First we need a config file for jest itself. Granted, PHPUnit also has a really spiky config file, so I'll take back the eye-roll.

You can throw your Jest config into a key in your package.json, but I prefer to create a jest.config.js since it's easier to read.

Create the file jest.config.js in your projects root directory with the following content:  

// jest.config.js
module.exports = {
    // Where are your vue tests located?
    "roots": [
        "<rootDir>/tests/Vue"
    ],
    // vue: transform vue with vue-jest to make jest understand Vue's syntax
    // js: transform js files with babel, we can now use import statements in tests
    "transform": {
        ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
        "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
    }
    // (optional) with that you can import your components like
    // "import Counter from '@/Counter.vue'"
    // (no need for a full path)
    "moduleNameMapper": {
        "^@/(.*)$": "<rootDir>/resources/js/$1"
    },
}

I would also recommend creating a setup.js file where you can later do preparations for tests.

My npm-package creates that file by default, but I will skip it here though: It's not mandatory and this article is long enough.

Ah, you are instantly 7% more cool!

It makes sense to have that file since we will need a lot later!

Just add the following lines to your jest.config.js:

"setupFilesAfterEnv": [
    "<rootDir>tests/Vue/setup.js"
],

Of course, you need to create that (empty) file in the test/Vue/ or Jest will vomit a lot of errors on your shoes.

Cool, now we can write tests?

Almost. Not quite. Now it gets ugly.

First of all note, that jest is a running on Node, and, as of now, we can't use our beloved import statements there.

All of your single file components will likely use these statements, and it would be nice to write tests the same way.

For that reason Babel.js can be used to transform our tests and components before feeding them to Jest.

This is also the reason for the transform statements in our jest.config.js file.

You guessed it: If we use Babel, we will need another config file to tell what and how we actually want to do transformations.

Oprah screaming "you get a config file, and you get one"

In babel config land

The least complicated way to do this is to simply at a .babelrc file in your repo like this:

// .babelrc
{
  "presets": ["@babel/preset-env"]
}

That will work since in normal Laravel installs, laravel-mix, the mothership Laravel asset building, uses its own internal Babel config and will ignore this file.

If you want to be really picky you still could only add the config for testing, even though it is not necessary in normal Laravel projects and looks quiet confusing:

// .babelrc
{
    "env": {
        "test": {
            "presets": [
                [
                    "@babel/preset-env"
                ]
            ]
        }
    }
}

Both configs tell babel-jest and vue-jest: "Please, just use the things in this preset of babel to make it as compatible as possible - and just make it work".

I'll leave it at that, we would actually only need a few selected plugins from the preset-env preset, but this post is nerdy enough.

Just one more thing

We are almost there I promise. The last problem we have to solve is that newer version of Jest as well as Laravel Mix use a newer version of Babel that is incompatible with vue-jest. 🤯

I'll make it short, there is another library to make them compatible called bable-bridge.

See the vue-jest docs for more information.

Install it via

npm install --save-dev babel-core@bridge

Believe it or not, now we can finally write our first test. That was... smooth sailing right?

Confusing street signs with all the things required for vue testing

The first test, better keep it simple

For the first test I would recommend you keep it simple. There are a lot of other problems that can happen even though Jest now finally knows how to handle your tests and Vue files.

Common problems are:

  • Using other (global) components in the component under test
  • Plugins
  • Global functions you added like trans() or route()
  • Ajax-Calls made by a component
  • Using a global event emitter

I plan on writing a post about mitigating some of these problems, but for this post a simple component should be enough. After all, the topic is the test setup. Just keep in mind that there are still roadblocks ahead.

In your resources/js/ folder create a Counter.vue file:

// resources/js/Counter.vue
<template>
    <div>
        <h1>Count: {{ counter }}</h1>

        <button @click="counter++">+1</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            counter: 0,
        }
    }
}
</script>

The typical useless counter component, but good to see, if our setup works.

Next step, create your test in tests/js/ as Counter.spec.js

// tests/js/Counter.spec.js
import { mount } from '@vue/test-utils'
import Counter from '../../resources/js/Counter.vue'

describe('Counter.vue', () => {
    it('increments counter', () => {
        const wrapper = mount(Counter);

        expect(wrapper.vm.counter).toBe(0);

        wrapper.find('button').trigger('click')

        expect(wrapper.vm.counter).toBe(1);
    })
})

Now you should be able to run jest via node_modules/.bin/jest and see the following output:

To make it a little simpler add a script to your package.json file

// package.json
{
    // ...
    "scripts": {
        //...        
        "test" : "jest"
        //...        
    },
    // ...
}

Believe it our not, we made it through :)

Still having problems?

In my personal projects and for the colleagues I annoyed with testing this there were no bigger problems.

If you still experience errors, here are a few options:

  • Consider using my package for the setup, there is much less room for typos
  • Install the testing packages with the compatible versions as shown in the TL;DR
  • Submit an issue/pull request to my project

Let's make vue testing more approachable together!

Wrap it up 🌯

This post shows that there is a lot involved in simply getting started with Vue.js tests in your Laravel application.

Even though there are still many roadblocks from here, I hope this post will save some people time.

So long, Cherrio 👋
Simon

In a hurry? Just do this and try your luck! 🤞


npm install -—save-dev jest vue-jest @vue/test-utils babel-core@bridge

npx vue-tests-laravel-setup

Now you can run your tests by


node_modules/.bin/jest

Happy testing :)

You can try to install the specific versions of the packages we need, these are the following:

npm i @vue/test-utils@^1.1.1 babel-core@^7.0.0-bridge.0 jest@^26.6.3 vue-jest@^3.0.7

This should work, because it only updates patch and minor version which should not contain any breaking changes, and these were the versions I used.

Keep in mind, that you will need vue and vue-template-compiler for Vue 2.

Read more posts
List image of the post How to test static pages automatically in Laravel Read more
Written 1 year ago
3 min read

How to test static pages automatically in Laravel

I developed a way to automatically crawl and test big parts of your Laravel application.

With just one line of code, you can now write a "peace of mind" test that gives you a lot of confidence.

Read more
List image of the post Your Tool is not the Problem - Your Workflow is Read more
Written 2 years ago
3 min read

Your Tool is not the Problem - Your Workflow is

A few (meta) thoughts on the search for the best project management tool.

Usually, the chaos you'll find in a software is a quite accuarate representation of your teams' communication skills - and also a big chance for improvement.

Read more
Back to all posts