Build a Real-time Chat Application with Socket.io and Node.js [With Automated Testing]
Tags: node, devops, beginners, tutorial
Published: 2023-04-17 05:18:26

In this tutorial, let's learn how to use Socket.IO and Node.js and create a simple chat application. We will set up a basic Node.js app and install the necessary dependencies. With a basic chat interface and add a simple feature where users can easily send and receive messages with the help of the Socket.IO connection. Socket.io is a widely used library that helps with real-time communication between web clients and servers. Finally, we will write a simple test with the Mocha framework and automate it through Harness CI. 

## Prerequisites
- [Node.js](https://nodejs.org/en/download) installed
- [Npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed
- Free [Harness CI](https://app.harness.io/auth/#/signup/?module=ci&utm_source=internal&utm_medium=social&utm_campaign=community&utm_content=pavan_chat_tutorial&utm_term=get-started) account to automate testing

## Tutorial
- Install Node.js on your machine: You can download and install the latest version of Node.js from the official website: https://nodejs.org/

- Create a new Node.js project: Open a terminal window and create a new directory for your project. Navigate into the new directory and run the following command to create a new package.json file for your project:

```
npm init -y
```
- Install Socket.io: Run the following command to install Socket.io in your project:
```
npm install socket.io
```
- Create a new Node.js server: Create a new file named `server.js` in your project directory and add the following code:

```
const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);

app.use(express.static(__dirname + '/public'));

io.on('connection', (socket) => {
  console.log('A user connected');
  
  socket.on('disconnect', () => {
    console.log('A user disconnected');
  });
  
  socket.on('message', (message) => {
    console.log('Received message:', message);
    io.emit('message', message);
  });
});

http.listen(3001, () => {
  console.log('Server started on port 3001');
});
```
This code adds an event listener for the message event, which is emitted by the client when the user clicks the send button. The server logs the message to the console and then emits a message event to all connected clients using the io.emit() method.

- Create a new HTML file: Create a new file named `index.html` in the public directory and add the following code. It adds an input field and a button to the index.html file to allow users to send messages to the server. Here's an example:


```
<!DOCTYPE html>
<html>
  <head>
    <title>Socket.io Tutorial</title>
  </head>
  <body>
    <h1>Socket.io Tutorial</h1>
    <div id="messages"></div>
    <input id="message-input" type="text" placeholder="Type your message here">
    <button id="send-button">Send</button>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io();
      socket.on('connect', () => {
        console.log('Connected to server');
      });
      socket.on('disconnect', () => {
        console.log('Disconnected from server');
      });
      const messages = document.getElementById('messages');
      const messageInput = document.getElementById('message-input');
      const sendButton = document.getElementById('send-button');
      sendButton.addEventListener('click', () => {
        const message = messageInput.value;
        socket.emit('message', message);
        messageInput.value = '';
      });
      socket.on('message', (message) => {
        const messageElement = document.createElement('div');
        messageElement.innerText = message;
        messages.appendChild(messageElement);
      });
    </script>
  </body>
</html>
```

- Start the server: Run the following command in your terminal to start the server:
```
node server.js
```
You should see the output on your terminal below,
`Server started on port 3001`

And when you open the 3001 on your local, you should see your chat app running.
![socket app running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ir7opgjny08w79ldvp3.png)

This should be your application folder structure as of now.

`socketio-nodejs-tutorial/
  |- node_modules/
  |- public/
  |   |- index.html
  |- server.js
  |- package.json`

## Add a Test Case to the Chat Application
Since Socket.io is a real-time framework, it can be a bit challenging to write automated tests for it. However, we can write a simple test to verify that the client and server can communicate with each other and send/receive messages.

Install the mocha testing framework and the chai assertion library by running the following command:

```
npm install mocha chai --save-dev
```
[Note: Run the above command in the root of your app]

Make a new folder named `test` and add a file named `test.js` in it with the following code.

```
const assert = require('chai').assert;
const io = require('socket.io-client');
const serverUrl = 'http://localhost:3000';

describe('Socket.io test', function() {
  let client1, client2;

  before(function(done) {
    // Connect to the server
    client1 = io.connect(serverUrl);
    client2 = io.connect(serverUrl);
    done();
  });

  after(function(done) {
    // Disconnect from the server
    client1.disconnect();
    client2.disconnect();
    done();
  });

  it('Should be able to send and receive messages', function(done) {
    client1.emit('message', 'Hello, world!');
    client2.on('message', function(msg) {
      assert.equal(msg, 'Hello, world!');
      done();
    });
  });
});

```
Now make sure your app is running. 

In another terminal open the application and run the following command to test the application.

```
npm test
```
You should see a successful test passing message.
![tests passed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swr950j3lrgzpmhl8zf0.png)

Push your application code to a source control management system such as GitHub.

My application code is here - [chat-app-tutorial](https://github.com/pavanbelagatti/chat-app-tutorial.git)

You can fork it or use your own (the one you pushed to your GitHub) to setup automated tests using a CI tool. We are going to extend this tutorial to show you how you can automate tests. 

## Automate Tests using a CI tool
Here, we will be using the fastest CI tool, Harness.

Sign in to your free Harness CI module. 

Get started with creating your first pipeline
![first pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6rgri1gpcbmbt8q2jx1.png)

Authenticate with your GitHub account as your application code is present there.
![add github](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcdkkrm132wok2r37his.png)

![authenticate with GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1efbg84t05szpe9lh1c.png)

Select your application repository. 
![node repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b36t56621avcu6pwhrt.png)

Next, start configuring the pipeline.
![configure pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64cynm2yovq0jwitwfen.png)

Select Node.js from the list as our application is written in Node.js. What you will then see is a default yaml configuration for your Node.js application.
![default yaml](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xshado7pmedhdtri99yc.png)

Continue and click the 'Create the pipeline' button. You will the pipeline dashboard.
![create node](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trjamnuhplkzmru8biq0.png)

Click on the 'Build Node App' stage, you should see the run step added by default with pre-configurations.
![nodeapp step](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6couf7gihkcktphkr49.png)

Click the 'Build Node App' step, that is under execution and you will see the default configurations.
![default configurations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0w7gxt5cmx2wks1eyoi3.png)

We want the application to be running before testing it. How to make the application run? We will add a background step to run the server.
![background step](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nkzfdy9qwx83wbff1kib.png)

Configure the Background step with the commands shown below.
```
npm install
npm install express --save-dev
node server.js
```
The screenshot of the same step is shown below
![step with commands](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d5mtddtc4vertfdchlx1.png)

Apply changes and add another step to test the application.
![test the application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5wc5tqvb8l9gyb3ipj0.png)

Choose the Run step from the step library.
![run step added](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ly9el9u5pmea4u1il0th.png)

Add the following commands to the run step.

```
npm install
npm test
```
You can see the screenshot of the run step with commands added. 
![run step commands added](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsotq0g32pt74bvt9l7w.png)

Apply changes and save the pipeline.

At this point, this is how your pipeline should look like.
![pipeline looks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d3jpd8opx9mawbso7kbt.png)

Save everything and run the pipeline.
![run pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7cqtp6zb7ulml32lj6q.png)

You should see a successful pipeline execution with test passing.
![successful pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfv4l09k19wryrtqtcbc.png)

You have the pull and push triggers already enabled by default.
![triggers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtp3sabw7mkn7my1jyw0.png) 

So when any developer pushes code to your main branch, the pipeline gets triggered and runs the test. This way the tests can be automated easily. 

If you like to learn CI/CD from scratch, I have another tutorial you might like. Take a look.
{% embed https://dev.to/pavanbelagatti/learn-how-to-setup-a-cicd-pipeline-from-scratch-for-a-go-application-4m69 %}