title: How can I mock the imports of an ES6 module?
body: <p>I have the following ES6 modules:</p>
  <h3>File <em>network.js</em></h3>
  <pre><code>export function getDataFromServer() {
    return ...
  }
  </code></pre>
  <h3>File <em>widget.js</em></h3>
  <pre><code>import { getDataFromServer } from 'network.js';
  
  export class Widget() {
    constructor() {
      getDataFromServer(&quot;dataForWidget&quot;)
      .then(data =&gt; this.render(data));
    }
  
    render() {
      ...
    }
  }
  </code></pre>
  <p>I'm looking for a way to test Widget with a mock instance of <code>getDataFromServer</code>. If I used separate <code>&lt;script&gt;</code>s instead of ES6 modules, like in Karma, I could write my test like:</p>
  <pre><code>describe(&quot;widget&quot;, function() {
    it(&quot;should do stuff&quot;, function() {
      let getDataFromServer = spyOn(window, &quot;getDataFromServer&quot;).andReturn(&quot;mockData&quot;)
      let widget = new Widget();
      expect(getDataFromServer).toHaveBeenCalledWith(&quot;dataForWidget&quot;);
      expect(otherStuff).toHaveHappened();
    });
  });
  </code></pre>
  <p>However, if I'm testing ES6 modules individually outside of a browser (like with <a href="https://en.wikipedia.org/wiki/Mocha_(JavaScript_framework)" rel="noreferrer">Mocha</a> + <a href="https://en.wikipedia.org/wiki/Babel_(transcompiler)" rel="noreferrer">Babel</a>), I would write something like:</p>
  <pre><code>import { Widget } from 'widget.js';
  
  describe(&quot;widget&quot;, function() {
    it(&quot;should do stuff&quot;, function() {
      let getDataFromServer = spyOn(?????) // How to mock?
      .andReturn(&quot;mockData&quot;)
      let widget = new Widget();
      expect(getDataFromServer).toHaveBeenCalledWith(&quot;dataForWidget&quot;);
      expect(otherStuff).toHaveHappened();
    });
  });
  </code></pre>
  <p>Okay, but now <code>getDataFromServer</code> is not available in <code>window</code> (well, there's no <code>window</code> at all), and I don't know a way to inject stuff directly into <code>widget.js</code>'s own scope.</p>
  <h2>So where do I go from here?</h2>
  <ol>
  <li><strong>Is there a way to access the scope of <code>widget.js</code>, or at least replace its imports with my own code?</strong></li>
  <li><strong>If not, how can I make <code>Widget</code> testable?</strong></li>
  </ol>
  <hr />
  <h2>Stuff I considered:</h2>
  <h3>a. Manual dependency injection.</h3>
  <p>Remove all imports from <code>widget.js</code> and expect the caller to provide the deps.</p>
  <pre><code>export class Widget() {
    constructor(deps) {
      deps.getDataFromServer(&quot;dataForWidget&quot;)
      .then(data =&gt; this.render(data));
    }
  }
  </code></pre>
  <p>I'm very uncomfortable with messing up Widget's public interface like this and exposing implementation details. No go.</p>
  <hr />
  <h3>b. Expose the imports to allow mocking them.</h3>
  <p>Something like:</p>
  <pre><code>import { getDataFromServer } from 'network.js';
  
  export let deps = {
    getDataFromServer
  };
  
  export class Widget() {
    constructor() {
      deps.getDataFromServer(&quot;dataForWidget&quot;)
      .then(data =&gt; this.render(data));
    }
  }
  </code></pre>
  <p>then:</p>
  <pre><code>import { Widget, deps } from 'widget.js';
  
  describe(&quot;widget&quot;, function() {
    it(&quot;should do stuff&quot;, function() {
      let getDataFromServer = spyOn(deps.getDataFromServer)  // !
        .andReturn(&quot;mockData&quot;);
      let widget = new Widget();
      expect(getDataFromServer).toHaveBeenCalledWith(&quot;dataForWidget&quot;);
      expect(otherStuff).toHaveHappened();
    });
  });
  </code></pre>
  <p>This is less invasive, but it requires me to write a lot of boilerplate for each module, and there's still a risk of me using <code>getDataFromServer</code> instead of <code>deps.getDataFromServer</code> all the time. I'm uneasy about it, but that's my best idea so far.</p>