A Simple Walkthrough to Setting up GitLab Locally and a Simple Project to Sample Automated Testing with Puppeteer, Postman, and Unit Testing on GitLab — Part 2

Tay QW
12 min readJul 8, 2023

--

Photo by Domenico Loia on Unsplash

Following on from our previous post, we will now look at the Continuous Integration element of application software development. We’ll include a straightforward web application project that shows how to serve a very basic page, an API, and a function.

We’ll examine using various testing tools at various development testing stages using the web application. For unit tests, API tests, and UI tests, we’ll utilize Jest, Postman, and Puppeteer, respectively. Before creating deployment artifacts that an engineer would use to deploy in various application deployment settings, these tests may be automatically updated and regressed using GitLab’s CI/CD pipelines.

The simple web application is a very basic application that uses ExpressJS for the UI and API and NodeJS for the backend. It is not a production-ready application. In this article, it serves only as an example.

Setting up Application Project

We need a practical project to work with in order to demonstrate the capabilities of automated testing with GitLab, Puppeteer, Postman, and unit testing. We will lead you through the process of constructing a sample project that will serve as the foundation for our testing efforts in this part.

A simple user interface (UI) developed with Express.js, a lightweight and versatile web application framework, and a collection of API endpoints for backend functionality will comprise our sample project. This project will allow us to put various testing approaches and tools to the test.

This article assumes that NodeJS and NPM are already available.

  1. Create a new directory for your project and navigate to it.
mkdir test-project
cd test-project

2. Initialize a new Node.js project

npm init

3. Install Express.js as a dependency for our project

npm install express

4. Install Jest, Jest-junit, Puppeteer as dev-dependencies for our project. They will used in the later stages.

npm install --save-dev jest jest-junit puppeteer

5. Create a new file named app.js in your project directory

6. In app.js, set up a basic Express.js server by requiring the Express module and creating an instance of the Express application. Add a simple route to respond with "Hello, World!" when the root URL is accessed. We will also create a route that serves static pages.

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => res.send('Hello World!'));
app.use('/test', express.static('public'));

app.listen(port, () => console.log(`Express app running on port ${port}!`));

7. Create a new file named module1.js in your project. This module serves the purpose of demonstrating unit testing.

module.exports = function(){
return 1 + 2;
}

8. Create a new directory named public in your project.

9. Create a new file named index.html in the publicdirectory in the project. This will serve as a webpage that demonstrates simply adding the numbers in 2 inputs, var1_field & var2_field and updating the result in the input result. It also contains a simple function, myFunctionwhich reads from the 2 variable fields, adds the value and updates the result field with the result of the addition.

<!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">
<title>My Website</title>
<link rel="stylesheet" href="./style.css">
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<script>
function myFunction(){
const val = parseInt(document.getElementById("var1_field").value) + parseInt(document.getElementById("var2_field").value);
document.getElementById("result").value = val;
}
</script>
</head>
<body>
<main>
<h1>Welcome to test Website</h1>
<input name="var1_field" id="var1_field"/> + <input name="var2_field" id="var2_field"/>
<br/>
= <input name="result" id="result"/>
<br/>
<button name="submitbtn" id="submitbtn" onclick="myFunction()">Test</button>
</main>
</body>
</html>

10. Test the setup by running the command node app.js in your project directory. You should see the message "Express app running on port 3000!" in the console.

There are 2 URLs that you can access

  1. http://localhost:3000/ : Loads a page with ‘Hello World!’

2. http://localhost:3000/test : Loads the sample webpage

In the following sections, we will use this modest starting point to demonstrate the power of automated testing with GitLab, Puppeteer, Postman, and unit testing.

Unit Testing

Unit testing is vital for assuring code quality and detecting errors early in the development process. In this section, we’ll look at how to integrate unit testing into the GitLab pipeline, allowing for automatic and continuous testing of your projects.

We will be using Jest for most of our tests and thereafter, generate reports using Jest-junit. Thus, the steps will include an initial configuration for test report outputs.

  1. Create a new file named jest.config.js in your project directory.
module.exports = {
// Other Jest configuration options
reporters: [
'default',
['jest-junit', { outputDirectory: 'test-reports/junit' }],
],
};

This configuration will create the test report in XML format in the test-report/junit directory within the project directory.

2. Create a new directory named __unit_tests__ in your project directory.

3. Create a unit test script named module1.test.js in the __unit_tests__ directory.

const add = require("../module1");
describe('Unit Test', () => {
test('Test module1 ', () => {
expect(add()).toBe(3);
});
});

This test script will test the add() function in module1.js when the test is invoked. This is a very simple demonstration of unit testing and there are more in-depth discussion that is beyond this article’s scope.

4. Run the test command on the unit test script in terminal

npx jest -i __unit_tests__

Note: You can add the test command in the package.json

We shall add the test command in the package.json for later use in GitLab pipeline.

"scripts": {
"start": "node app.js",
...
"test-unit": "jest -i __unit_tests__",
...
},

