如何用Jest模拟Sequelize?

时间:2020-11-02 15:42:31

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

我正在尝试编写代码的单元测试,这些代码调用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的调用。

>

1 个答案:

答案 0 :(得分:0)

我将使用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