Just-Eat spectrum-bottom spectrum-top facebook google-plus instagram linkedIn pinterest reddit rss twitter_like twitter_reply twitter_share twitter_veriviedtwitter vimeo whatsapp youtube error_filled error file info-filled info loading star tick arrow arrowLeft arrowRight close download minus-filled minus move play plus-filled plus searchIcon settings

Tag : Front-end

2115 views

Unit testing front-end JavaScript with AVA and jsdom

Writing tests for JavaScript code that interacts with the DOM can be tricky. Luckily, using a combination of AVA and jsdom, writing those tests becomes a lot easier.

This article will walk you through how to set everything up so you can get started writing your tests today.

What is AVA?

AVA is described as a “Futuristic JavaScript test runner“. Sounds fancy, huh?! So, what is it exactly that makes it “futuristic“?!

Tests run quickly

AVA runs test files in parallel, each in its own separate process, with the tests inside those files running concurrently. This offers better performance than other test runners that run tests serially, such as Mocha. This also means that each test file is run in an isolated environment — great for writing atomic tests.

Simple API

AVA’s API is very small because, in AVA’s own words, it is “highly opinionated“. You won’t find any assertion aliases here! This reduces the cognitive load required when writing tests.

Write tests in ES2015

You don’t need to do anything to be able to write tests in ES2015, AVA supports this out of the box! Under the covers it’s using Babel to transpile with the es2015 and stage-2 presets.

No implicit globals

AVA has no implicit globals, simply import it into your test file and you have everything you need.

Other benefits

There are a whole host of other benefits which AVA offers such as:

  • Promise support
  • Generator function support
  • Async function support
  • Observable support
  • Enhanced assertion messages
  • Clean stack traces

All of this combined sounds very “futuristic” to me!

Getting off the launchpad with AVA

Now that we know more about AVA, let’s create a new project and start writing some tests.

Start by running npm init inside a new project folder. This will create a package.json file, which will contain various pieces of information about the project such as its name, authors, and dependencies, among others. Hitting enter for each question will fill in a default value.

Installing AVA

Add AVA to the project by typing npm install ava --save-dev, then update the scripts section in package.json:

The --verbose flag enables the verbose reporter, which means more information is displayed when the tests are run.

When using npm scripts, the path to AVA in the node_modules folder will be resolved for us, so all we need to do is type npm test on the command line. Doing so at the moment this will give us an exception:

Let’s fix that by adding a test.

Writing a test

Create a test directory, with a file named demo.test.js inside, then add a test:

First, AVA is imported into the module, then the test function is called, passing a string as the first parameter which describes what the test is doing. The second parameter is the test implementation function which contains the body of the test, this provides us with an object, t, from which we can call the assertion functions.

The is assertion is used here, which takes two values and checks that they are both equal (using === so there is no type conversion).

Note: You can choose any name you like for the t parameter, such as assert. However, using the t convention in AVA will wrap the assertions with power-assert which provides more descriptive messages.

Run npm test and the test result will be printed out

Success! Our test passed as expected. To see an example of what a failing test would look like change the test assertion to t.is(1 + 1, 1). Run the test now and you’ll see an error

As you can see, there is a lot of useful information provided in order to help us track down the issue.

Testing modules

To demonstrate how to test a module, create a new folder called src in the root of the project with a file inside called demo-module.js with the contents:

Update demo.test.js by first importing the module, then adding a new test:

Running npm test now will give you the following exception

Uh oh, what happened?

AVA will transpile ES2015 code in your tests; however, it won’t transpile code in modules imported from outside those tests. This is so that AVA has zero impact on your production environment.

If our source modules are written in ES2015, how do we tell AVA that we’d like them to be transpiled too?

Transpiling source files

To transpile source files, the quick and dirty option is to tell AVA to load babel-register which will automatically transpile the source files on the fly. This is ok if you have a small number of test files, but there is a performance cost which comes with loading babel-register in every forked process.

The other option is to transpile your sources before running the tests in order to improve performance.

The next two sections look at how each technique can be achieved.

Transpile with babel-register

Add babel-register by running npm install babel-register --save-dev, then add a "babel" config to package.json

Next, add "babel-register" to the AVA "require" section

Run npm test and the tests will once again pass, great!

The recommendation from the AVA team is to use babel-registeruntil the performance penalty becomes too great“. As your test base grows you’ll need to look into setting up a precompilation step.

Setting up a precompilation step

A precompilation step will transpile your source modules before the tests are run in order to improve performance. Let’s look at one way to set this up.

Note: If you were following along with the last section you’ll need to remove the references to babel-register. First run npm uninstall babel-register --save-dev, then remove "babel-register" from the AVA "require" section in package.json.

Start by adding the babel-cli and babel-preset-es2015 packages to the project: npm install babel-cli babel-preset-es2015 --save-dev.

Next, add a "babel" config to package.json

In order to run the tests, we need to update the npm scripts. Add a new npm script called precompile