API Testing

APIs (Application Programming Interfaces) are critical in modern software development because they enable communication and interaction across many systems. In this section, we will look at how to perform API testing with Postman, a popular tool for testing and verifying APIs.

This article assumes that Postman is already available.

  1. Create a new collection in Postman as Sample Tests

2. Create a new HTTP API in the Sample Tests collection in Postman

3. Set the API endpoint method and URL in Postman
API : Get
URL: localhost:3000/

4. Add tests for this API endpoint in Postman

5. Start your NodeJS web application by running npm start in the terminal of your IDE for the web application

6. Send a request to this endpoint in Postman

Successful test

The tests in the example will test for 2 conditions from the response of calling the endpoint

  • The response has a status code of 200
  • The response has a body of “Hello World!”

All behavior of the API endpoint call corresponds to our application’s API endpoint.

7. Save the HTTP API in Postman

8. Export the Postman collection as JSON file

9. Create a new directory named __api_tests__ in your web application project

10. Move the exported Postman collection JSON file into __api_tests__ of your web application project.

We will demonstrate the testing in GitLab later in this article

UI Testing

User interface (UI) testing plays a crucial role in ensuring the quality and functionality of web applications. In this section, we will explore how to set up automated UI testing using Puppeteer in the GitLab environment. Puppeteer is a powerful Node.js library that provides a high-level API for automating browser interactions.

  1. Create a new directory named __ui_tests__ in your web application project
  2. Create a new file named ui.test.js in the __ui_tests__ directory
const puppeteer = require('puppeteer');

describe('Frontend Test', () => {
let browser;
let page;

beforeAll(async () => {
browser = await puppeteer.launch({
executablePath: "/usr/bin/chromium-browser" || null,
headless: "new",
args: ['--no-sandbox', '--disable-dev-shm-usage'],
});
page = await browser.newPage();
});

afterAll(async () => {
await browser.close();
});

it('Positive test - 1+2=3', async () => {
// Navigate to your web application
await page.goto('http://localhost:3000/test');

await page.$eval('#var1_field', el => el.value = 1);
await page.$eval('#var2_field', el => el.value = 2);

// Click on the button
await page.click('#submitbtn');

const actualResult = await page.$eval("#result", (input) => {
return input.value
});

// Compare the expected and actual results
expect(actualResult).toEqual("3");
});

it('Positive test - 1+2!=1', async () => {
// Navigate to your web application
await page.goto('http://localhost:3000/test');

await page.$eval('#var1_field', el => el.value = 1);
await page.$eval('#var2_field', el => el.value = 2);

// Click on the button
await page.click('#submitbtn');

const actualResult = await page.$eval("#result", (input) => {
return input.value
});

// Compare the expected and actual results
expect(actualResult).not.toEqual("1");
})

});

Under the Frontend Test group, we have 2 tests

  • Positive test — 1+2=3
    This is a positive test where it will attempt to insert 1 into var1_fieldtext input, insert 2 into var2_fieldtext input, simulate a click on the submitbtn button and populate the result in to the resulttext input. It will then assert the test to check if the result text input has the value of 3.
  • Positive test — 1+2!=1
    This is a positive test where it will attempt to insert 1 into var1_fieldtext input, insert 1 into var2_fieldtext input, simulate a click on the submitbtn button and populate the result in to the resulttext input. It will then assert the test to check if the result text input does not have the value of 3.

Besides the test, there is pre-test initialization, beforeAllfunction, where Puppeteer will start the headless Chromium browser with a new page. There is also a post-test function, afterAll function, where Puppeteer will close the browser.

3. Start the web application

4. Run the test command on the unit test script in terminal

npx jest -i __ui_tests__
Local testing

You may automate the validation of your web application’s UI by incorporating Puppeteer-based UI testing into your GitLab process. This ensures that UI elements render correctly, user interactions work as expected, and your application provides a pleasant user experience.

As your programme changes and new features are released, update and expand your UI tests on a regular basis. Continuous UI testing allows you to detect UI issues early and maintain a high-quality standard in your online application.

In the following part, we will look at how to set up the GitLab pipeline to execute our tests.

Photo by Pankaj Patel on Unsplash

Setting up and Testing the GitLab Pipeline

In this section, we will build the GitLab pipeline that will run the various tests that we have written. It should be noted that API and UI tests will require the application to be running in order to run.

API tests will additionally require the use of a specific Docker image in order to run Postman tests.

  1. Create a file named .gitlab-ci.yml in the project directory
stages:
- test

variables:
GIT_CURL_VERBOSE: 1
GIT_TRACE: 1

unit_tests:
stage: test
image: node:20-alpine
script:
- npm install
- npm run test-unit
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml

postman_test:
stage: test
image:
name: postman/newman:alpine
entrypoint: ["/bin/sh", "-c"]
script:
- npm install -g newman-reporter-junitfull
- npm install
- npm start &
- sleep 5
- newman run __api_tests__/Sample_Tests.postman_collection.json -r junit --reporter-junit-export test-reports/junit/junit.xml
after_script:
- kill $(jobs -p) # Stop the Node.js application after tests
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml

