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 de getDataFromServer
. Se eu usasse <script>
s separados em vez de módulos ES6, como no Karma, 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 eu 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 agora getDataFromServer
não está disponível no window
(bem, não existe window
) e não sei como injetar coisas diretamente no widget.js
próprio escopo.
Então, para onde eu vou daqui?
- Existe uma maneira de acessar o escopo
widget.js
ou, pelo menos, substituir suas importações por meu próprio código? - Caso contrário, como posso fazer o teste
Widget
?
Coisas que eu considerei:
uma. Injeção de dependência manual.
Remova todas as importações widget.js
e espere que o chamador forneça os deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Estou muito desconfortável por atrapalhar a interface pública do Widget assim 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 requer que eu escreva um monte de clichê para cada módulo, e ainda há o risco de eu usá-lo em getDataFromServer
vez de deps.getDataFromServer
o tempo todo. Estou desconfortável com isso, mas essa é a minha melhor ideia até agora.
createSpy
( github.com/jasmine/jasmine/blob/… ) com uma referência importada para getDataFromServer do módulo 'network.js'. Para que, no arquivo de testes do widget, você importasse getDataFromServer e, então, let spy = createSpy('getDataFromServer', getDataFromServer)
spyOn
naquele objeto, importado do network.js
módulo. É sempre uma referência ao mesmo objeto.
Widget
a interface pública da? Widget
está bagunçado sem deps
. Por que não tornar a dependência explícita?