Migrating AngularJS tests From Karma to Jest
Tags: angular, jest, karma, javascript
Published: 2018-04-27 15:58:24

---
title: Migrating AngularJS tests From Karma to Jest
published: true
description: A description of how 
tags: AngularJS, Jest, Karma, JavaScript 
---

<img src="https://images.unsplash.com/photo-1495312365922-8a2e01490131?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ab7d6c2691cb1fbd878cb449c07bb8a1&auto=format&fit=crop&w=1350&q=80">

# Disclaimer

This is just a write up of how we migrated our AngularJS tests to Jest, it's not a guide and there certainly are elements that I won't cover but I hope it helps somebody out there.

In this post it is assumed that you have some good knowledge in javascript unit testing configuration using karma, that you have heard or know of Jest and that NPM, Babel, Typescript are familiar to you.

This [article](https://medium.com/@leannezhang/clarifying-different-testing-tools-chai-mocha-enzyme-jest-d7a561f01857) explains what Jest and some other tools are pretty well.

# Context
At the end of 2017, we started migrating our whole front-end code base to Typescript and Webpack. In my opinion, Typescript is a godsend for large enterprise javascript code bases and we appreciate it. The old setup was getting complicated to maintain as it was custom made and didn't really follow javascript best practices.

Our front-end code is divided into two parts:
- a library of reusable components that we use internally across all applications
- the code that uses the lib and contains the application specific elements (views, custom components, services etc....)

The old setup required us to build the lib and then build the app that depended on it and a whole bunch of stuff that would give you nightmares; we even had "black magic" written in some places. If you ever come across such things in a code base, it doesn't smell good. I reckon even you agree we had to change stuff and change we did.

To get back on topic, after having setup our new build with Webpack and converted our js files to typescript; there was one last thing to do: make the tests work again !

Ah, didn't I tell you that ? Well in addition to what was previously mentioned, our tests weren't running anymore as a side effect of all the custom stuff.

The test setup was based on _Karma/Mocha/Grunt/PhantomJS_ and just didn't work anymore. My first reflex was to update the tests to make them work with karma but that proved a more daunting task than I anticipated it to be. The plugin system
of karma can be cool but this time it was being more of a hassle than anything. I tried karma-typescript (really nice lib and awesome maintainer, shout out to [@monounity](https://github.com/monounity)); at first it went well, a majority of the library tests worked and all that but when I tried to run the application tests all hell broke loose. We used namespaces for the lib and karma-typescript didn't really like it so I opened an issue that **monouty** fixed but I then ran into other problems.

In light of all these issues, I couldn't make it work on time and had to leave it alone for a while, there were other things that needed my attention unfortunately. Fast forward to April 12th 2018, I was attending a meetup with a friend that was about TDD and BDD (an article about the meetup in french but with slides in english [link](http://blog.js-republic.com/meetup-js-star-tdd/)) and they used Jest (woohoo, he's finally talking about it). I had heard of the framework and read this good [article](https://medium.zenika.com/testing-an-angularjs-app-with-jest-3029a613251) on using it for angular apps. This reminded me of my unfinished business with karma. I pitched Jest to my team and given my previous run-ins with karma, we decided to go ahead and migrate (don't know till you try) all our tests to it.

# Migration

## karma config
Here are the karma config files we used. The first one is for the app and there wasn't any attempt to make it work. The second one is for our internal library and is the one that I tried to make work.

{% gist https://gist.github.com/aktraore/dc48afedbd13c9bba62a43b82b94acd1 %}

{% gist https://gist.github.com/aktraore/75941b7fe418a6ca198097e101f51ecd %}

## Jest configuration

I started reading the official documentation (who said devs don't read the manual?) and there was a section about testing web frameworks that led to my previously mentioned article and this lifesaving piece by [@benbrandt](https://engineering.talentpair.com/@benbrandt). There aren't many articles about Jest + Angular out there and trust me you need it when doing this kind of migration.

### Typescript

We are using Typescript and Jest doesn't natively support it so we need a preprocessor to do the job. Enter [TS-Jest](https://github.com/kulshekhar/ts-jest), it does it all for you.

So we end up with a transform that looks like this.

```json
    "transform": {
      "^.+\\.ts?$": "ts-jest",
    },
```

I also had to create a separate _tsconfig_ file for _ts-jest_ because it doesn't support all the options that we use in our typescript config file. I also disabled the _TsDiagnostics_ but you shouldn't

```json
    "globals": {
      "ts-jest": {
        "tsConfigFile": "test-tsconfig.json",
        "enableTsDiagnostics": false
      }
    }
```


### Namespace

I read the articles for a bit, and started creating the configuration file for the library tests. The first problem I ran into was managing our namespace. After reading the docs for a while, I saw the _moduleNameMapper_ option and that was it, problem solved.

```json
    "moduleNameMapper": {
      "customNamespace/(.*)$": "<rootDir>/src/$1",
    },
```

### Loading html files

We use webpack for our build and load html files using webpack's _html-loader_. I needed the same functionality for the tests. A couple of google searches later I found this [stackoverflow issue](https://stackoverflow.com/questions/39483893/how-can-i-use-my-webpacks-html-loader-imports-in-jest-tests). After reading all the comments and answers, I decided to follow their advice and create a custom preprocessor for Jest (yes Jest allows you to do that).

{% gist https://gist.github.com/aktraore/7fb02263aee4024a82b5f32041e42a56 %}

All that's left is to include it in the config.

```json
    "transform": {
      "^.+\\.ts?$": "ts-jest",
      "^.+\\.html$": "<rootDir>/src-test/utils/htmlLoader.ts"
    },
```

### Making sure img tag doesn't break my tests

In some of our html templates, we directly import images and that doesn't work with Jest; you need to stub it. Searching on the internet brought me to this package [jest-static-stubs](https://www.npmjs.com/package/jest-static-stubs) that is just perfect for the job. In the _moduleNameMapper_ section of the config we just add the right line:

```json
    "moduleNameMapper": {
      "customNameSpace/(.*)$": "<rootDir>/src/$1",
      "^.+\\.(jpg|jpeg|gif|png|mp4|mkv|avi|webm|swf|wav|mid)$": "jest-static-stubs/$1"
    }
```

### Angular-mocks and global jquery

Due to how certain things work with angular ( this is better explained in mr brandt's article) we have to expose certain values (Jquery, Angular) on the global scope. In addition to that we need to import _angular-mocks_ so that Angular sets up the app prior to running the tests. This is all in the form of an init file that is later referenced in the Jest configuration.

Init file content:

{% gist https://gist.github.com/aktraore/238fe13111dbc1fd393763fdd7d712e7 %}

Referencing config in Jest config:

```json
"setupTestFrameworkScriptFile": "<rootDir>/src-test/utils/init.ts",
```

## Library config

We ended up with a configuration for Jest like this in our _package.json_:

{% gist https://gist.github.com/aktraore/dad253f618bb9ecc757592cb41c5998c %}

That was it for our library tests and we even had code coverage without adding anything else. The cherry on top for me as a vs-code fanboy is the existence of this [extension](https://github.com/jest-community/vscode-jest). The extension is pretty cool and I would recommend checking it out if you use vs-code and Jest.

Moving on to the application tests, I thought it would be a straightforward copy-paste and adapt thing.... Little did I know other issues were awaiting.


## ES6 module support

As previously stated, our code is split into two parts: a library that is an npm module and the applications that depend on it. The library is written in typescript and we compile to es6. I needed to configure Jest to correctly load es6 modules and this [issue](https://github.com/facebook/jest/issues/2081) had the answer somewhere in the thread. The solution was to use _babel-jest_ for js files (my _node_modules_ in this case) and to add a _.babelrc_ file to my project containing:

{% gist https://gist.github.com/aktraore/203c9e2c4b3d6731c69930172cf1ef6c %}

The transform part of Jest config became :

```json
    "transform": {
      "^.+\\.js?$": "babel-jest",
      "^.+\\.ts?$": "ts-jest",
      "^.+\\.html$": "<rootDir>/src-test/utils/htmlLoader.ts"
    },
```

The final Jest config is not that different from the lib one :

{% gist https://gist.github.com/aktraore/346b2ab83297074a539ea9c1bc7bc39b %}


# Comparison with Karma

The performance between Jest and Karma cannot be compared as there is no reference from the time the karma tests worked. I can tell you that Jest takes **38.425 seconds** to run **92** tests organised in **9** test suites and run coverage. We went from **13** to **4** dependencies (_jest, ts-jest, babel-jest, jest-static-stubs_) needed to run our tests. PhantomJS is not needed anymore since Jest uses JSdom; that can be seen as an advantage or a disadvantage since we are no longer testing against real browsers. I hope testing against real browsers can be an option for Jest in the futur.

# Conclusion

It wasn't easy but in my opinion it was worth it; we now have a more maintainable and modern test configuration. Testing can be fun with the right tools and I hope that we can add to our test base on a more regular basis with this setup.

A big thanks to the open source community without which this wouldn't have been half as easy. Hope this helps you.

_A big thanks to [Steven](https://medium.com/@stevenyung), [Sam](https://twitter.com/AukRaiser), [Jean-Baptiste](https://github.com/veronj) for advice and editing._

_Photo credit goes to [@weilstyle](https://unsplash.com/@weilstyle)._