ui_tests:
stage: test
image:
name: node:20-alpine
entrypoint: ["/bin/sh", "-c"]
script:
- apk update && apk upgrade
- apk add chromium && apk version chromium
- npm install
- npm start &
- sleep 5
- npm run test-ui
after_script:
- kill $(jobs -p) # Stop the Node.js application after tests
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml
...

There will be a test stage in the pipeline. You can add more stages for deployment rituals, which will not be covered in this article.

We then specify 3 jobs that run the various tests.

Unit Tests

unit_tests:
stage: test
image: node:20-alpine
script:
- npm install
- npm run test-unit
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml

This section specifies the unit test. Specifically, it will use node:20-alpine Docker image to run the test. The script for this job main involves running npm install to install all dependencies and npm run test-unit which essentially runs the jest -i __unit_tests__ command to run the unit tests.

The artifacts generated from this job will be using files in test-reports/junit directory. The artifact will be kept for 2 days in GitLab and GitLab will make use the test-reports/junit/junit.xml for test reporting in GitLab. This applies to all other tests.

Postman Tests

postman_test:
stage: test
image:
name: postman/newman:alpine
entrypoint: ["/bin/sh", "-c"]
script:
- npm install -g newman-reporter-junitfull
- npm install
- npm start &
- sleep 5
- newman run __api_tests__/Sample_Tests.postman_collection.json -r junit --reporter-junit-export test-reports/junit/junit.xml
after_script:
- kill $(jobs -p) # Stop the Node.js application after tests
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml

This section specifies the Postman test. Specifically, it will use postman/newman:alpine Docker image to run the test. The script for this job mainly involves

  • Running npm install -g newman-reporter-junitfull to install reporting function globally in the spawned Docker container
  • Running npm installto install all dependencies
  • Spawns a new process to run the web application with npm start &
  • Waits for 5 seconds with sleep 5
  • Runs Postman Newman command to run the Postman collection

Lastly, we will kill the process of the web application after the tests have been completed.

UI Tests

ui_tests:
stage: test
image:
name: node:20-alpine
entrypoint: ["/bin/sh", "-c"]
script:
- apk update && apk upgrade
- apk add chromium && apk version chromium
- npm install
- npm start &
- sleep 5
- npm run test-ui
after_script:
- kill $(jobs -p) # Stop the Node.js application after tests
artifacts:
paths:
- test-reports/junit/
expire_in: 2 days
reports:
junit: test-reports/junit/junit.xml

This section specifies the UI test using Puppeteer. Specifically, it will use node:20-alpine Docker image and we want to start via bash . Within the script,

  • Run apk update and apk upgrade to update the image
  • Run apk add chromium to install Chromium and apk version chromium to verify the installation
  • Running npm installto install all dependencies
  • Spawns a new process to run the web application with npm start &
  • Waits for 5 seconds with sleep 5
  • Runs the UI test with npm run test-ui

Lastly, we will kill the process of the web application after the tests have been completed.

After the pipeline code has been created, we will need to push the whole project to the GitLab Project’s code repository. Thereafter, GitLab will automatically start the pipeline jobs.

Sample of the pipeline and the associated jobs
Sample of the pipeline and the test results

As our project evolves, we must update and expand our unit tests on a regular basis. Consider including code coverage information to track the effectiveness of our tests and indicate areas that need more testing.

As our API endpoints evolve and new functionality are added, we will need to update our API tests. It is critical to examine and maintain our API tests on a regular basis in order to ensure the quality and stability of our API-driven apps.

As your application matures and new features are introduced, we will update and expand our UI testing on a regular basis. Continuous UI testing enables us to detect UI issues early and maintain a high level of quality in our online application.

Photo by Jordan Wozniak on Unsplash

Finally, we looked at how to set up GitLab, the power of Puppeteer for automated UI testing, the adaptability of Postman for API testing, and how to integrate unit testing into the GitLab process.

Automated testing is critical in modern software development because it allows us to identify errors early, assure code quality, and provide reliable products to our users. We’ve shown how to expedite our development workflow and improve the quality of our projects by leveraging tools like GitLab, Puppeteer, Postman, and unit testing.

Although there isn’t much in-depth implementation or integration, I was able to enjoy using the many tools, and I hope that my journey can help you in any way.

As we come to the end of this journey, keep in mind that automated testing is a continuing activity. Continuously update and expand your tests as your projects evolve, new features are introduced, and requirements change. Regularly evaluating and maintaining your tests is critical to preserving the quality and dependability of your product. I encourage you to continue exploring, delving further into the tools we’ve discussed, and discovering more methodologies and frameworks that meet your individual needs.

--

--

Tay QW

IT executive with a passion for making technology simpler. I'm on a mission to make IT easier to grasp for everyone. Make technology accessible to all!