Al probar un módulo que tiene una dependencia en un archivo diferente. Al asignar ese módulo para ser jest.Mockmecanografiado 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

importse 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.tsdeberí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,deppor lo tanto, debe usar el tipo de conversión. Como importadodepno 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 simuladoTestedClassDependencypuede 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>>SomeClassexpresió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
mockedayudante dets-jestcomo 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
tslintts-jestestá en tus dependencias de desarrollo,agregue esta regla a su
tslint.json:"no-implicit-dependencies": [true, "dev"]fuente
ts-jesty 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": trueen tsconfig.json. Esto cubre cosas comonoImplicitAny,strictNullChecksetc., 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 claseApidentro 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
mockMyFunctiongustamockReturnValueomockResolvedValuehttps://jestjs.io/docs/en/mock-function-api.htmlmockMyFunction.mockReturnValue('value');mockMyFunctionse puede utilizar normalmente para esperarexpect(mockMyFunction).toHaveBeenCalledTimes(1);fuente
Emitir
as jest.MockSimplemente lanzando la función a
jest.Mockdeberí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.MockedClasso 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 = myFunctiony 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
depymockReturnValueOnceson totalmente seguros. Además, tsserver sabe quedepencencyse importó y se le asignó, pordeplo que todas las refactorizaciones automáticas que admite tsserver también funcionarán.Nota: mantengo la biblioteca.
fuente