DEV Community

Testing Node.js Applications with Real-world Examples

Testing is a crucial part of software development. It ensures that your application works as expected and helps to catch bugs early.

This guide will explore how to test different types of Node.js applications, including a simple web server, a REST API, and a command-line application. We'll use real-world code samples and best practices to illustrate the concepts.

Prerequisites

To follow along with this guide, you should have Node.js and npm installed. Additionally, you'll need to install Jest for testing:

npm install --save-dev jest
Enter fullscreen mode Exit fullscreen mode

Testing a Simple Web Server

First, let's start with testing a simple web server using the http module.

Setting up the Web Server

Create a file named server.js:

// server.js
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello, world!');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

module.exports = server;
Enter fullscreen mode Exit fullscreen mode

This is a simple HTTP server that responds with "Hello, world!" to GET requests to the root URL.

Writing Tests for the Web Server

Now, let's write some tests for this server. Create a file named server.test.js:

// server.test.js
const request = require('supertest');
const server = require('./server');

describe('Simple Web Server', () => {
  afterAll(() => {
    server.close();
  });

  it('should respond with "Hello, world!"', async () => {
    const response = await request(server).get('/');
    expect(response.status).toBe(200);
    expect(response.text).toBe('Hello, world!');
  });

  it('should respond with 404 for other routes', async () => {
    const response = await request(server).get('/nonexistent');
    expect(response.status).toBe(404);
    expect(response.text).toBe('Not Found');
  });
});
Enter fullscreen mode Exit fullscreen mode

In this test file, we use supertest to make requests to our server and verify the responses.

Testing a REST API

Next, let's test a REST API built with Express.

Setting up the REST API

Create a file named app.js:

// app.js
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

let items = [];

app.get('/items', (req, res) => {
  res.status(200).json(items);
});

app.post('/items', (req, res) => {
  const newItem = req.body;
  items.push(newItem);
  res.status(201).json(newItem);
});

app.delete('/items/:id', (req, res) => {
  const { id } = req.params;
  items = items.filter(item => item.id !== parseInt(id, 10));
  res.status(204).send();
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

This is a simple REST API with endpoints to get, create, and delete items.

Writing Tests for the REST API

Create a file named app.test.js:

// app.test.js
const request = require('supertest');
const app = require('./app');

describe('REST API', () => {
  let newItem;

  it('should create a new item', async () => {
    newItem = { id: 1, name: 'Test Item' };
    const response = await request(app).post('/items').send(newItem);
    expect(response.status).toBe(201);
    expect(response.body).toEqual(newItem);
  });

  it('should get all items', async () => {
    const response = await request(app).get('/items');
    expect(response.status).toBe(200);
    expect(response.body).toEqual([newItem]);
  });

  it('should delete an item', async () => {
    const response = await request(app).delete(`/items/${newItem.id}`);
    expect(response.status).toBe(204);

    const getResponse = await request(app).get('/items');
    expect(getResponse.status).toBe(200);
    expect(getResponse.body).toEqual([]);
  });
});
Enter fullscreen mode Exit fullscreen mode

In this test file, we test the creation, retrieval, and deletion of items in the REST API.

Testing a Command-line Application

Finally, let's test a simple command-line application.

Setting up the Command-line Application

Create a file named cli.js:

// cli.js
const fs = require('fs');

const filePath = './data.txt';

function readData() {
  return fs.readFileSync(filePath, 'utf-8');
}

function writeData(data) {
  fs.writeFileSync(filePath, data);
}

function clearData() {
  fs.unlinkSync(filePath);
}

module.exports = { readData, writeData, clearData };

if (require.main === module) {
  const command = process.argv[2];
  const data = process.argv[3];

  if (command === 'read') {
    console.log(readData());
  } else if (command === 'write') {
    writeData(data);
    console.log('Data written successfully');
  } else if (command === 'clear') {
    clearData();
    console.log('Data cleared successfully');
  } else {
    console.log('Unknown command');
  }
}
Enter fullscreen mode Exit fullscreen mode

This command-line application reads, writes, and clears data in a text file.

Writing Tests for the Command-line Application

Create a file named cli.test.js:

// cli.test.js
const fs = require('fs');
const { readData, writeData, clearData } = require('./cli');

describe('Command-line Application', () => {
  const filePath = './data.txt';

  afterEach(() => {
    if (fs.existsSync(filePath)) {
      fs.unlinkSync(filePath);
    }
  });

  it('should write data to a file', () => {
    const data = 'Test data';
    writeData(data);
    const fileContent = fs.readFileSync(filePath, 'utf-8');
    expect(fileContent).toBe(data);
  });

  it('should read data from a file', () => {
    const data = 'Test data';
    fs.writeFileSync(filePath, data);
    const fileContent = readData();
    expect(fileContent).toBe(data);
  });

  it('should clear data from a file', () => {
    const data = 'Test data';
    fs.writeFileSync(filePath, data);
    clearData();
    expect(fs.existsSync(filePath)).toBe(false);
  });
});
Enter fullscreen mode Exit fullscreen mode

In this test file, we test the read, write, and clear functions of the command-line application.

Conclusion

Testing is a vital part of developing robust Node.js applications. In this guide, we covered:

  • Testing a Simple Web Server: Using the http module and supertest.
  • Testing a REST API: Using Express and supertest.
  • Testing a Command-line Application: Using the fs module for file operations.

By following these examples and best practices, you can ensure your Node.js applications are thoroughly tested and reliable.

Happy testing!

Top comments (0)