Unit Testing
Aspects of Front-End Testing
- UI events
- HTML rendering
- Client - Server communication
- Client side computations
Tools you'll need
- Unit Testing Framework
- Jest, Mocha, Jasmine
- Mock framework (Optional)
- Sinon
- Test runner (Optional)
- Karma
Setting up Jasmine
<!DOCTYPE html>
<html lang="en">
<head>
<title>Testing with Jasmine</title>
<!-- include jasmine -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.min.js"></script>
<!-- include jquery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!-- jasmine-jquery -->
<script src="./jasmine-jquery.js"></script>
</head>
<body>
<!-- include the file you want to test -->
<script src="./index.js"></script>
<!-- include the file in which you write your tests -->
<script src="./smile.spec.js"></script>
</body>
</html>
Setting up Jasmine
* Note: If you want access to your html in jasmine,
put this at the top of your test js file:
// tell jasmine-jquery where to load html from
jasmine.getFixtures().fixturesPath = './';
// load html file
loadFixtures("index.html");
Basic Structure of Jasmine Tests
describe("I'm a set of related tests", function() {
it("should do A", function () {
...
})
it("should do B", function () {
....
})
})
* Use `describe()` to create a set of tests(spec)
* Use `it()` to write a single unit test
Test #1: createSmilePost()
* createSmilePost takes in data for a smile post:
* Returns an html element with the new smile post
{
id: 9,
rating: 9,
date: "the data of the post",
title: "title goes here...",
content: "content of the post goes here...",
likes: 2
}
....
// smile data -> smile html element
const smileMarkup = ({ id, rating, date, title, content, likes }) => `
<div data-id="d-${id}" class="smile-post">
<div class="rating">
${rating}
</div>
<div class="main-content">
<h3 class="title">
${title}
</h3>
<p>
${content}
</p>
</div>
<div class="like-container">
<button class="like"> </button>
<div>${likes}</div>
</div>
<div class="date-container">
<div class="date">
${date}
</div>
</div>
</div>
`;
const createSmilePost = (smileData) => strToHtml(smileMarkup(smileData))
...
Test #1: createSmilePost()
describe("testing createSmilePost", () => {
it("sets data-id properly", () => {
const smile = {
id: 1,
rating: 3,
date: "date",
title: "Smile Please",
likes: 2,
content: `content`
};
const smilePost = createSmilePost(smile);
expect(smilePost.dataset.id).toBe(`d-${1}`);
});
});
Test #1: createSmilePost()
Test #2: increment likes on click
* Lets test an event handler this time
* `renderPage()`
* takes:
* an html element
* the entire smile data(an array of smile objects)
* renders smile posts into the provided html element
* adds event handlers to handle "liking" of a smile post
* Similar to RateProfessor.start()
Test #2: increment likes on click
const renderPage = (root, data) => {
// create smiles
root.appendChild(createSmiles(data));
// add event handlers
root.querySelectorAll(".smile-post").forEach(smile => {
const likeBtn = getLikeButtonFromSmilePost(smile);
likeBtn.addEventListener("click", () => {
const likes = getLikeCountDiv(smile);
const count = +likes.innerText;
likes.innerText = count + 1;
});
});
};
describe("testing smile post likes onclick", () => {
it("increments # likes on click", () => {
// data for all smiles. We have only one smile post for our test
const smiles = [{
id: 1,
rating: 3,
date: "date",
title: "Smile Please",
likes: 2,
content: `content`
}];
// create an html element for renderPage()
const renderTarget = document.createElement("div");
// call renderPage with our smiles data
renderPage(renderTarget, smiles);
// get all dom elements we need
const smilePost = $(renderRoot).find(".smile-post[data-id='d-1']");
const likeDiv = smilePost.find(".like-container>div");
const likeBtn = smilePost.find(".like-container>button");
// before click check if the like counter has the correct value
expect(+likeDiv.text()).toBe(2);
// trigger a click on the like button
likeBtn.trigger('click');
// check if the like counter got incremented
expect(+likeDiv.text()).toBe(3);
});
});
Test #2: increment on click
Open up your tests.html to check your tests
Some guidelines for unit testing
* One feature per test
* Keep tests simple
* Keep tests detereministic
Resources
* Jasmine
* jasmine-jquery
https://github.com/velesin/jasmine-jquery
* For React/Vue:
* jest
* enzyme
Unit Testing
By Devjeet Roy
Unit Testing
Basics of front-end unit testing in Javascript
- 630