UPDATE: Ich habe diesen und andere Ansätze in https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README zusammengefasst. md
Was ist der beste Weg, um das folgende Beispiel richtig zu verspotten?
Das Problem ist, dass nach dem Import foo
der Verweis auf das Original nicht verspottet bleibt bar
.
module.js:
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
module.test.js:
import * as module from '../src/module';
describe('module', () => {
let barSpy;
beforeEach(() => {
barSpy = jest.spyOn(
module,
'bar'
).mockImplementation(jest.fn());
});
afterEach(() => {
barSpy.mockRestore();
});
it('foo', () => {
console.log(jest.isMockFunction(module.bar)); // outputs true
module.bar.mockReturnValue('fake bar');
console.log(module.bar()); // outputs 'fake bar';
expect(module.foo()).toEqual('I am foo. bar is fake bar');
/**
* does not work! we get the following:
*
* Expected value to equal:
* "I am foo. bar is fake bar"
* Received:
* "I am foo. bar is bar"
*/
});
});
Vielen Dank!
EDIT: Ich könnte ändern:
export function foo () {
return `I am foo. bar is ${bar()}`;
}
zu
export function foo () {
return `I am foo. bar is ${exports.bar()}`;
}
aber das ist p. hässlich meiner Meinung nach überall zu tun: /
UPDATE: Ich habe diesen und andere Ansätze in https://github.com/magicmark/jest-how-do-i-mock-x/blob/master/src/function-in-same-module/README zusammengefasst. md
javascript
testing
mocking
jestjs
Kennzeichen
quelle
quelle
jest
GH-Seite github.com/facebook/jest/issues/936#issuecomment-545080082Antworten:
Das Problem scheint damit zu tun zu haben, wie Sie erwarten, dass der Umfang der Leiste gelöst wird.
Einerseits
module.js
exportieren Sie in zwei Funktionen (anstelle eines Objekts, das diese beiden Funktionen enthält). Aufgrund der Art und Weise, wie Module exportiert werden, ist der Verweis auf den Container der exportierten Objekte so,exports
wie Sie es erwähnt haben.Auf der anderen Seite behandeln Sie Ihren Export (den Sie mit einem Alias versehen haben
module
) wie ein Objekt, das diese Funktionen enthält und versucht, eine seiner Funktionen (die Funktionsleiste) zu ersetzen.Wenn Sie sich Ihre foo-Implementierung genau ansehen, haben Sie tatsächlich einen festen Verweis auf die Balkenfunktion.
Wenn Sie glauben, die Balkenfunktion durch eine neue ersetzt zu haben, haben Sie gerade die Referenzkopie im Bereich Ihrer module.test.js ersetzt
Damit foo tatsächlich eine andere Version von bar verwendet, haben Sie zwei Möglichkeiten:
Exportieren Sie in module.js eine Klasse oder eine Instanz, die sowohl die foo- als auch die bar-Methode enthält:
Module.js:
export class MyModule { function bar () { return 'bar'; } function foo () { return `I am foo. bar is ${this.bar()}`; } }
Beachten Sie die Verwendung dieses Schlüsselworts in der foo-Methode.
Module.test.js:
import { MyModule } from '../src/module' describe('MyModule', () => { //System under test : const sut:MyModule = new MyModule(); let barSpy; beforeEach(() => { barSpy = jest.spyOn( sut, 'bar' ).mockImplementation(jest.fn()); }); afterEach(() => { barSpy.mockRestore(); }); it('foo', () => { sut.bar.mockReturnValue('fake bar'); expect(sut.foo()).toEqual('I am foo. bar is fake bar'); }); });
Schreiben Sie, wie Sie sagten, die globale Referenz in den globalen
exports
Container neu. Dies ist kein empfohlener Weg, da Sie möglicherweise in anderen Tests seltsame Verhaltensweisen einführen, wenn Sie die Exporte nicht ordnungsgemäß auf den Ausgangszustand zurücksetzen.quelle
Eine alternative Lösung kann darin bestehen, das Modul in eine eigene Codedatei zu importieren und die importierte Instanz aller exportierten Entitäten zu verwenden. So was:
import * as thisModule from './module'; export function bar () { return 'bar'; } export function foo () { return `I am foo. bar is ${thisModule.bar()}`; }
Jetzt ist das Verspotten
bar
wirklich einfach, dafoo
auch die exportierte Instanz von verwendet wirdbar
:import * as module from '../src/module'; describe('module', () => { it('foo', () => { spyOn(module, 'bar').and.returnValue('fake bar'); expect(module.foo()).toEqual('I am foo. bar is fake bar'); }); });
Das Importieren des Moduls in seinen eigenen Code sieht seltsam aus, aber aufgrund der Unterstützung des ES6 für zyklische Importe funktioniert es wirklich reibungslos.
quelle
fwiw, die Lösung, für die ich mich entschieden habe, war die Verwendung der Abhängigkeitsinjektion durch Festlegen eines Standardarguments.
Also würde ich mich ändern
export function bar () { return 'bar'; } export function foo () { return `I am foo. bar is ${bar()}`; }
zu
export function bar () { return 'bar'; } export function foo (_bar = bar) { return `I am foo. bar is ${_bar()}`; }
Dies ist keine grundlegende Änderung an der API meiner Komponente, und ich kann die Leiste in meinem Test leicht überschreiben, indem ich die folgenden Schritte ausführe
import { foo, bar } from '../src/module'; describe('module', () => { it('foo', () => { const dummyBar = jest.fn().mockReturnValue('fake bar'); expect(foo(dummyBar)).toEqual('I am foo. bar is fake bar'); }); });
Dies hat den Vorteil, dass auch der Testcode etwas besser ist :)
quelle
Ich hatte das gleiche Problem und aufgrund der Flusenstandards des Projekts waren das Definieren einer Klasse oder das Umschreiben von Referenzen in den
exports
Optionen keine Codeüberprüfung, auch wenn dies nicht durch die Flusendefinitionen verhindert wurde. Als praktikable Option bin ich auf das Babel-Rewire-Plugin gestoßen , das zumindest optisch viel sauberer ist. Während ich fand, dass dies in einem anderen Projekt verwendet wurde, auf das ich Zugriff hatte, bemerkte ich, dass es bereits in einer Antwort auf eine ähnliche Frage enthalten war, die ich hier verlinkt habe . Dies ist ein Ausschnitt, der für diese Frage (und ohne Verwendung von Spionen) angepasst wurde und aus der verknüpften Antwort als Referenz bereitgestellt wird (ich habe zusätzlich zum Entfernen von Spionen auch Semikolons hinzugefügt, da ich kein Heide bin):import __RewireAPI__, * as module from '../module'; describe('foo', () => { it('calls bar', () => { const barMock = jest.fn(); __RewireAPI__.__Rewire__('bar', barMock); module.foo(); expect(bar).toHaveBeenCalledTimes(1); }); });
https://stackoverflow.com/a/45645229/6867420
quelle
Funktioniert bei mir:
cat moduleWithFunc.ts export function funcA() { return export.funcB(); } export function funcB() { return false; } cat moduleWithFunc.test.ts import * as module from './moduleWithFunc'; describe('testFunc', () => { beforeEach(() => { jest.clearAllMocks(); }); afterEach(() => { module.funcB.mockRestore(); }); it.only('testCase', () => { // arrange jest.spyOn(module, 'funcB').mockImplementationOnce(jest.fn().mockReturnValue(true)); // act const result = module.funcA(); // assert expect(result).toEqual(true); expect(module.funcB).toHaveBeenCalledTimes(1); }); });
quelle
Wenn Sie Ihre Exporte definieren, können Sie Ihre Funktionen als Teil des Exportobjekts referenzieren. Dann können Sie die Funktionen in Ihren Mocks einzeln überschreiben. Dies liegt daran, wie der Import als Referenz und nicht als Kopie funktioniert.
exports.bar () => { return 'bar'; } exports.foo () => { return `I am foo. bar is ${exports.bar()}`; }
describe('MyModule', () => { it('foo', () => { let module = require('./module') module.bar = jest.fn(()=>{return 'fake bar'}) expect(module.foo()).toEqual('I am foo. bar is fake bar'); }); })
quelle
exports is undefiend