dsp_099
dsp_099

Reputation: 6121

How to clean-up / reset redis-mock in an Express Jest test?

I have an app which tallies the number of visits to the url. The tallying is done in Redis. I'm using redis-mock which simulates commands like INCR in memory.

The following test visits the page 3 times and expects the response object to report current as 3:

let app = require('./app');
const supertest = require("supertest");

jest.mock('redis', () => jest.requireActual('redis-mock'));

/* Preceeded by the exact same test */

it('should report incremented value on multiple requests', (done) => {
    const COUNT = 3;
    const testRequest = function (cb) { supertest(app).get('/test').expect(200, cb) };

    async.series([
      testRequest,
      testRequest,
      testRequest
    ], (err, results) => {
      if (err) console.error(err);

      const lastResponse = _.last(results).body;
      expect(
        lastResponse.current
      ).toBe(COUNT);

      done();
    });

  });

The issue is that if I keep reusing app, the internal "redis" mock will continue getting incremented between tests.

I can side-step this a bit by doing this:

beforeEach(() => {
  app = require('./app');
  jest.resetAllMocks();
  jest.resetModules();
});

Overwriting app seems to do the trick but isn't there a way to clean-up the "internal" mocked module somehow between tests?

Upvotes: 4

Views: 2930

Answers (1)

vorillaz
vorillaz

Reputation: 6266

My guess is that somehow the '/test' endpoint gets invoked in some other tests in the suite, you could try to run specific parts of your suite using .only or even trying to run the entire suite serially.

To answer the original questions the entire suite must be isolated and consistent either if you are running a specific test case scenario or if you are trying to run the entire suite, thus you need to clear up any leftovers that they could actually affect the results.

So you can actually use the .beforeEach or the .beforeAll methods, provided by Jest in order to "mock" Redis and the .afterAll method for clearance.

A dummy implementation would look like this:

import redis from "redis";
import redis_mock from "redis-mock";
import request from "supertest";

jest.mock("redis", () => jest.requireActual("redis-mock"));

// Client to be used for manually resetting the mocked redis database
const redisClient = redis.createClient();

// Sometimes order matters, since we want to setup the mock
// and boot the app afterwards
import app from "./app";

const COUNT = 3;
const testRequest = () => supertest(app).get("/test");

describe("testing", () => {
  afterAll((done) => {
    // Reset the mock after the tests are done
    jest.clearAllMocks();
    // You can also flush the mocked database here if neeeded and close the client
    redisClient.flushall(done);
    // Alternatively, you can also delete the key as
    redisClient.del("test", done);
    redisClient.quit(done);
  });

  it("dummy test to run", () => {
    expect(true).toBe(true);
  });

  it("the actual test", async () => {
    let last;
    // Run the requests in serial
    for (let i = 0; i < COUNT - 1; i++) {
      last = await testRequest();
    }

    // assert the last one
    expect(last.status).toBe(200);
    expect(last.body.current).toBe(COUNT);
  });
});

Upvotes: 2

Related Questions