4

I'm trying to figure out a way to mock redis in this module:

const Redis  = require('ioredis');
  const myFunction = {
    exists: (thingToCheck) {
      let redis_client = new Redis(
        6379,
        process.env.REDIS_URL,
        {
          connectTimeout: 75,
          dropBufferSupport: true,
          retryStrategy: functionHere
        });

    redis_client.exists(thingToCheck, function (err, resp) {
     // handlings in here
    });
  }
};

Using this test-code:

const LambdaTester = require('lambda-tester');
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const mockRedis = sinon.mock(require('ioredis'));

describe( 'A Redis Connection error', function() {
    before(() => {
        mockRedis.expects('constructor').returns({
            exists: (sha, callback) => {
                callback('error!', null);
            }
        });
      });

      it( 'It returns a database error', function() {
          return LambdaTester(lambdaToTest)
              .event(thingToCheck)
              .expectError((err) => {
                   expect(err.message).to.equal('Database error');
              });
      });
});

I also tried a few variations, but I'm kind of stuck as I basically need to mock the constructor and I'm not sure Sinon supports this?

mockRedis.expects('exists').returns(
  (thing, callback) => {
    callback('error!', null);
  }
);
sinon.stub(mockRedis, 'constructor').callsFake(() => console.log('test!'));
sinon.stub(mockRedis, 'exists').callsFake(() => console.log('test!'));

Not sure what else to try here, I also tried using rewire as suggested here, but using mockRedis.__set__("exists", myMock); never set that private variable.

I want to fake my error paths ultimately.
I would love to hear what others are doing to test redis in node js .

Morten Siebuhr
  • 6,068
  • 4
  • 31
  • 43
cameck
  • 2,058
  • 20
  • 32
  • 1
    Maybe you can have a look on unit test of ioredis, https://github.com/luin/ioredis/blob/master/test/helpers/mock_server.js , there is how ioredis build a fake server. – Yu Huang Jun 02 '17 at 18:12
  • Cool thanks! I ended up just creating a local redis server with Docker and killing it for my error testing, but when I have more time I want to build a better mock. – cameck Jun 02 '17 at 18:35
  • But the above does work great, just would rather not have to have a docker dependency to run tests – cameck Jun 02 '17 at 18:36
  • Yes, Docker is really suitable for building testing database. In my case, I usually connect the app to redis cluster in docker, but I stub all redis operations. Thus the redis is always empty, but it saves lots of efforts. – Yu Huang Jun 03 '17 at 02:02

1 Answers1

1

Your problem is not whether Sinon supports this or that, but rather a missing understanding of how "classes" work in Ecmascript, as shown by the attempt at stubbing constructor property in the test code. That will never work, as that property has nothing to do with how any resulting objects turn out. It is simply a reference to the function that was used to create the object. I have covered a very similar topic on the Sinon tracker that you might have interest in reading to gain some core JS foo :-) Basically, it is not possible to stub a constructor, but you can probably coerce your code to use another constructor function in its place through either DI or link seams.

As a matter of fact, a few answers down in the same thread, you will see me covering an example of how I myself designed a Redis using class to be easily testable by supporting dependency injection. You might want to check it out, as it is more or less directly applicable to your example module above.

Another technique, which you already has tried getting to work, is using link seams (using rewire). The Sinon homepage has a nice article on doing this. Both rewire and proxyquire will do the job just fine here: I think you have just complicated the matter a bit by wrapping the require statement with a mock.

Even though I am on the Sinon maintainer team, I never use the mock functionality, so I cannot tell you how to use that, as I think it obscures the testing, but to get the basic link seams working using rewire I would basically remove all the Sinon code first and get the basic case going (removing redis for a stubbed module you have created).

Only then, add Sinon code as required.

oligofren
  • 20,744
  • 16
  • 93
  • 180
  • This is super useful, thanks! Learned some new things today :D – cameck Jun 12 '17 at 17:49
  • Sweet, always nice if one can help out. Ping back if you should go ahead with this and get stuck. Might update with some details if needed. – oligofren Jun 12 '17 at 21:38