How to mock the imports of an ES6 module? : <p>I have the following ES6 modules:</p>

<p><strong>network.js</strong></p>

<pre><code>export function getDataFromServer() {
  return ...
}
</code></pre>

<p><strong>widget.js</strong></p>

<pre><code>import { getDataFromServer } from 'network.js';

export class Widget() {
  constructor() {
    getDataFromServer("dataForWidget")
    .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("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(window, "getDataFromServer").andReturn("mockData")
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});
</code></pre>

<p>However, if I'm testing ES6 modules individually outside of a browser (like with Mocha + babel), I would write something like: </p>

<pre><code>import { Widget } from 'widget.js';

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(?????) // How to mock?
    .andReturn("mockData")
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    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>

<h1>So where do I go from here?</h1>

<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("dataForWidget")
    .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("dataForWidget")
    .then(data =&gt; this.render(data));
  }
}
</code></pre>

<p>then:</p>

<pre><code>import { Widget, deps } from 'widget.js';

describe("widget", function() {
  it("should do stuff", function() {
    let getDataFromServer = spyOn(deps.getDataFromServer)  // !
      .andReturn("mockData");
    let widget = new Widget();
    expect(getDataFromServer).toHaveBeenCalledWith("dataForWidget");
    expect(otherStuff).toHaveHappened();
  });
});
</code></pre>

<p>This is less invasive but 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>
