仅针对一个测试使用新模拟覆盖现有/共享 Jest 模拟

时间:2021-06-04 15:11:29

标签: javascript testing jestjs integration-testing

我有一个集成测试的 Jest 测试套件,使用 suite-global 模拟进行数据库访问,其中根据 SQL 我返回不同的模拟响应:

jest.mock('@my-org/our-mysql-wrapper', () => {
    const query = jest.fn(async (sql, params) => {
        if (sql === 'select foo from bar') {
            return [];
        } else if (sql === 'select baz') {
            return [{ messageId: 3 }, { messageId: 4 }, { messageId: 5 }];
        } else if (...) {
            return ...;
        } else {
            console.log('UNEXPECTED QUERY SENT TO MOCK: ', sql, params);
            return [];
        }
    });
    const end = jest.fn(async () => true);

    return jest.fn(() => ({ query, end }));
});

describe('suite', () => {
    //tests here
});

这对阳性测试很有用,但我对它感到沮丧的是阴性测试。例如,在某些情况下,如果数据库不返回任何结果,我们可能想要抛出错误。为了测试我需要让我的模拟对于相同的输入有不同的表现。典型的正面测试不会在运行前覆盖数据库模拟,而负面测试需要:

it('should throw and handle an error if the db returns no results for Widget lookup', async () => {
    const mockDB = require('@my-org/our-mysql-wrapper')();
    mockDB.query.mockImplementation(jest.fn(asnyc (sql, params) => {
        if ( sql === 'select * from Widgets' ){
            //this is the use-case that I want to override for this test
            return [];
        }else{
            //...
        }
    }));
    
    const someValue = await tool.doThing();
    expect(buglogger).toHaveBeenCalled(); //actual test will be more specific...

    //I tried plugging in mockRestore/mockClear/mockReset here
});

正如上面所写的,这个测试实际上通过了,但它会破坏在它之后运行的测试,因为它不会自行清理。据我所知,这就是 mockClear()mockReset()mockRestore() 在不同变体中应该做的事情;但我一直无法找到一种方法,在测试结束时将我的模拟恢复到原始的预覆盖模拟实现。

我还在其他一些情况下使用了 jest.spyOn(),但这也不是我想要的。在这种情况下,我的测试失败了,其他测试的模拟也仍然损坏。

it('should throw and handle an error if the db returns no results for Widget lookup', async () => {
    const mockDB = require('@my-org/our-mysql-wrapper')();
    jest.spyOn(mockDb, 'query');
    mockDB.query.mockImplementation(jest.fn(asnyc (sql, params) => {
        if ( sql === 'select * from Widgets' ){
            //this is the use-case that I want to override for this test
            return [];
        }else{
            //...
        }
    }));
    
    const someValue = await tool.doThing();
    expect(buglogger).toHaveBeenCalled(); //actual test will be more specific...
    mockDb.query.mockRestore();
});

我也尝试过 mockImplementationOnce() 并且这对我不起作用,因为有问题的查询不是将运行的第一个查询。使用此方法确实会在其自身之后自动清理,但不会(据我所知不能)使我的测试通过,因为它在调用相关查询之前第一次使用后会自行清理。

但是既然 mockImplementationOnce 可以通过一种恢复原始模拟的方式清理自己,难道不应该有一些手动方法来覆盖现有的模拟只是为了一个测试吗?这就是 mockImplementationOnce 正在做的,不是吗? (在第一次调用后清理而不是在 1 次测试后清理;但它似乎正在恢复原始模拟......)

我在这里做错了什么?

1 个答案:

答案 0 :(得分:0)

我认为问题出在这两种情况下,您正在尝试将模拟分配给您导入的实际包。

您是否尝试过如下更改模拟设置:

const mockDB = require('@my-org/our-mysql-wrapper')();
const dbSpy = jest.spyOn(mockDB, 'query');

dbSpy.mockImplementation(jest.fn(asnyc (sql, params) => {
  ...
}));

dbSpy.mockClear();
dbSpy.mockRestore();
相关问题