Beim Testen eines Moduls, das eine Abhängigkeit in einer anderen Datei hat. Wenn Sie dieses Modul als jest.Mock
Typoskript zuweisen, wird der Fehler mockReturnThisOnce
angezeigt, dass die Methode (oder eine andere jest.Mock-Methode) in der Abhängigkeit nicht vorhanden ist. Dies liegt daran, dass sie zuvor eingegeben wurde. Was ist der richtige Weg, um Typoskript dazu zu bringen, die Typen von jest.Mock zu erben?
Hier ist ein kurzes Beispiel.
Abhängigkeit
const myDep = (name: string) => name;
export default myDep;
test.ts
import * as dep from '../depenendency';
jest.mock('../dependency');
it('should do what I need', () => {
//this throws ts error
// Property mockReturnValueOnce does not exist on type (name: string)....
dep.default.mockReturnValueOnce('return')
}
Ich bin der Meinung, dass dies ein sehr häufiger Anwendungsfall ist und ich nicht sicher bin, wie ich das richtig eingeben soll. Jede Hilfe wäre sehr dankbar!
javascript
unit-testing
typescript
jestjs
Philip Chmalts
quelle
quelle
import
werden zuerst ausgewertet, unabhängig davon, ob Sie vor dem Import Code eingeben. Das wird also nicht funktionieren.mock...
Antworten:
Sie können Type Casting verwenden und Ihr
test.ts
sollte so aussehen:import * as dep from '../dependency'; jest.mock('../dependency'); const mockedDependency = <jest.Mock<typeof dep.default>>dep.default; it('should do what I need', () => { //this throws ts error // Property mockReturnValueOnce does not exist on type (name: string).... mockedDependency.mockReturnValueOnce('return'); });
TS Transpiler ist sich nicht bewusst, dass
jest.mock('../dependency');
sich der Typ ändert,dep
daher müssen Sie den Typ Casting verwenden. Da importiertdep
keine Typdefinition ist, müssen Sie den Typ mit abrufentypeof dep.default
.Hier sind einige andere nützliche Muster, die ich während meiner Arbeit mit Jest und TS gefunden habe
Wenn das importierte Element eine Klasse ist, müssen Sie beispielsweise typeof nicht verwenden:
import { SomeClass } from './SomeClass'; jest.mock('./SomeClass'); const mockedClass = <jest.Mock<SomeClass>>SomeClass;
Diese Lösung ist auch nützlich, wenn Sie einige native Knotenmodule verspotten müssen:
import { existsSync } from 'fs'; jest.mock('fs'); const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;
Für den Fall, dass Sie keinen automatischen Scherz-Mock verwenden möchten und lieber einen manuellen erstellen möchten
import TestedClass from './TestedClass'; import TestedClassDependency from './TestedClassDependency'; const testedClassDependencyMock = jest.fn<TestedClassDependency>(() => ({ // implementation })); it('Should throw an error when calling playSomethingCool', () => { const testedClass = new TestedClass(testedClassDependencyMock()); });
testedClassDependencyMock()
Erstellt eine verspottete ObjektinstanzTestedClassDependency
kann entweder eine Klasse oder ein Typ oder eine Schnittstelle seinquelle
jest.fn(() =>...
stattdessen verwendenjest.fn<TestedClassDependency>(() =>...
(ich habe gerade das Typ-Casting nach jest.fn entfernt), weil IntelliJ sich beschwert. Ansonsten hat mir diese Antwort geholfen, danke! Verwenden Sie dies in meinem package.json: "@ types / jest": "^ 24.0.3"jest.mock('./SomeClass');
obige Code?<jest.Mock<SomeClass>>SomeClass
Ausdruck erzeugt einen TS-Fehler für mich:Conversion of type 'typeof SomeClass' to type 'Mock<SomeClass, any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'typeof SomeClass' is missing the following properties from type 'Mock<SomeClass, any>': getMockName, mock, mockClear, mockReset, and 11 more.ts(2352)
Verwenden Sie den
mocked
Helferts-jest
wie hier beschrieben// foo.spec.ts import { mocked } from 'ts-jest/utils' import { foo } from './foo' jest.mock('./foo') // here the whole foo var is mocked deeply const mockedFoo = mocked(foo, true) test('deep', () => { // there will be no TS error here, and you'll have completion in modern IDEs mockedFoo.a.b.c.hello('me') // same here expect(mockedFoo.a.b.c.hello.mock.calls).toHaveLength(1) }) test('direct', () => { foo.name() // here only foo.name is mocked (or its methods if it's an object) expect(mocked(foo.name).mock.calls).toHaveLength(1) })
und wenn
tslint
ts-jest
liegt in deinen dev-Abhängigkeiten,Fügen Sie diese Regel zu Ihrem hinzu
tslint.json
:"no-implicit-dependencies": [true, "dev"]
quelle
ts-jest
und Klassen: github.com/tbinna/ts-jest-mock-examples und dieser Beitrag: stackoverflow.com/questions/58639737/…Ich verwende das Muster aus @ types / jest / index.d.ts direkt über dem Typ def für Mocked (Zeile 515):
import { Api } from "../api"; jest.mock("../api"); const myApi: jest.Mocked<Api> = new Api() as any; myApi.myApiMethod.mockImplementation(() => "test");
quelle
const myApi = new Api() as jest.Mocked<Api>;
jest.Mock<Api>
. Sie müssten mitgehenconst myApi = new Api() as any as jest.Mock<Api>
und ich würde sagen, das obige sieht ein bisschen besser aus als die doppelte Behauptung."strict": true
in tsconfig.json. Zu dieser Kategorie gehören solche SachennoImplicitAny
,strictNullChecks
etc., so dass Sie es wahr für sie individuell nicht festlegen müssen.myApi
? Es werden nicht generisch alle anderen Instanzen gestoppt, die von der KlasseApi
innerhalb des zu testenden Moduls initiiert wurden , oder?Es gibt zwei Lösungen, beide gießen die gewünschte Funktion
1) Verwenden Sie jest.MockedFunction
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;
2) Verwenden Sie jest.Mock
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.default as jest.Mock;
Es gibt keinen Unterschied zwischen diesen beiden Lösungen. Der zweite ist kürzer und ich würde daher vorschlagen, diesen zu verwenden.
Beide Casting-Lösungen ermöglichen das Aufrufen einer beliebigen Scherz-Mock-Funktion unter
mockMyFunction
likemockReturnValue
odermockResolvedValue
https://jestjs.io/docs/en/mock-function-api.htmlmockMyFunction.mockReturnValue('value');
mockMyFunction
kann normalerweise für erwarten verwendet werdenexpect(mockMyFunction).toHaveBeenCalledTimes(1);
quelle
Besetzung
as jest.Mock
Einfach die Funktion zu übertragen,
jest.Mock
sollte den Trick tun:(dep.default as jest.Mock).mockReturnValueOnce('return')
quelle
Folgendes habe ich mit [email protected] und [email protected] gemacht :
Quelle:
class OAuth { static isLogIn() { // return true/false; } static getOAuthService() { // ... } }
Prüfung:
import { OAuth } from '../src/to/the/OAuth' jest.mock('../src/utils/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } })); describe('createMeeting', () => { test('should call conferenceLoginBuild when not login', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); // Other tests }); });
So verspotten Sie eine nicht standardmäßige Klasse und ihre statischen Methoden:
jest.mock('../src/to/the/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } }));
Hier sollte eine Typkonvertierung vom Typ Ihrer Klasse zu
jest.MockedClass
oder so ähnlich sein. Aber es kommt immer zu Fehlern. Also habe ich es einfach direkt benutzt und es hat funktioniert.test('Some test', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); });
Wenn es sich jedoch um eine Funktion handelt, können Sie sie verspotten und die Typkonversation durchführen.
jest.mock('../src/to/the/Conference', () => ({ conferenceSuccessDataBuild: jest.fn(), conferenceLoginBuild: jest.fn() })); const mockedConferenceLoginBuild = conferenceLoginBuild as jest.MockedFunction< typeof conferenceLoginBuild >; const mockedConferenceSuccessDataBuild = conferenceSuccessDataBuild as jest.MockedFunction< typeof conferenceSuccessDataBuild >;
quelle
Ich habe dies gefunden in
@types/jest
:/** * Wrap a function with mock definitions * * @example * * import { myFunction } from "./library"; * jest.mock("./library"); * * const mockMyFunction = myFunction as jest.MockedFunction<typeof myFunction>; * expect(mockMyFunction.mock.calls[0][0]).toBe(42); */
Hinweis: Wenn Sie dies tun
const mockMyFunction = myFunction
und dann so etwasmockFunction.mockReturnValue('foo')
, ändern Sie sichmyFunction
ebenfalls.Quelle: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089
quelle
Verwenden Sie
as jest.Mock
und sonst nichtsDie prägnanteste Art, ein wie
default
in ts-jest exportiertes Modul zu verspotten , die ich mir vorstellen kann, besteht darin, das Modul als zu gießenjest.Mock
.Code:
import myDep from '../dependency' // No `* as` here jest.mock('../dependency') it('does what I need', () => { // Only diff with pure JavaScript is the presence of `as jest.Mock` (myDep as jest.Mock).mockReturnValueOnce('return') // Call function that calls the mocked module here // Notice there's no reference to `.default` below expect(myDep).toHaveBeenCalled() })
Leistungen:
default
Eigenschaft irgendwo im Testcode - Sie verweisen stattdessen auf den tatsächlich exportierten Funktionsnamen.* as
in der import Anweisung,typeof
Schlüsselwort,mocked
.quelle
Eine aktuelle Bibliothek löst dieses Problem mit einem Babel-Plugin: https://github.com/userlike/joke
Beispiel:
import { mock, mockSome } from 'userlike/joke'; const dep = mock(import('./dependency')); // You can partially mock a module too, completely typesafe! // thisIsAMock has mock related methods // thisIsReal does not have mock related methods const { thisIsAMock, thisIsReal } = mockSome(import('./dependency2'), () => ({ thisIsAMock: jest.fn() })); it('should do what I need', () => { dep.mockReturnValueOnce('return'); }
Beachten Sie dies
dep
undmockReturnValueOnce
sind vollständig typsicher. Darüber hinaus ist tsserver bekannt, dassdepencency
es importiert und zugewiesen wurdedep
sodass alle von tsserver unterstützten automatischen Refactorings auch funktionieren.Hinweis: Ich pflege die Bibliothek.
quelle