How to replace @types/jest with @jest/globals and jest-mock
Tags: jest, typescript
Published: 2021-08-22 09:38:31

---
title: How to replace @types/jest with @jest/globals and jest-mock
published: true
description: 
tags: jest, typescript
//cover_image: https://direct_url_to_image.jpg
---

I believe that it's always better to prefer official type definitions rather than unofficial DefinitelyTyped. And [Jest v27 introduced new defaults](https://jestjs.io/ja/blog/2021/05/25/jest-27) so I thought that it's a nice timing to replace `@types/jest` with `@jest/globals` and `jest-mock`. I'll share several problems I met:

## Dependency Update

`jest-circus` is now default test runner, so we don't need to depend on it directly. Also remove `@types/jest` to replace with `@jest/globals` and `jest-mock`:

```sh
npm remove jest-circus @types/jest
npm add -D jest@^27.0.6 ts-jest@^27.0.5
```

## Configuration

I'm using `ts-jest` to transpile TypeScript code, and its configuration is like below. Now we can remove both `testEnvironment` and `testRunner` in most cases:

```js
const { defaults: tsjPreset } = require('ts-jest/presets')

module.exports = {
  clearMocks: true,
  moduleFileExtensions: ['js', 'ts'],
  // no need `testEnvironment: "node"` any more
  testMatch: ['**/*.test.ts'],
  // no need `testRunner: "jest-circus"` any more
  transform: {
    ...tsjPreset.transform,
  },
  verbose: true
}
```

I [do not use preset to configure `ts-jest`](https://kulshekhar.github.io/ts-jest/docs/getting-started/presets#advanced), to let other integrations like puppeteer use it.

## Import from `@jest/globals` and `jest-mock`

As described in the [official API Document](https://jestjs.io/docs/api), we can import Jest API from `@jest/globals` module.

About `SpyInstance`, `@types/jest` provides it as `jest.SpyInstance`. But official type definition provides it as `import { SpyInstance } from 'jest-mock';` ... with generics!

```typescript
import { beforeEach, jest } from '@jest/globals';
import { SpyInstance } from 'jest-mock';

let spyOSHomedir: SpyInstance<string, []>;

beforeEach(() => {
  spyOSHomedir = jest.spyOn(os, 'homedir');
  spyOSHomedir.mockReturnValue(__dirname);
});
```

This generics will bring better type safety, and tons of code change. For instance, mocked function sometimes wants to return the value that satisfies required type partially. In the following case, mocked `fs.statSync` returns an object that has only `isFile()` method even though the [required type `fs.Stats` has more properties and methods](https://nodejs.org/docs/latest-v12.x/api/fs.html#fs_class_fs_stats). In such case we can use the [Partial utility type](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype):

```typescript
import { beforeEach, jest } from '@jest/globals';
import { SpyInstance } from 'jest-mock';
import fs from 'fs';

let spyFsStat: SpyInstance<Partial<fs.Stats | fs.BigIntStats>, [fs.PathLike, fs.StatOptions?]>

beforeEach(() => {
  spyFsStat = jest.spyOn(fs, 'statSync');
  spyFsStat.mockImplementation((file: fs.PathLike) => {
    return { isFile: () => file === expectedJdkFile };
  });
});
```

One more example: when you mock overloaded methods, you'll face some difficulty like:

https://javascript.plainenglish.io/mocking-ts-method-overloads-with-jest-e9c3d3f1ce0c

By applying the solution introduced in that article, we may lose integrity in test codes. So it would be an acceptable solution to use `unknown` in some cases:

```typescript
let spyFsReadDir: SpyInstance<unknown, Parameters<typeof fs.readdirSync>>;

beforeEach(() => {
  // returned value is typed as `Dirent[]` but we want to return `string[]`, so use `unknown` to relax mismatch
  spyFsReadDir = jest.spyOn(fs, 'readdirSync');
  spyFsReadDir.mockImplementation(() => ['JavaTest']);
});
```

That's all, you may refer to [my commit](https://github.com/KengoTODA/setup-java/commit/24f091afd1eab070d0107b0ac233a5f4cc77aef5) replacing DefinitelyTyped with official type definitions. Thank you!