在 Jest 中使用 TypeScript 模拟依赖关系

您还应该检查 JSON (不是在 DevTools 中,而是在后端)。Angular HttpClient 很难解析带有 \0字符的 JSON,DevTools 会忽略它,所以在 Chrome 中很难找到它。

当测试一个在不同文件中具有依赖项的模块并将该模块指定为 jest.mock时,TypeScript 会给出一个错误,表明该依赖项上不存在方法 mockReturnThisOnce(或任何其他 jest.mock方法) ,这是因为它是先前类型化的。

基于 这篇文章

使 TypeScript 从 jest.mock继承类型的正确方法是什么?

来自 jest.mock

这里有一个简单的例子。

这里有一个简单的例子。

依赖性

const myDep = (name: string) => name;
export default myDep;

依赖性

const myDep = (name: string) => name;
export default myDep;

测试

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')
}

测试

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')
}

我觉得这是一个非常常见的用例,不确定如何正确地输入它。

166558 次浏览
TestedClassDependency可以是类、类型或接口

我使用类型 def for Mocked (第515行)上面的@type/jest/index.d.ts 的模式:

import { Api } from "../api";
jest.mock("../api");


const myApi: jest.Mocked<Api> = new Api() as any;
myApi.myApiMethod.mockImplementation(() => "test");
在返回后端(Spring)中的字符串时,您可能返回为 返回“使用过的弹簧”; 但是,根据 Spring,这个解析是不正确的。

下面是我对 Jest@24.8.024.0.2所做的:

来源:

class OAuth {


static isLogIn() {
// return true/false;
}


static getOAuthService() {
// ...
}
}

测试:

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
});
});
而是使用返回 “弹簧用”;

下面是模拟非默认类及其静态方法的方法:

jest.mock('../src/to/the/OAuth', () => ({
OAuth: class {
public static getOAuthService() {
return {
getAuthorizationUrl() {
return '';
}
};
}
}
}));
再见

这里应该有一些类型转换,从类的类型到 jest.MockedClass或类似的东西。但结果总是出错。所以我就直接用了,而且成功了。

test('Some test', () => {
OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
return false;
});
});
从’. ./src/to/the/OAuth’导入{ OAuth }

但是,如果它是一个函数,您可以模拟它并进行类型对话。

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
>;
Mock (’. ./src/utils/OAuth’,() = > ({

使用 mocked助手 OAuth: class { 就像这里解释的那样:

// foo.spec.ts
import { foo } from './foo'
jest.mock('./foo')


// here the whole foo var is mocked deeply
const mockedFoo = jest.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(jest.mocked(foo.name).mock.calls).toHaveLength(1)
})
Public static getOAuthService (){ 返回{

最近的一个库通过一个 babel 插件解决了这个问题: https://github.com/userlike/joke

例如:

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');
}
GetAuthorizationUrl (){ 报税表」 ;

请注意,depmockReturnValueOnce是完全类型安全的。最重要的是,tsserver 知道导入了 depencency并将其分配给了 dep,因此 tsserver 支持的所有自动重构也都能正常工作。

} }; }

注意: 我维护图书馆。

} })); 描述(‘ createMeeting’,() = > {

有两种解决方案测试的 TypeScript 版本3.x 和4. x,都铸造所需的功能

Test (“不登录时应调用 conferenceLoginBuild”,() = > {

1)使用 jest. MockedFunction

import * as dep from './dependency';


jest.mock('./dependency');


const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;

2)开玩笑,嘲笑

import * as dep from './dependency';


jest.mock('./dependency');


const mockMyFunction = dep.default as jest.Mock;

这两种解决方案之间没有区别。第二个比较短,所以我建议用那个。

两种强制转换解决方案都允许在 mockMyFunction上调用任何 jest mock 函数,如 mockReturnValuemockResolvedValue IsLogIn = jest.fn () Https://jestjs.io/docs/en/mock-function-api.html

mockMyFunction.mockReturnValue('value');
返回虚假;

mockMyFunction可以正常使用

expect(mockMyFunction).toHaveBeenCalledTimes(1);
});

演员 as jest.Mock

//其他测试

简单地将函数转换为 jest.Mock就可以解决这个问题:

(dep.default as jest.Mock).mockReturnValueOnce('return')


注意: 当你做 const mockMyFunction = myFunction和类似 mockFunction.mockReturnValue('foo')的动作时,你也是一个变化的 myFunction

资料来源: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089

我吸毒。NetCore 为我的后端任务,我能够解决这个问题使用牛顿软件。从控制器返回 JSON 字符串的 JSON 库包。

只使用 as jest.Mock

显然,并非所有的 JSON 序列化器都是按照正确的规范构建的. NET 5的“ return Ok (”) ;”肯定是不够的。

我能想到的最简洁的模拟模块输出为 default的方法可以归结为将模块转换为 jest.Mock

密码:

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()
})
受养人」)

