如何在 vitest 中模拟 node-postgres

How to mock node-postgres in vitest

提问人:Florat 提问时间:10/19/2023 更新时间:11/8/2023 访问量:35

问:

我试图用 esm 来嘲笑。但我遇到了不同的问题。 当我在函数中使用该类时:node-postgresPool

import {Pool} from 'pg';
...
const pool = new Pool({connectionString});
const client = await pool.connect();
await client.query('BEGIN');
...

当我如上所述导入时,我可以用以下命令模拟池:Poolimport {Pool} from 'pg'

vi.mock('pg', () => {
    const Pool = vi.fn();
    const Client = vi.fn();
    return {Pool, Client};
});
function queryMockImplementation(migrationTableExists: boolean, migratedFiles?: string[]) {
    return (queryString: string) => {
        if (queryString === `SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname = 'administration' AND tablename = 'migrations')`) {
            return Promise.resolve({rows: [{exists: migrationTableExists}]});
        }
        return Promise.resolve({rows: []});
    }
}
function connectMockImplementation(clientQueries: string[], throwOnSqlQuery?: string) {
    return async() => {
        return {
            query: vi.fn().mockImplementation((queryString: string) => {
                if (throwOnSqlQuery === queryString) {
                    throw new Error('Error thrown by mock for client query.');
                }
                clientQueries.push(queryString);
            }),
            release: vi.fn(),
        };
    };
}

然后在测试中,我可以进行以下操作:

it('Should mock', () => async {
            const {Pool} = await import('pg');
            const clientQueries: string[] = [];
            Pool.prototype.query = vi.fn().mockImplementation(queryMockImplementation(false));
            Pool.prototype.connect = vi.fn().mockImplementation(connectMockImplementation(clientQueries));
            const pool = new Pool();
}

但这不是预期的方式。它有效,但我必须将数组注入到模拟实现中,以便客户端观察用 .我找不到直接嘲笑客户的方法。此外,这种导入方式在使用该库的包中也会产生 esm 问题。因此,当我尝试访问时:clientQueriesclient.queryPoolPool

import pg from 'pg';
const {Pool} = pg;

我得到最生动的错误:

Error: [vitest] No "default" export is defined on the "pg" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "vi.importActual" inside:

vi.mock("pg", async () => {
  const actual = await vi.importActual("pg")
  return {
    ...actual,
    // your mocked methods
  },
})

所以我正在调整模拟:

vi.mock('pg', async () => {
    const actual = <Record<string, any>>await vi.importActual("pg")
    const Pool = vi.fn();
    const Client = vi.fn();
    return {...actual, Pool, Client};
});

但现在我还有另外两个问题。我必须在测试中提供实际的数据库和连接字符串,并且客户端查询模拟不再起作用。

单元测试 模拟 es6-modules node-postgres vitest

评论


答:

0赞 Florat 11/8/2023 #1

我解决了这个问题。vitest-allowSyntheticDefaultImports 的示例存储库

import {describe, vi, it, expect} from 'vitest';
import {connect} from './index';
import pg from 'pg';
const {Pool} = pg;

vi.mock('pg', async () => {
    const actual = <Record<string, unknown>>await vi.importActual('pg');

    return {
        ...actual,
        default: {
            Pool: vi.fn(),
        }
    };
});

describe('connect', () => {
    it('should call Pool', () => {
        connect('postgres://...');
        expect(Pool).toBeCalledTimes(1);
    });
});