The precompile npm script will tell Babel to take the files in the src directory, transpile them, then output the results to the dist directory.

Next, the test npm script needs to be updated so that it runs the precompile step before running the tests

The double ampersand (&&) tells npm to first run the precompile script and then the AVA tests.

The final task is to update the reference to demo-module inside demo.test.js to point at the compiled code, we do this by replacing ../src with ../dist:

Run npm test and we’re presented with all green tests!

Testing the DOM using Node

So far we have the ability to test JavaScript code, but what if we’d like to test a function which makes use of the DOM? Node doesn’t have a DOM tree, so how do we get around this?

One option is to use a combination of a test runner and a browser — a popular combination is Karma and PhantomJS. These offer a lot of benefits like being able to test against real browsers, run UI tests, take screenshots, and the ability to be run as part of a CI process.

However, they typically come with a fairly large overhead, so running lots of small tests can take minutes at a time. Wouldn’t it be great if there was a JavaScript implementation of the DOM?

Welcome to the stage; jsdom!

jsdom

jsdom is described as “A JavaScript implementation of the WHATWG DOM and HTML standards, for use with Node.js“.

It supports the DOM, HTML, canvas, and many other web platform APIs, making it ideal for our requirements.

Because it’s purely JavaScript, jsdom has very little overhead when creating a new document instance which means that tests run quickly.

There is a downside to using a JavaScript implementation over an actual browser – you are putting your trust in the standards being implemented and tested correctly, and any inconsistencies between browsers will not be detected. This is a deal breaker for some, but for the purposes of unit testing I think it is a reasonable risk to take; jsdom has been around since early 2010, is actively maintained, and thoroughly tested. If you are looking to write UI tests then a combination of something like Karma and PhantomJS may be a better fit for you.

Integrating jsdom

Setting up jsdom can be a daunting task, the documentation is great, but very lengthy and goes into a lot of detail (you should still read it!). Luckily a package called browser-env can help us out.

Add browser-env to the project npm install browser-env --save-dev.

Create a helpers directory (which is ignored by convention when using AVA) inside test, then add setup-browser-env.js with the contents

We need to tell AVA to require this module before any of the tests are run so that browser-env can create the full browser environment before any DOM references are encountered. Inside your package.json add

Note: You may have noticed that this file is written in ES5. This is because AVA will transpile ES2015 code in the tests, yet it won’t transpile any modules imported or, in this case, required from outside the tests — see the transpiling source files section.

Testing the DOM

Let’s write a test which makes use of the document global which has been provided thanks to jsdom. Add a new test to the end of demo.test.js:

First, we add a paragraph element with some text to the document body, then query for that element using document.querySelector, and finally, we verify that the selected paragraph tag has an innerHTML value equal to 'Hello, world'.

Run the tests with npm test

Congratulations, you’ve just unit-tested the (virtual) DOM!

Test coverage with nyc

As a bonus let’s quickly set up some test coverage. Because AVA runs each test file in a separate Node.js process, we need a code coverage tool which supports this. nyc ticks the box — it’s basically istanbul with support for subprocesses.

Add it to the project with npm install nyc --save-dev, then update the test npm script by adding nyc before the call to ava:

You’ll also need to update the Babel config to tell it to include source maps when developing so that the reporter can output the correct lines for the transpiled code:

Run the tests and witness the awesome code coverage table!

What next?

If you’re interested in what else you can do with AVA, have a look through the AVA readme, check out the AVA recipe docs, read about common pitfalls, and listen to this JavaScript Air podcast episode. I’d also recommend looking into setting up linting for your code.

You can browse the source code for this blog post on GitHub.

So, now you have no excuse for not testing your front-end JavaScript!


Damian Mullins is a UI Engineer at Just Eat. Progressive enhancement advocate, web standards supporter, JavaScript enthusiast.

684 views

Aligning your Front End Process

Developing with front-end code is highly deceptive. From a distance it seems straightforward; HTML, CSS and JavaScript are easy languages right?

Not so much.

As a project scales, the structure of your front-end code becomes essential. How you choose to do things when your website has just a few pages and one developer working on it can quickly come back to bite you if you neglect to think about the future.

At JUST EAT we have a number of developers working on the front-end of any one of our products. As with any project, ensuring that those developers are working together, and not against each other, is of massive importance.

I want to take you through some of the things that I’ve found help to keep our projects and developers aligned and our codebase more structured. Although these solutions have come about because of working with larger scale projects, they are equally useful on smaller projects. In essence, they are all about encouraging good habits and practices.

Consistent Workflow

Front-end workflow tools such as Gulp and Grunt have become extremely popular over the last couple of years. They put in place a set of tasks that can then be carried out by any developer working with the project.

Each task is comprised of a set of operations to run; this can include things like code linting, CSS preprocessing, compressing code and images and running JavaScript tests you have written.

