如何使用 Jest 仅为特定测试模拟模块的功能

fxl*_*ire 5 unit-testing typescript jestjs

我需要模拟依赖项的功能,但仅限于特定的测试用例。在我的其他测试中,我想保持模块不变。我也在嘲笑其他模块。那些需要保持嘲笑。

我尝试了多种方法,因为这个问题的许多变体都被问到了,但是由于我需要在不被嘲笑的情况下使用原始模块,所以其他解决方案似乎都不起作用......

这是我需要的一个例子

// app.ts
import * as moduleA from 'moduleA';

// ...

app.post('/signup', async (req, res) => {
   // ...
   const hashed = await moduleA.hash(req.body.password);
   // ...
});

export default app;


// app.test.ts

// some mocks
jest.mock('./database', () => ({ ... }));
// ...

import * as request from 'supertest';
import app from './app';

// ...

describe('A', () => {
    test('Those should work', async () => {
        const response = await request(app).post('/signup').send({ password: 'pw' });
        expect(response.status).toBe(200);
        // ...
    });
    // many other tests
    
    test('I need to force hash to crash here', async () => {
        // mock moduleA.hash only for this test
        const response = request(app).post('/signup').send({ password: 'pw' });
        expect(response.status).toBe(500);
    });

    test('moduleA.hash should be back to its default function', async () => {
        // request(app) and moduleA.hash will work properly, not mocked
        // ./database stays mocked
    });
});
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 3

您可以使用jest.spyOn()来模拟hash的方法moduleA,并使用.mockRestore()来恢复hash到原始实现。

\n\n

这是解决方案:

\n\n

app.ts:

\n\n
import express from \'express\';\nimport bodyParser from \'body-parser\';\n\nimport * as moduleA from \'./moduleA\';\nconst app = express();\n\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(bodyParser.json());\n\napp.post(\'/signup\', async (req, res) => {\n  try {\n    const hashed = await moduleA.hash(req.body.password);\n    console.log(hashed);\n    res.sendStatus(200);\n  } catch (error) {\n    res.sendStatus(500);\n  }\n});\n\nexport default app;\n
Run Code Online (Sandbox Code Playgroud)\n\n

moduleA.ts:

\n\n
export const hash = async (data: string) => {\n  return \'real hashed value\';\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

app.spec.ts:

\n\n
import app from \'./app\';\nimport request from \'supertest\';\nimport * as moduleA from \'./moduleA\';\n\ndescribe(\'app\', () => {\n  test(\'Those should work\', async () => {\n    const response = await request(app)\n      .post(\'/signup\')\n      .send({ password: \'pw\' });\n    expect(response.status).toBe(200);\n  });\n\n  test(\'I need to force hash to crash here\', async () => {\n    const mockedError = new Error(\'hash error\');\n    const hashSpy = jest.spyOn(moduleA, \'hash\').mockRejectedValueOnce(mockedError);\n    const response = await request(app)\n      .post(\'/signup\')\n      .send({ password: \'pw\' });\n    expect(response.status).toBe(500);\n    expect(moduleA.hash).toBeCalledWith(\'pw\');\n    hashSpy.mockRestore();\n  });\n\n  test(\'moduleA.hash should be back to its default function\', async () => {\n    const logSpy = jest.spyOn(console, \'log\');\n    const response = await request(app)\n      .post(\'/signup\')\n      .send({ password: \'pw\' });\n    expect(response.status).toBe(200);\n    expect(logSpy).toBeCalledWith(\'real hashed value\');\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

100%覆盖率的单元测试结果:

\n\n
PASS  src/stackoverflow/47540126/app.spec.ts\n  app\n    \xe2\x9c\x93 Those should work (72ms)\n    \xe2\x9c\x93 I need to force hash to crash here (10ms)\n    \xe2\x9c\x93 moduleA.hash should be back to its default function (14ms)\n\n  console.log src/stackoverflow/47540126/app.ts:3569\n    real hashed value\n\n  console.log node_modules/jest-mock/build/index.js:860\n    real hashed value\n\n------------|----------|----------|----------|----------|-------------------|\nFile        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |\n------------|----------|----------|----------|----------|-------------------|\nAll files   |      100 |      100 |      100 |      100 |                   |\n app.ts     |      100 |      100 |      100 |      100 |                   |\n moduleA.ts |      100 |      100 |      100 |      100 |                   |\n------------|----------|----------|----------|----------|-------------------|\nTest Suites: 1 passed, 1 total\nTests:       3 passed, 3 total\nSnapshots:   0 total\nTime:        4.156s, estimated 5s\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是完成的演示: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47540126

\n