Ho i seguenti moduli 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() {
...
}
}
Sto cercando un modo per testare Widget con una finta istanza di getDataFromServer
. Se avessi usato <script>
moduli separati anziché ES6, come in Karma, avrei potuto scrivere il mio test come:
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();
});
});
Tuttavia, se sto testando i moduli ES6 singolarmente al di fuori di un browser (come con Mocha + babel), scriverei qualcosa del tipo:
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();
});
});
Va bene, ma ora getDataFromServer
non è disponibile in window
(beh, non c'è window
affatto), e non conosco un modo per iniettare cose direttamente nel widget.js
proprio ambito.
Quindi dove vado da qui?
- Esiste un modo per accedere all'ambito
widget.js
o almeno sostituire le sue importazioni con il mio codice? - In caso contrario, come posso renderlo
Widget
testabile?
Roba che ho considerato:
un. Iniezione manuale delle dipendenze.
Rimuovere tutte le importazioni da widget.js
e aspettarsi che il chiamante fornisca i deps.
export class Widget() {
constructor(deps) {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
Sono molto a disagio nel rovinare l'interfaccia pubblica di Widget in questo modo e nel mostrare i dettagli dell'implementazione. Non andare.
b. Esporre le importazioni per consentire di deriderle.
Qualcosa di simile a:
import { getDataFromServer } from 'network.js';
export let deps = {
getDataFromServer
};
export class Widget() {
constructor() {
deps.getDataFromServer("dataForWidget")
.then(data => this.render(data));
}
}
poi:
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();
});
});
Questo è meno invasivo ma mi richiede di scrivere un sacco di boilerplate per ogni modulo, e c'è comunque il rischio che io lo usi getDataFromServer
invece di deps.getDataFromServer
tutto il tempo. Non sono a mio agio, ma finora è la mia migliore idea.
createSpy
( github.com/jasmine/jasmine/blob/… ) con un riferimento importato per ottenereDataFromServer dal modulo 'network.js'. In modo che, nel file di test del widget, importassi getDataFromServer e poilet spy = createSpy('getDataFromServer', getDataFromServer)
spyOn
su quell'oggetto, importato dal network.js
modulo. È sempre un riferimento allo stesso oggetto.
Widget
l'interfaccia pubblica? Widget
è incasinato senza deps
. Perché non rendere esplicita la dipendenza?