Podczas testowania modułu, który ma zależność w innym pliku. Kiedy przypisanie tego modułu do typu jest.Mock
maszynopisu powoduje błąd, że metoda mockReturnThisOnce
(lub jakakolwiek inna metoda jest.Mock) nie istnieje w zależności, to dlatego, że została wcześniej wpisana. Jaki jest właściwy sposób uzyskania maszynopisu, aby odziedziczył typy z jest.Mock?
Oto szybki przykład.
Zależność
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')
}
Wydaje mi się, że jest to bardzo częsty przypadek użycia i nie jestem pewien, jak to poprawnie wpisać. Każda pomoc będzie mile widziana!
javascript
unit-testing
typescript
jestjs
Philip Chmalts
źródło
źródło
import
są oceniane jako pierwsze, bez względu na to, czy przed importem umieścisz jakiś kod. Więc to nie zadziała.mock...
Odpowiedzi:
Możesz użyć rzutowania typów i Twój
test.ts
powinien wyglądać tak: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'); });
Transpiler TS nie jest świadomy, że
jest.mock('../dependency');
zmienia typ,dep
więc musisz użyć rzutowania typów. Ponieważ importdep
nie jest definicją typu, musisz pobrać jej typtypeof dep.default
.Oto kilka innych przydatnych wzorców, które znalazłem podczas pracy z Jest i TS
Gdy importowany element jest klasą, nie musisz używać typeof na przykład:
import { SomeClass } from './SomeClass'; jest.mock('./SomeClass'); const mockedClass = <jest.Mock<SomeClass>>SomeClass;
To rozwiązanie jest również przydatne, gdy musisz mockować niektóre natywne moduły węzła:
import { existsSync } from 'fs'; jest.mock('fs'); const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;
W przypadku, gdy nie chcesz używać automatycznego makiety i wolisz utworzyć ręczną
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()
tworzy mockowaną instancję obiektu, któraTestedClassDependency
może być klasą, typem lub interfejsemźródło
jest.fn(() =>...
zamiastjest.fn<TestedClassDependency>(() =>...
(właśnie usunąłem rzutowanie typów po jest.fn), ponieważ IntelliJ narzeka. W przeciwnym razie ta odpowiedź pomogła mi dzięki! Używając tego w moim pakiecie.json: "@ types / jest": "^ 24.0.3"jest.mock('./SomeClass');
w powyższym kodzie?<jest.Mock<SomeClass>>SomeClass
Ekspresja powoduje błąd TS dla mnie: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)
Użyj
mocked
pomocnika,ts-jest
jak wyjaśniono tutaj// 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) })
i jeśli
tslint
ts-jest
jest w zależnościach od deweloperów,dodaj tę regułę do
tslint.json
:"no-implicit-dependencies": [true, "dev"]
źródło
ts-jest
i klas: github.com/tbinna/ts-jest-mock-examples i ten post: stackoverflow.com/questions/58639737/ ...Używam wzorca z @ types / jest / index.d.ts tuż nad typem def dla Mocked (wiersz 515):
import { Api } from "../api"; jest.mock("../api"); const myApi: jest.Mocked<Api> = new Api() as any; myApi.myApiMethod.mockImplementation(() => "test");
źródło
const myApi = new Api() as jest.Mocked<Api>;
jest.Mock<Api>
. Musiałbyś iść z tymconst myApi = new Api() as any as jest.Mock<Api>
i powiedziałbym, że ten powyżej wygląda trochę lepiej niż podwójna asercja."strict": true
pliku tsconfig.json. Obejmuje to rzeczy jaknoImplicitAny
,strictNullChecks
itd., Więc nie trzeba, aby ustawić go na true dla nich indywidualnie.myApi
? Generalnie nie będzie on zastępował wszystkich innych instancji zainicjowanych przez klasęApi
w testowanym module, prawda?Istnieją dwa rozwiązania, oba odlewają pożądaną funkcję
1) Użyj jest.MockedFunction
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;
2) Użyj jest.Mock
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.default as jest.Mock;
Nie ma różnicy między tymi dwoma rozwiązaniami. Drugi jest krótszy, dlatego sugerowałbym użycie tego.
Oba rozwiązania castingowe pozwalają na wywołanie dowolnej funkcji jest mock na
mockMyFunction
likemockReturnValue
lubmockResolvedValue
https://jestjs.io/docs/en/mock-function-api.htmlmockMyFunction.mockReturnValue('value');
mockMyFunction
może być używany normalnie do oczekiwaniaexpect(mockMyFunction).toHaveBeenCalledTimes(1);
źródło
Odlew
as jest.Mock
Proste rzutowanie funkcji na
jest.Mock
powinno załatwić sprawę:(dep.default as jest.Mock).mockReturnValueOnce('return')
źródło
Oto, co zrobiłem z [email protected] i [email protected] :
źródło:
class OAuth { static isLogIn() { // return true/false; } static getOAuthService() { // ... } }
test:
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 }); });
Oto jak mockować klasę inną niż domyślna i jej metody statyczne:
jest.mock('../src/to/the/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } }));
Tutaj powinna być konwersja typu z typu twojej klasy na
jest.MockedClass
lub coś podobnego. Ale zawsze kończy się to błędami. Więc użyłem go bezpośrednio i zadziałało.test('Some test', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); });
Ale jeśli jest to funkcja, możesz z niej kpić i przeprowadzić konwersację typu.
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 >;
źródło
Znalazłem to w
@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); */
Uwaga: kiedy to robisz,
const mockMyFunction = myFunction
a potem coś w stylumockFunction.mockReturnValue('foo')
, również się zmieniaszmyFunction
.Źródło: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089
źródło
Niedawna biblioteka rozwiązuje ten problem za pomocą wtyczki babel: https://github.com/userlike/joke
Przykład:
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'); }
Pamiętaj o tym
dep
imockReturnValueOnce
są w pełni bezpieczne. Poza tym tsserver jest świadomy tego, żedepencency
został zaimportowany i został do niego przypisany,dep
więc wszystkie automatyczne refaktoryzacje obsługiwane przez tsserver również będą działać.Uwaga: prowadzę bibliotekę.
źródło