【问题标题】:How to mock Sequelize with Jest?如何用 Jest 模拟 Sequelize?
【发布时间】:2021-02-15 07:05:17
【问题描述】:

我正在尝试为调用 Sequelize 以创建数据库的代码编写单元测试。

我终其一生都无法弄清楚如何模拟对 Sequelize 的调用,以便我可以断言它们已正确创建了数据库表。

我的 Sequelize 代码如下:

import { Sequelize, DataTypes } from "sequelize";

export setup_db = async (db_path: string) => {
    //Get read/write connection to database
    const sequelizeContext = new Sequelize({
      dialect: "sqlite",
      storage: db_path,
    });

    //Check if connection is secure, throw error if not
    try {
      await sequelizeContext.authenticate();
    } catch (err) {
      throw err;
    }

    //Define first table
    const Table1 = sequelizeContext.define(
      "table1",
      {
        fieldName_1: {
          type: DataTypes.STRING
        }
      },
      { tableName: "table1" }
    );

    //Define second table
    const Table2 = sequelizeContext.define(
      "table2", 
      {
        fieldName_1: {
          type: DataTypes.STRING
        },
      {tablename: "table2"}
    });

    //Define relationship between tables... each Gamertag hasMany wzMatches
    Table1.hasMany(Table2);

    await Table1.sync();
    await Table2.sync();
  };

理想情况下,我想断言define 被正确调用,hasMany 被正确调用,sync 被每个数据库调用。

我目前的测试代码如下,虽然报错了

无法窥探 authenticate 属性,因为它不是函数;取而代之的是未定义。

import { setup_db } from "../../core/dataManager";
const Sequelize = require("sequelize").Sequelize;

describe("DataManager.setup_db", () => {
  it("should call sequelize to correctly set up databases", async () => {
    //Arrange
    const authenticateSpy = jest.spyOn(Sequelize, "authenticate");

    //Act
    await setup_db("path/to/db.db");

    //Assert
    expect(authenticateSpy).toHaveBeenCalledTimes(1);
  });
});

我不确定spyOn 是否是正确的调用方法,或者我是否可以/如何使用jest.mock 模拟和检查对Sequelize 的调用。

【问题讨论】:

    标签: typescript unit-testing jestjs mocking sequelize.js


    【解决方案1】:

    我将使用jest.mock(moduleName, factory, options) 手动模拟sequelize 模块。

    单元测试解决方案:

    index.ts:

    import { Sequelize, DataTypes } from 'sequelize';
    
    export const setup_db = async (db_path: string) => {
      const sequelizeContext = new Sequelize({
        dialect: 'sqlite',
        storage: db_path,
      });
    
      try {
        await sequelizeContext.authenticate();
      } catch (err) {
        throw err;
      }
    
      const Table1 = sequelizeContext.define(
        'table1',
        {
          fieldName_1: {
            type: DataTypes.STRING,
          },
        },
        { tableName: 'table1' },
      );
    
      const Table2 = sequelizeContext.define(
        'table2',
        {
          fieldName_1: {
            type: DataTypes.STRING,
          },
        },
        { tableName: 'table2' },
      );
    
      (Table1 as any).hasMany(Table2);
    
      await Table1.sync();
      await Table2.sync();
    };
    

    index.test.ts:

    import { setup_db } from './';
    import { Sequelize, DataTypes } from 'sequelize';
    import { mocked } from 'ts-jest/utils';
    
    jest.mock('sequelize', () => {
      const mSequelize = {
        authenticate: jest.fn(),
        define: jest.fn(),
      };
      const actualSequelize = jest.requireActual('sequelize');
      return { Sequelize: jest.fn(() => mSequelize), DataTypes: actualSequelize.DataTypes };
    });
    
    const mSequelizeContext = new Sequelize();
    
    describe('64648688', () => {
      afterAll(() => {
        jest.resetAllMocks();
      });
      it('should setup db correctly', async () => {
        const mTable1 = { hasMany: jest.fn(), sync: jest.fn() };
        const mTable2 = { sync: jest.fn() };
        mocked(mSequelizeContext.define).mockImplementation((modelName): any => {
          switch (modelName) {
            case 'table1':
              return mTable1;
            case 'table2':
              return mTable2;
          }
        });
        await setup_db(':memory:');
        expect(Sequelize).toBeCalledWith({ dialect: 'sqlite', storage: ':memory:' });
        expect(mSequelizeContext.authenticate).toBeCalled();
        expect(mSequelizeContext.define).toBeCalledWith(
          'table1',
          {
            fieldName_1: {
              type: DataTypes.STRING,
            },
          },
          { tableName: 'table1' },
        );
        expect(mSequelizeContext.define).toBeCalledWith(
          'table2',
          {
            fieldName_1: {
              type: DataTypes.STRING,
            },
          },
          { tableName: 'table2' },
        );
        expect(mTable1.hasMany).toBeCalledWith(mTable2);
        expect(mTable1.sync).toBeCalledTimes(1);
        expect(mTable2.sync).toBeCalledTimes(1);
      });
    });
    

    单元测试结果:

     PASS  src/stackoverflow/64648688/index.test.ts (16.442s)
      64648688
        ✓ should setup db correctly (11ms)
    
    ----------|----------|----------|----------|----------|-------------------|
    File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    ----------|----------|----------|----------|----------|-------------------|
    All files |    91.67 |      100 |      100 |    90.91 |                   |
     index.ts |    91.67 |      100 |      100 |    90.91 |                12 |
    ----------|----------|----------|----------|----------|-------------------|
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        20.184s
    

    【讨论】:

      猜你喜欢
      • 2021-01-13
      • 1970-01-01
      • 2018-12-25
      • 2021-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      相关资源
      最近更新 更多