Como zombar das importações de um módulo ES6?
Eu tenho os seguintes módulos ES6:
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() {
...
}
}
Estou procurando uma maneira de testar o Widget com uma instância simulada degetDataFromServer
. Se eu usasse separado<script>
s em vez dos módulos ES6, como no Karma, eu poderia escrever meu teste como:
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();
});
});
No entanto, se estiver testando os módulos ES6 individualmente fora de um navegador (como no Mocha + babel), escreveria algo como:
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();
});
});
Ok, mas agoragetDataFromServer
não está disponível emwindow
(bem, não háwindow
) e não sei como injetar coisas diretamente nowidget.js
próprio escopo.
widget.js
, ou pelo menos substituir suas importações por meu próprio código?Caso contrário, como posso fazerWidget
testável?Coisas que eu considerei:uma. Injeção de dependência manual.Remova todas as importações dewidget.js
e espere que o chamador forneça os deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Fico muito desconfortável ao atrapalhar a interface pública do Widget dessa maneira e expor os detalhes da implementação. Não vá.
b. Exponha as importações para permitir zombar delas.Algo como:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
então:
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();
});
});
Isso é menos invasivo, mas exige que eu escreva um monte de clichê para cada módulo, e ainda há o risco de eu usargetDataFromServer
ao invés dedeps.getDataFromServer
o tempo todo. Estou desconfortável com isso, mas essa é a minha melhor ideia até agora.