Jest is a JavaScript testing framework maintained by Facebook, Inc. designed and built by Christoph Nakazawa with a focus on simplicity and support for large web applications. It works with projects using Babel, TypeScript, Node.js, React, Angular, Vue.js and Svelte. Wikipedia
In this post, I will show you how to use the jest
mocking feature to test dom elements without loading the actual dom. Before that let’s understand why mocking is important
Why mocking
Mocking is a process used in unit testing when the unit being tested has external dependencies. The purpose of mocking is to isolate and focus on the code being tested and not on the behaviour or state of external dependencies.
Let’s suppose you have the following code in javascript
const $ = require("jquery");
function showHide() {
let element = $('#test');
const top = element[0].getBoundingClientRect().top;
if (top >= 100) {
element[0].addClass("success");
} else {
element[0].removeClass("failed");
}
}
module.exports = { showHide };
You can see that in the above function we are using the JQuery
selector and also calling JQuery
function addClass
and removeClass
method of JQuery
. If you want to test the above function then either you can load the real HTML or you can use the jest
mocking feature to avoid loading the DOM (external dependencies).
In the post, I will show how to test the above function without loading the actual DOM.
const $ = require("jquery");
const { showHide, showTooltip } = require("../playground");
describe('Jquery DOM ', () => {
it('should call document.getElementById', () => {
const getBoundingClientRectSpy = jest.fn(() => ({ top: 100 }));
var myObj = [{
name: 'test',
getBoundingClientRect: getBoundingClientRectSpy,
addClass: jest.fn(),
}];
jest.spyOn($.fn, 'init').mockReturnValue(myObj);
showHide();
expect(getBoundingClientRectSpy).toBeCalled();
})
it('should call document.getElementById2', () => {
const getBoundingClientRectSpy = jest.fn(() => ({ top: 10 }));
const removeClass = jest.fn();
var myObj = [{
name: 'test',
getBoundingClientRect: getBoundingClientRectSpy,
addClass: jest.fn(),
removeClass
}];
jest.spyOn($.fn, 'init').mockReturnValue(myObj);
showHide();
expect(getBoundingClientRectSpy).toBeCalled();
expect(removeClass).toBeCalledTimes(1);
})
it('Should be mocked through fn.find()',function(){
var mockElement = {
parentNode:true,
expression:"#myId .myClass"
};
var fn_find = $.fn.find;
var spy = jest.spyOn($.fn, "find").mockImplementation((expression)=>{
if(expression===mockElement.expression){
return mockElement;
}
return fn_find(expression);
});
var foundElement = $(mockElement.expression);
expect(foundElement).toBe(mockElement);
$.fn.find = fn_find;
});
it('Should be mocked through document.getElementById',function(){
var mockElement = {
id:"someId",
parentNode:true
};
var document_getElementById = document.getElementById;
var spy = jest.spyOn(document, "getElementById").mockImplementation((id)=>{
if(id===mockElement.id){
return mockElement;
}
return document_getElementById(id);
});
var foundElement = $("#"+mockElement.id);
expect(foundElement[0]).toBe(mockElement);
document.getElementById = document_getElementById;
});
})
-
The first api call is fn.init()
-
The init() function declaration:
-
When an #id is found document.getElementById() is called to find the element.
-
When an expression is found fn.find() is used instead: