Ich habe die folgenden ES6-Module:
network.js
export function getDataFromServer() {
return ...
}
widget.js
import { getDataFromServer } from 'network.js';
export class Widget() {
constructor() {
getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
render() {
...
}
}
Ich suche nach einer Möglichkeit, Widget mit einer Scheininstanz von zu testen getDataFromServer
. Wenn ich <script>
wie in Karma separate s anstelle von ES6-Modulen verwenden würde, könnte ich meinen Test wie folgt schreiben:
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();
});
});
Wenn ich jedoch ES6-Module einzeln außerhalb eines Browsers teste (wie bei Mocha + babel), würde ich Folgendes schreiben:
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();
});
});
Okay, aber jetzt getDataFromServer
ist es nicht verfügbar window
(nun, es gibt überhaupt keine window
), und ich kenne keine Möglichkeit, Dinge direkt in widget.js
den eigenen Bereich zu injizieren .
Wohin gehe ich von hier aus?
- Gibt es eine Möglichkeit, auf den Umfang von zuzugreifen
widget.js
oder zumindest seine Importe durch meinen eigenen Code zu ersetzen? - Wenn nicht, wie kann ich
Widget
testbar machen ?
Sachen, über die ich nachgedacht habe:
ein. Manuelle Abhängigkeitsinjektion.
Entfernen Sie alle Importe von widget.js
und erwarten Sie, dass der Anrufer die Deps bereitstellt.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Es ist mir sehr unangenehm, die öffentliche Oberfläche von Widget so durcheinander zu bringen und Implementierungsdetails offenzulegen. No Go.
b. Legen Sie die Importe offen, um sie verspotten zu können.
Etwas wie:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
dann:
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();
});
});
Dies ist weniger invasiv, erfordert jedoch, dass ich für jedes Modul eine Menge Boilerplate schreibe, und es besteht immer noch das Risiko, dass ich es getDataFromServer
anstelle von deps.getDataFromServer
ständig verwende. Ich bin mir nicht sicher, aber das ist meine bisher beste Idee.
createSpy
( github.com/jasmine/jasmine/blob/… ) mit einem importierten Verweis auf getDataFromServer aus dem Modul 'network.js' zu verwenden. Damit Sie in der Testdatei des Widgets getDataFromServer importieren und dannlet spy = createSpy('getDataFromServer', getDataFromServer)
spyOn
auf diesem Objekt aus dem network.js
Modul importieren . Es ist immer ein Verweis auf dasselbe Objekt.
Widget
öffentliche Schnittstelle durcheinander bringt . Widget
ist durcheinander ohne deps
. Warum nicht die Abhängigkeit explizit machen?