Mam następujące moduły 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() {
...
}
}
Szukam sposobu na przetestowanie Widget za pomocą próbnej instancji getDataFromServer
. Gdybym użył oddzielnych <script>
modułów zamiast modułów ES6, jak w Karmie, mógłbym napisać swój test tak:
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();
});
});
Jeśli jednak testuję moduły ES6 indywidualnie poza przeglądarką (jak z Mocha + babel), napisałbym coś takiego:
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, ale teraz getDataFromServer
nie jest dostępny w window
(cóż, w ogóle nie ma window
) i nie znam sposobu, aby wstrzyknąć rzeczy bezpośrednio do widget.js
własnego zakresu.
Więc dokąd mam się udać?
- Czy istnieje sposób uzyskania dostępu do zakresu
widget.js
lub przynajmniej zastąpienia jego importu własnym kodem? - Jeśli nie, w jaki sposób mogę uczynić
Widget
możliwym do przetestowania?
Rzeczy, które rozważałem:
za. Ręczne wstrzykiwanie zależności.
Usuń wszystkie importy z widget.js
i oczekuj, że wywołujący dostarczy deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Nie podoba mi się zepsucie publicznego interfejsu Widgeta w ten sposób i ujawnianie szczegółów implementacji. Nie idź.
b. Ujawnij importy, aby umożliwić ich kpienie.
Coś jak:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
następnie:
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();
});
});
Jest to mniej inwazyjne, ale wymaga ode mnie napisania wielu schematów dla każdego modułu i nadal istnieje ryzyko, że będę go używać getDataFromServer
zamiast tego przez deps.getDataFromServer
cały czas. Niepokoi mnie to, ale jak dotąd to mój najlepszy pomysł.
createSpy
( github.com/jasmine/jasmine/blob/… ) z importowanym odniesieniem do getDataFromServer z modułu „network.js”. Tak więc w pliku testów widżetu zaimportowałbyś getDataFromServer, a następnielet spy = createSpy('getDataFromServer', getDataFromServer)
spyOn
na tym obiekcie zaimportowanym z network.js
modułu. Jest to zawsze odniesienie do tego samego obiektu.
Widget
publiczny interfejs? Widget
jest pomieszany bez deps
. Dlaczego nie wyrazić zależności w sposób jawny?