开玩笑的typescript属性模拟类型不存在

ext*_*ext 10 typescript jestjs

当使用jest.fn()添加模拟通常可以访问.mock属性来访问的详细信息,如电话,类似这样:

test('not working', () => {
    const foo = new Foo();
    foo.addListener = jest.fn();
    foo.func(); // will call addListener with a callback
    const callback = foo.addListener.mock.calls[0][0];
    expect(callback()).toEqual(1); // test the callback
});
Run Code Online (Sandbox Code Playgroud)

用打字稿而不是普通的javascript实现测试时,出现错误:

错误TS2339:类型'((callback:()=> number)=> void'不存在属性'mock'。

我可以通过强制转换来消除错误,any但是肯定有更好的方法:

const callback = (foo.addListener as any).mock.calls[0][0];
Run Code Online (Sandbox Code Playgroud)

在这个简单的代码中,可以使用重写模拟程序以存储参数,jest.fn(fn => { callback = fn; });但是使用时会发生相同的错误foo.addListener.mockClear(),无法以相同的方式进行重做。

那么如何摆脱错误,最好不丢失类型安全性呢?

gzt*_*mas 47

对于到达这里的任何人来说,比投射到any可能会投射为 jest.Mock 好一点

const callback = (foo.addListener as jest.Mock).mock.calls[0][0];
Run Code Online (Sandbox Code Playgroud)


Bri*_*ams 15

您可以jest.spyOn与类似函数的函数结合使用,mockImplementation同时在TypeScript中保留类型安全性:

class Foo {
  addListener = (callback: () => number) => { }
  func = () => {
    this.addListener(() => 1);
  }
}

test('working', () => {
  const foo = new Foo();
  const mockAddListener = jest.spyOn(foo, 'addListener'); // spy on foo.addListener
  mockAddListener.mockImplementation(() => { }); // replace the implementation if desired
  foo.func(); // will call addListener with a callback
  const callback = mockAddListener.mock.calls[0][0];
  expect(callback()).toEqual(1); // SUCCESS
});
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法在类中模拟函数而不在测试中启动它? (17认同)

小智 7

jest.spyOn是正确的方法。

但是,如果您想对不属于类的简单函数执行相同的操作,则可以使用jest.mocked. 以下是从 Firebase 导入函数的示例:

/** Example code depending on a simple function */
import {
  addDoc,  // Function to be mocked
  collection,
  getFirestore
} from 'firebase/firestore';

export async function createRecord(record: any) {
  const collectionRef = collection(getFirestore(), 'example-path');

  return addDoc(collectionRef, record);
}
Run Code Online (Sandbox Code Playgroud)
/** Unit tests for example code */
import { addDoc } from 'firebase/firestore';

jest.mock('firebase/firestore');

describe('Create record', () => {
  test('creates a new record', async () => {
    const mockedAddDoc = jest.mocked(addDoc);

    await createRecord({ hello: 'world' });

    expect(mockedAddDoc.mock.calls[0][0]).toMatchObject({ hello: 'world' });
  });
});
Run Code Online (Sandbox Code Playgroud)

https://jestjs.io/docs/jest-object#jestmockedtitem-t-deep--false

  • 这里的“jest.mocked(dependency)”提示是关键 (2认同)

Ogg*_*las 6

使用时出现以下错误axios

TS2339 (TS) 属性“mockResolvedValueOnce”在类型“AxiosStatic”上不存在

尝试使用axios as jest.Mock但出现以下错误:

TS2352 (TS) 类型 'AxiosStatic' 到类型 'Mock<any, any>' 的转换可能是一个错误,因为这两种类型与另一种类型都没有足够的重叠。如果这是故意的,请先将表达式转换为“未知”。类型 'AxiosStatic' 缺少来自类型 'Mock<any, any>' 的以下属性:getMockName、mock、mockClear、mockReset 等 12 个。

通过指定为解决它 axios as unknown as jest.Mock

AxiosRequest.test.tsx

import axios from 'axios';
import { MediaByIdentifier } from '../api/mediaController';

jest.mock('axios', () => jest.fn());

test('Test AxiosRequest',async () => {
    const mRes = { status: 200, data: 'fake data' };
    (axios as unknown as jest.Mock).mockResolvedValueOnce(mRes);
    const mock = await MediaByIdentifier('Test');
    expect(mock).toEqual(mRes);
    expect(axios).toHaveBeenCalledTimes(1);
});
Run Code Online (Sandbox Code Playgroud)

媒体控制器.ts:

import { sendRequest } from './request'
import { AxiosPromise } from 'axios'
import { MediaDto } from './../model/typegen/mediaDto';

const path = '/api/media/'

export const MediaByIdentifier = (identifier: string): AxiosPromise<MediaDto> => {
    return sendRequest(path + 'MediaByIdentifier?identifier=' + identifier, 'get');
}
Run Code Online (Sandbox Code Playgroud)

请求.ts:

import axios, { AxiosPromise, AxiosRequestConfig, Method } from 'axios';

const getConfig = (url: string, method: Method, params?: any, data?: any) => {
     const config: AxiosRequestConfig = {
         url: url,
         method: method,
         responseType: 'json',
         params: params,
         data: data,
         headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json' },
    }
    return config;
}

export const sendRequest = (url: string, method: Method, params?: any, data?: any): AxiosPromise<any> => {
    return axios(getConfig(url, method, params, data))
}
Run Code Online (Sandbox Code Playgroud)