Как имитировать продолжение с помощью Jest?

#typescript #unit-testing #jestjs #mocking #sequelize.js

#typescript #модульное тестирование #jestjs #издевательство #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, потому что оно не является функцией; вместо этого задано undefined.

 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:

Я собираюсь использовать 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