Testing using jest and puppeteer in nodejs
In this article i will explain you about testing using jest and puppeteer which you can either do in your nodejs application
Kinds of testing
-
Unit Test
-
Integration Test
-
End-to-End Testing
We will be looking on to the above testing with an example step by step using jest and puppeteer in this article
Testing tools we needed for testing
-
Test Runner: Execute your tests, summarize results eg Mocha, Jest
-
Assetion Library: Define testing logic, conditions eg Chai, Jest
-
Headless Browser: Simulates browser interaction, automated testing eg Puppeteer we will be using automated testing for end to end testing
Step1: Setup a node.js project
-
We will be setting up a node.js project and we will be adding index.html file and app.js file and styles.css and util.js to our root path
-
This command will help you in initializing your nodejs project
npm init
- Add below code to your app.js file
const { checkAndGenerate, createElement } = require("./util");
const initApp = () => {
// Initializes the app, registers the button click listener
const newUserButton = document.querySelector("#btnAddUser");
newUserButton.addEventListener("click", addUser);
};
const addUser = () => {
// Fetches the user input, creates a new HTML element based on it
// and appends the element to the DOM
const newUserNameInput = document.querySelector("input#name");
const newUserAgeInput = document.querySelector("input#age");
const outputText = checkAndGenerate(
newUserNameInput.value,
newUserAgeInput.value
);
if (!outputText) {
return;
}
const userList = document.querySelector(".user-list");
const element = createElement("li", outputText, "user-item");
userList.appendChild(element);
};
// Start the app!
initApp();
- Add below code to your util.js file
const generateText = (name, age) => {
// Returns output text
return `${name} (${age} years old)`;
};
exports.createElement = (type, text, className) => {
// Creates a new HTML element and returns it
const newElement = document.createElement(type);
newElement.classList.add(className);
newElement.textContent = text;
return newElement;
};
const validateInput = (text, notEmpty, isNumber) => {
// Validate user input with two pre-defined rules
if (!text) {
return false;
}
if (notEmpty && text.trim().length === 0) {
return false;
}
if (isNumber && +text === NaN) {
return false;
}
return true;
};
exports.checkAndGenerate = (name, age) => {
if (!validateInput(name, true, false) || !validateInput(age, false, true)) {
return false;
}
return generateText(name, age);
};
exports.generateText = generateText;
exports.validateInput = validateInput;
- Add below code to your styles.css file
body {
font-family: sans-serif;
}
.control-panel {
width: 25rem;
}
.input-container {
margin: 1rem;
}
.input-container label,
.input-container input {
display: block;
width: 100%;
}
.input-container input {
font: inherit;
}
.button {
font: inherit;
background: #521751;
color: white;
border: 1px solid #521751;
padding: 0.5rem 1rem;
border-radius: 3px;
margin: 0 1rem;
}
.user-list {
display: flex;
flex-direction: column;
list-style: none;
margin: 1rem;
padding: 0;
}
.user-item {
margin-bottom: 1rem;
padding: 1rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
width: 20rem;
}
Add below code to your index.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="styles.css" />
<title>JS Testing</title>
</head>
<body>
<section class="control-panel">
<div class="input-container">
<label for="name">Name</label>
<input type="text" id="name" />
</div>
<div class="input-container">
<label for="age">Age</label>
<input type="number" id="age" />
</div>
<button id="btnAddUser" class="button">Add User</button>
</section>
<hr />
<section class="user-output">
<ul class="user-list"></ul>
</section>
<script src="dist/main.js"></script>
</body>
</html>
Step2: Install webpack and webpack dev server
- Use below command to install webpack and modify your package.json file to serve your application with webpack
//run this command where you have created your project
npm install --save-dev webpack webpack-cli
//modify the package.json file and add these script
"scripts": {
"start": "webpack app.js --mode development --watch",
"test": "echo \"Error: no test specified\" && exit 1"
},
- Use below command to install jest
npm install --save-dev jest
Step 3: Writing and Running Unit Test
-
Create a separate file same as file which you are testing for example iam gonna test a function inside util.js file then i will create a file with the name util.test.js
-
We will be testing generatedText function inside util.js file
Inside util.test.js file update the following code
const { generateText } = require("./util");
test("should output name and age", () => {
const text = generateText("Abc", 29);
expect(text).toBe("Abc (29 years old)");
});
-
In above code we are testing generateText function and we have given the name of test to be "should output name and age" and we are calling the function inside test function and expecting the result to be "Max (29 years old)"
-
Modify the package.json file and add the script for jest testing
//modify the package.json file and add these script
"scripts": {
"start": "webpack app.js --mode development --watch",
"test": "jest --watch"
},
- Run the command and it will start the test and it will keep running and look for the change and test accordingly
npm run test
Step 4: Writing and Running Integration Test
- In our integration testing we are considering unit test as well now we have a function named checkAndGenerate inside util.js file which is calling validateinput and generateText function which were part of our unit test
Add the below code to your util.test.js file and run command npm run test to test using jest
const { generateText } = require("./util");
test("should output name and age", () => {
const text = generateText("Abc", 29);
expect(text).toBe("Abc (29 years old)");
});
test("should generate a valid text output", () => {
const text = checkAndGenerate("Abc", 29);
expect(text).toBe("Abc (29 years old)");
});
Step 5: Writing and running end to end test or user interface test using puppeteer
- Install puppeteer using below command
npm install --save-dev puppeteer
Once done just add another test case inside util.test.js file
const puppeteer = require("puppeteer");
const { generateText, checkAndGenerate } = require("./util");
test("should output name and age", () => {
const text = generateText("Abc", 29);
expect(text).toBe("Abc (29 years old)");
});
test("should generate a valid text output", () => {
const text = checkAndGenerate("Abc", 29);
expect(text).toBe("Abc (29 years old)");
});
test("should create an element with text and correct class", async () => {
const browser = await puppeteer.launch({
headless: true,
});
const page = await browser.newPage();
await page.goto("file path for your index.html file");
await page.click("input#name");
await page.type("input#name", "Anna");
await page.click("input#age");
await page.type("input#age", "28");
await page.click("#btnAddUser");
const finalText = await page.$eval(".user-item", (el) => el.textContent);
expect(finalText).toBe("Def (28 years old)");
}, 10000);
- In the above test case we are creating a browser using this code
const browser = await puppeteer.launch({
headless: true,
});
- Once browser is created we are navigating to the page that has to be displayed in the browser created by puppeteer
const page = await browser.newPage();
await page.goto("file path for your index.html file");
- Once page is rendered we are clicking on the input text and filling up those input text and finally click on submit button
await page.goto("file path for your index.html file");
await page.click("input#name");
await page.type("input#name", "Anna");
await page.click("input#age");
await page.type("input#age", "28");
await page.click("#btnAddUser");
- Once we have inputed and click on submit button we are fetching the final result and testing against our expected value
const finalText = await page.$eval(".user-item", (el) => el.textContent);
expect(finalText).toBe("Def (28 years old)");