Testing is a crucial part of software development, ensuring that your code works as expected and remains reliable over time. When working with Express.js, a popular Node.js framework for building web applications, testing can become complex due to dependencies on external modules and services. Sinon.js is a powerful library that helps developers create mocks and stubs to isolate parts of their application during testing. This article provides a comprehensive guide to mocking and stubbing in Express.js tests using Sinon.js.

Understanding Mocks and Stubs

Before diving into implementation, it’s important to understand the difference between mocks and stubs. Both are types of test doubles used to replace real objects in tests, but they serve different purposes.

What is a Stub?

A stub provides predefined responses to method calls made during the test. It does not have any logic or expectations; its sole purpose is to return specific data when invoked. Stubs are useful for controlling the environment and isolating the unit under test.

What is a Mock?

A mock not only provides predefined responses but also verifies that certain interactions happen during the test. Mocks are used to assert that specific functions are called with expected arguments, ensuring the correct behavior of the code.

Setting Up Sinon.js in Your Tests

To get started, install Sinon.js using npm:

npm install sinon --save-dev

Mocking and Stubbing in Practice

Consider an Express.js route handler that relies on a database service to fetch user data. During testing, we want to mock this database service to avoid actual database calls.

Example: Stubbing a Service Method

Suppose we have a service method getUserById(id). We can stub this method to return a fixed user object.

const sinon = require('sinon');
const userService = require('./userService');

describe('GET /user/:id', () => {
  it('should return user data', () => {
    const stub = sinon.stub(userService, 'getUserById').resolves({ id: 1, name: 'Alice' });
    // Your test code here
    // Remember to restore the stub after test
    stub.restore();
  });
});

Example: Mocking a Function Call

Mocks can verify that functions are called with specific arguments. For example, mocking a response object's send method:

const sinon = require('sinon');

it('should call res.send with user data', () => {
  const res = {
    send: sinon.spy()
  };
  const req = { params: { id: 1 } };

  // Call your route handler here
  yourRouteHandler(req, res);

  sinon.assert.calledWith(res.send, sinon.match({ id: 1, name: 'Alice' }));
});

Best Practices for Using Sinon.js

  • Always restore stubs and mocks after each test to prevent side effects.
  • Use sinon.stub() for replacing functions and methods.
  • Use sinon.mock() when you need to set expectations on function calls.
  • Combine mocks and stubs to both control behavior and verify interactions.
  • Leverage Sinon’s fake timers for testing code that relies on timeouts or intervals.

Conclusion

Mocking and stubbing are essential techniques for effective testing in Express.js applications. Sinon.js provides a flexible and powerful toolkit to create test doubles, enabling developers to write reliable and maintainable tests. By understanding the differences and best practices, you can improve your testing strategy and ensure your application behaves correctly under various scenarios.