Al probar un módulo que tiene una dependencia en un archivo diferente. Al asignar ese módulo para ser jest.Mock
mecanografiado da un error de que el método mockReturnThisOnce
(o cualquier otro método jest.Mock) no existe en la dependencia, esto se debe a que se tecleó previamente. ¿Cuál es la forma correcta de obtener mecanografiado para heredar los tipos de jest.Mock?
He aquí un ejemplo rápido.
Dependencia
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')
}
Siento que este es un caso de uso muy común y no estoy seguro de cómo escribirlo correctamente. ¡Cualquier ayuda será muy apreciada!
javascript
unit-testing
typescript
jestjs
Philip Chmalts
fuente
fuente
import
se evalúan primero, sin importar si coloca algún código antes de la importación. Entonces esto no funcionará.mock...
Respuestas:
Puede utilizar la conversión de tipos y
test.ts
debería verse así: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 no es consciente de que
jest.mock('../dependency');
cambia el tipo de,dep
por lo tanto, debe usar el tipo de conversión. Como importadodep
no es una definición de tipo, debe obtener su tipotypeof dep.default
.Aquí hay algunos otros patrones útiles que encontré durante mi trabajo con Jest y TS
Cuando el elemento importado es una clase, no tiene que usar typeof, por ejemplo:
import { SomeClass } from './SomeClass'; jest.mock('./SomeClass'); const mockedClass = <jest.Mock<SomeClass>>SomeClass;
Esta solución también es útil cuando tiene que simular algunos módulos nativos de nodo:
import { existsSync } from 'fs'; jest.mock('fs'); const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;
En caso de que no desee utilizar la simulación automática de broma y prefiera crear una manual
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()
crea una instancia de objeto simuladoTestedClassDependency
puede ser de clase, de tipo o de interfazfuente
jest.fn(() =>...
lugar dejest.fn<TestedClassDependency>(() =>...
(acabo de eliminar el tipo de conversión después de jest.fn) porque IntelliJ se está quejando. De lo contrario, esta respuesta me ayudó, ¡gracias! Usando esto en mi package.json: "@ types / jest": "^ 24.0.3"jest.mock('./SomeClass');
en el código anterior?<jest.Mock<SomeClass>>SomeClass
expresión me produce un error de TS: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)
Utilice el
mocked
ayudante dets-jest
como se explica aquí// 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) })
y si
tslint
ts-jest
está en tus dependencias de desarrollo,agregue esta regla a su
tslint.json
:"no-implicit-dependencies": [true, "dev"]
fuente
ts-jest
y clases: github.com/tbinna/ts-jest-mock-examples y esta publicación: stackoverflow.com/questions/58639737/…Utilizo el patrón de @ types / jest / index.d.ts justo encima del type def para Mocked (línea 515):
import { Api } from "../api"; jest.mock("../api"); const myApi: jest.Mocked<Api> = new Api() as any; myApi.myApiMethod.mockImplementation(() => "test");
fuente
const myApi = new Api() as jest.Mocked<Api>;
jest.Mock<Api>
. Tendrías que seguirconst myApi = new Api() as any as jest.Mock<Api>
y diría que el de arriba se ve un poco mejor que una doble afirmación."strict": true
en tsconfig.json. Esto cubre cosas comonoImplicitAny
,strictNullChecks
etc., por lo que no tiene que configurarlo como verdadero para ellos individualmente.myApi
? No eliminará genéricamente todas las demás instancias iniciadas por la claseApi
dentro del módulo que se está probando, ¿verdad?Hay dos soluciones, ambas tienen la función deseada
1) Usa broma.
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;
2) Usa broma.
import * as dep from './dependency'; jest.mock('./dependency'); const mockMyFunction = dep.default as jest.Mock;
No hay diferencia entre estas dos soluciones. El segundo es más corto y, por lo tanto, sugeriría usar ese.
Ambas soluciones de fundición permiten llamar a cualquier función simulada de broma en me
mockMyFunction
gustamockReturnValue
omockResolvedValue
https://jestjs.io/docs/en/mock-function-api.htmlmockMyFunction.mockReturnValue('value');
mockMyFunction
se puede utilizar normalmente para esperarexpect(mockMyFunction).toHaveBeenCalledTimes(1);
fuente
Emitir
as jest.Mock
Simplemente lanzando la función a
jest.Mock
debería hacer el truco:(dep.default as jest.Mock).mockReturnValueOnce('return')
fuente
Esto es lo que hice con [email protected] y [email protected] :
fuente:
class OAuth { static isLogIn() { // return true/false; } static getOAuthService() { // ... } }
prueba:
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 }); });
Así es como simular una clase no predeterminada y sus métodos estáticos:
jest.mock('../src/to/the/OAuth', () => ({ OAuth: class { public static getOAuthService() { return { getAuthorizationUrl() { return ''; } }; } } }));
Aquí debería haber alguna conversión de tipo del tipo de su clase a
jest.MockedClass
o algo así. Pero siempre termina con errores. Así que lo usé directamente y funcionó.test('Some test', () => { OAuth.isLogIn = jest.fn().mockImplementationOnce(() => { return false; }); });
Pero, si es una función, puedes burlarte de ella y hacer el tipo de conversación.
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 >;
fuente
He encontrado esto en
@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); */
Nota: Cuando lo haces
const mockMyFunction = myFunction
y luego algo comomockFunction.mockReturnValue('foo')
, también estás cambiandomyFunction
.Fuente: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089
fuente
Una biblioteca reciente resuelve este problema con un complemento de babel: https://github.com/userlike/joke
Ejemplo:
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'); }
Tenga en cuenta que
dep
ymockReturnValueOnce
son totalmente seguros. Además, tsserver sabe quedepencency
se importó y se le asignó, pordep
lo que todas las refactorizaciones automáticas que admite tsserver también funcionarán.Nota: mantengo la biblioteca.
fuente