The biggest benefit of using any workflow tool is that it gives all of the developers working on the project a consistent way of carrying out these tasks. The configuration files are a part of your base project and so, once the tasks are setup, a newcomer to the project will be able to run these almost immediately.

These tasks can also be run as part of a projects build process to ensure that uncompressed code and images are never deployed. Doing this means you aren’t reliant on developers having to remember to carry out tasks required to make your website production ready – they are done automatically in your release build.

If you take away only one thing from this article, it should be the importance of spending some effort on your projects workflow. The benefits vastly outweigh any potential time you will spend initially setting it up.

Modularise

One of the hardest things to do when developing the front-end of a website or application is to step back from thinking about the context you are developing for and build code for any scenario.

Taking CSS as an example, it’s easy to name things with the page we are building for, such as .searchResults-item or .homepageCarousel. These classnames are useless in terms of reusing those styles; what happens if I later want to use the same styles that the .searchResults-item element has, but to a list of user comments instead? It would make much more sense to use classnames that can be used in any context.

Try to build all of your code in components. Across teams, this means that you will save time as you reuse classes and styles when building new pages; Some components may be able to be used with very little modification, others may be able to be adapted and modified slightly.

The same goes for JavaScript. Ensure modules that you write can be potentially reused in any part of your site, or on any future projects. Well written JavaScript modules can be used on any project, not just the one you are working on right now.

Thinking in components isn’t an easy habit to get into, but as you get better at doing so, your front-end code will become much easier to work with, while having the additional benefit of being much more future-proof.

Stay on the same page

It’s a widely held saying that a good codebase should look like it has been written by one developer, irrespective of how many people are working on it.

This can be a challenge; every developer likes to write code in slightly different ways and so this involves encouraging change for the benefit of the team and project.

In my experience, good developers will embrace code consistency within a team over any personal preferences they may hold. Ultimately, there’s no point arguing over the use of spaces vs tabs. If you have a preference for a certain type of formatting, then try to make sure you can give reasons beyond ‘because I like doing it that way’.

The most important thing is to make these choices as a team and stick to them. Using a linter – such as Stylelint for CSS or ESLint for JavaScript – will ensure that the formatting you decide on is adhered to.

Using linters for code formatting can seem over the top and un-needed to some. I find them to be quite the opposite. They act as your first line of defence in code quality, stopping any badly formatted code hitting the codebase. They also help to ensure code reviews focus on the way code has been written rather than getting bogged down trying to spot formatting inconsistencies. This is tiresome both for those doing the reviewing and those whose code is being reviewed.

CSS Naming Schemes are also very valuable in terms of project consistency. BEM and Kickoff are naming schemes that help promote modularity, but also put in place rules over how classnames are defined.

When a naming scheme is being used, classnames become more self documenting and it becomes easier to see how classnames relate to one another.

Ultimately, the more you can do to promote consistency across your team, the easier your codebase will be to maintain and work with.

Code Commenting and Documentation

As with anything important, you truly realise how good something is when it’s not there and this is especially true of code comments and documentation.

First up, if you aren’t commenting your code, you need to start. This doesn’t just apply to those working in a team; code you write today you will inevitably return to in several weeks or months. It’s always good to have an insight into what you were thinking when you first wrote that code.

Commenting doesn’t need to be some elaborate process – it’s just about explaining the basics. If you’re writing a function in JavaScript, say what it does, even if you think it’s obvious. Writing a utility class or an element modifier in CSS? Then explain how it will affect an element it is applied to.

Code commenting becomes even more important when working in a team. For example, at JUST EAT, it’s impossible for me to see every piece of code that goes into the project I work on. So when I come to modifying an area of the site I’ve never used, it is a massive help if the person who wrote that code commented it.

At the very least, I try to write a comment for every CSS component that I write, or for every function in JavaScript.

Documentation is more involved but is equally useful. Spending time to document how things have been done in your project is useful not just to other developers working on the project but also to you.

At JUST EAT, we maintain a Component Library which contains every CSS component class available in our project. If another developer comes to work on a new feature, they can take a look through this documentation to see if something can be reused first. Without this, every developer would need to know every front-end component in the codebase – an unrealistic goal for any mid-size project that changes over time.

If you have a number of developers in your team, try to spend some time building up documentation relevant to your project – or projects – that you work on. They can be valuable as a reference point for all the members of a team, and especially so for new hires.

In summary

Trying to keep a consistent codebase which is being worked on by a number of developers isn’t easy, especially as new members join and your team grows. The key is to put the right processes in place that will gain you and your team the most value over time.

Look at what causes you the most headaches – is there confusion over how to do simple things like compiling preprocessor code or image compressing? If so, spend time on your workflow tooling. Is your CSS hard to manage because the structure isn’t clear or there is too much inconsistency? Look at how you can modularise your CSS and tools that can help ensure your team writes CSS in a more consistent manner.

Putting the effort in to review your workflow and team processes will pay back handsomely over time.

Web