How can I mock the imports of an ES6 module?
<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>