Testing AngularJS applications with Jest. You can see the article on Medium for more information: AngularJS and Jest. Three steps to improve your legacy frontend tests.
npm install angularjs-jest --save-dev
or yarn add --dev angularjs-jest
Required libraries in your project:
angular
in version1.5.0
or higherangular-mocks
in version1.5.0
or higherjest
in version higher than23.0.1
import { createTestApp } from 'angularjs-jest';
const testApp = createTestApp({
// modules to include in test app (optional)
modules: ['some.test.module'],
// services or service components to mock in test app (optional)
mocks: {
SomeSyncService: { get: () => 42 },
SomeAsyncService: { getAsync: () => Promise.resolve('42, I promise') },
},
// additional services or service components you want to access (optional)
access: ['$http']
});
testApp.$scope.name = 'Alice';
const element = testApp.render('Hello {{ name }}');
expected(element.text()).toEqual('Hello Alice')
The render
function accepts any valid AngularJS template. For example you can use an arbitrary HTML code or the components you have defined in your application (you just need to remember to provide module names in the modules
parameter of createTestApp
).
Let's assume you have the following component defined in your app:
module.component('asyncComponent', {
template: '<strong>Answer:</strong> {{ $ctrl.answer }}',
controller(SomeAsyncService) {
this.$onInit = () => SomeAsyncService.getAsync().then((resp) => { this.answer = resp; });
}
})
You can test this component the following way:
const element = testApp.render('<async-component></async-component>');
// at first it contains no answer
expect(element.text().trim()).toEqual('Answer:');
// but then the promise resolves and the answer appears
await testApp.eventually(() => expect(element.text().trim()).toEqual('Answer: 42, I promise'));
The eventually
function returns a Promise
. This Promise
is resolved if the provided function eventually passes, and it is rejected if the function fails to pass after several calls.
By default there is no delay between the calls, however it can be configured, which is useful when some services respond with delay. In the example below the assertion will be performed at most 10 times with 200 ms delay between the calls.
await testApp.eventually(
() => expect(element.text().trim()).toEqual('Answer: 42, I promise'),
{ interval: 200, limit: 10 },
);
The library provides a snapshot serializer for HTML code generated by AngularJS. It removes some of the clutter comments generated by AngularJS and reformats the HTML to be developer-friendly. It works out of the box:
const element = testApp.render('Hello {{ name }}');
expect(element).toMatchSnapshot();
In case of an asynchronous component, when the state stabilizes after a few digest cycles, you should make an additional assertion to check if the component is ready. For example:
const element = testApp.render('<async-component></async-component>');
await testApp.eventually(() => expect(element.text()).toContain('42, I promise'));
expect(element).toMatchSnapshot();