好处:

    它(‘做我需要的’,() = > {
  • 不需要在测试代码中的任何地方引用 default属性-而是引用实际导出的函数名,
  • //与纯 JavaScript 的唯一区别是‘ as jest. Mock’的存在
  • 您可以使用相同的技术来模仿命名导出,
  • (myDep as jest. Mock)
  • 导入语句中没有 * as,
  • //调用在此处调用模拟模块的函数
  • 没有使用 typeof关键字的复杂铸造,
  • //注意,下面没有引用“ . default”
  • 没有像 mocked那样的额外依赖。
})

好处:

  • 不需要在测试代码中的任何地方引用 default属性-而是引用实际导出的函数名,
  • 这是丑陋的,事实上摆脱这种丑陋就是我为什么要看这个问题的原因,但是为了从模块模拟中获得强类型,你可以这样做:

    const myDep = (require('./dependency') as import('./__mocks__/dependency')).default;
    
    
    jest.mock('./dependency');
    
  • 您可以使用相同的技术来模仿命名导出,
  • 导入语句中没有 * as,
  • 确保您需要的是 './dependency',而不是直接使用 mock,否则您将得到两个不同的实例化。

使用 < a href = “ https://jestjs.io/docs/mock-function-api # jestmock edclass”rel = “ nofollow norefrer”> MockedClass

import SoundPlayer from '../sound-player';


jest.mock('../sound-player'); // SoundPlayer is now a mock constructor


const SoundPlayerMock = SoundPlayer as jest.MockedClass<typeof SoundPlayer>;
E 稍后将产生一个错误:

我只想补充一点,就是在 get/post 方法(.get<T>)上必须使用 省略通用参数

预期的类型来自声明的属性“ response seType”

Something 这将会奏效:

this.http.get(`https://myapi.com/health`, {responseType: 'text'})
在这里输入’{ headers? : HttpHeader | {[ header: string ] : string |

Something 这将 没有工作:

this.http.get<string>(`https://myapi.com/health`, {responseType: 'text'})
字符串[] ; } | 未定义; 观察: “ events”; context? : HttpContext |

后者将产生一个错误:

未定义;

预期的类型来自声明的属性“ response seType” 在这里输入’{ headers? : HttpHeader | {[ header: string ] : string | 字符串[] ; } | 未定义; 观察: “ events”; context? : HttpContext | 布尔值 | 未定义; 响应类型? : “ json”| 未定义; 布尔值 | 未定义; 响应类型? : “ json”| 布尔值 | 未定义; }’

我们希望类型化模拟的是,模拟对象类型包含模拟对象类型和 Jest 模拟类型的联合。

import foo from 'foo';
jest.mock('foo');


const mockedFoo = foo as jest.MockedFunction<typeof foo>;
// or: const mockedFooClass = foo as jest.MockedClass<typeof FooClass>;




mockedFoo.mockResolvedValue('mockResult');


// Or:
(mockedFoo.getSomething as jest.MockedFunction<typeof mockedFoo.getSomething>).mockResolvedValue('mockResult');

我也有同样的问题,但是在我的情况下,我忘记将代理 URL 添加到 API。

readonly apiUrl = this.appConfigService.appConfig.apiUrl + 'EndPointUrl';

如您所见,您可以手动强制转换所需的内容,或者需要遍历所有 的属性/方法来输入/强制转换所有内容。

为此(深度模拟类型)可以使用 Jest 27.4.0中引入的 开玩笑的

import foo from 'foo';
jest.mock('foo');


const mockedFoo = jest.mocked(foo, true);


mockedFoo.mockImplementation() // correctly typed
mockedFoo.getSomething.mockImplementation() // also correctly typed

这个答案帮助我找到了答案: https://stackoverflow.com/a/66887643/12134299

; { 返回值一次(‘ return’) ;

最新的玩笑允许你用 开玩笑的,被嘲笑了很容易地做到这一点

import * as dep from '../dependency';


jest.mock('../dependency');


const mockedDependency = jest.mocked(dep);


it('should do what I need', () => {
mockedDependency.mockReturnValueOnce('return');
});
});

对我来说,这就足够了:

let itemQ: queueItemType
jest.mock('../dependency/queue', () => {
return {
add: async (item: queueItemType, ..._args: any) => {
// then we can use the item that would be pushed to the queue in our tests
itemQ = item
return new Promise(resolve => {
resolve('Mocked')
})
},
}
})
被嘲笑) }) }, }

然后,无论何时调用 add 方法,它都会执行上面的代码,而不是将其推送到队列,在本例中是这样的。

使用 TypeScript 2.8,我们可以像这样使用 ReturnType:

import * as dep from "./depenendency"


jest.mock("./dependency")


const mockedDependency = <jest.Mock<ReturnType<typeof dep.default>>>dep.default


it("should do what I need", () => {
mockedDependency.mockReturnValueOnce("return")
})