10

I have a class AProvider that requires './b.provider'.

const BProvider = require('./b.provider');

class AProvider {
  static get defaultPath() {
    return `defaults/a/${BProvider.getThing()}`;
  }
}

module.exports = AProvider;

b.provider.js is adjacent to a.provider.js and looks like

global.stuff.whatever = require('../models').get('Whatever'); // I didn't write this!

class BProvider {
  static getThing() {
    return 'some-computed-thing';
  }
}
module.exports = BProvider;

In my test I use proxyquire to mock out ./b.provider as follows:

import { expect } from 'chai';
import proxyquire from 'proxyquire';

describe('A Provider', () => {
  const Provider = proxyquire('../src/a.provider', {
    './b.provider': {
      getThing: () => 'b-thing'
    },
  });

  describe('defaultPath', () => {
    it('has the expected value', () => {
      expect(Provider.defaultPath).to.equal('defaults/a/b-thing')
    });
  });
});

However when I run the test BProvider is still requiring the actual './b.provider' not the stub and BProvider's reference to global.stuff.whatever is throwing an error.

Why isn't this working?

Dave Sag
  • 13,266
  • 14
  • 86
  • 134
  • 1
    Have you tried declaring the proxyquire directive in a `before` statement? Also, not sure if this would help, but this article seems to think using sinon alongside it is necessary: http://www.thoughtdelimited.org/thoughts/post.cfm/instructing-proxyquire-to-ignore-nested-requires – francisco.preller Mar 02 '17 at 09:19
  • Interesting link. I didn't realise the Require still happened despite proxyquire. In a taxi right now but will test a solution when I get home. Thanks. – Dave Sag Mar 02 '17 at 11:44
  • Okay I have worked out the problem and have found an easy solution which I will post below. Thanks @francisco.preller for pointing me in the right direction. – Dave Sag Mar 02 '17 at 12:23
  • I tried the post by @Dave Sag and it did not work... however, I found that if I require proxyquire like this ```const proxyquire = require("proxyquire").noCallThru();``` all is good! – Dan Starns Sep 16 '19 at 17:20
  • @francisco.preller Hi wanted to read that article, but the link is dead. Can you repoint, please? – Emmanuel Mahuni Apr 10 '21 at 04:50

1 Answers1

10

The answer as to why this is happening is as follows

proxyquire still requires the underlying code before stubbing it out. It does this to enable callthroughs.

The solution is simply to explicitly disallow callthroughs.

The test becomes:

import { expect } from 'chai';
import proxyquire from 'proxyquire';

describe('A Provider', () => {
  const Provider = proxyquire('../src/a.provider', {
    './b.provider': {
      getThing: () => 'b-thing',
      '@noCallThru': true
    },
  });

  describe('defaultPath', () => {
    it('has the expected value', () => {
      expect(Provider.defaultPath).to.equal('defaults/a/b-thing')
    });
  });
});

Running this test works perfectly.

Dave Sag
  • 13,266
  • 14
  • 86
  • 134