我如何用sinon模拟导入的类定义

问题描述 投票:0回答:1

我似乎无法在我的规范文件中正确模拟导入,并且我想知道是否有人可以看到我所缺少的内容。

这是我的数据库连接的导出类

import Knex from 'knex';
import { merge } from 'lodash';
import knexfile from '../knexfile';

class Database {
  private knexInstance: Knex;
  private config: object;

  connect(options = {}): void {
    if (this.knexInstance) {
      return;
    }
    this.config = merge({}, knexfile, options);
    this.knexInstance = Knex(this.config);
  }

  get query(): Knex {
    if (!this.knexInstance) {
      this.connect();
    }

    return this.knexInstance;
  }

  close(done): void {
    if (!this.knexInstance) {
      done();
      return;
    }

    this.knexInstance.destroy(done);
  }
}

export default new Database();

这里是试图使用数据库文件的操作文件。

import db from '../../database';
const tableName = 'attempts';

export const typeDef = `
  extend type Query {
    attempt(id: String): Attempt!
  }

  extend type Mutation {
    createAttempt(questionId: String!, attemptId: String!, choiceId: String): Attempt
  }

  type Attempt {
    id: String!
    correctanswers: Int!
    userid: String!
    examid: String!
  }
`;

export const resolvers = {
  Query: {
    attempt(_, { id = '' }) {
      return db
        .query(tableName)
        .where({ id })
        .first();
    },
  },
  Mutation: {
    async createAttempt(root, args) {
      const [answer] = await db
        .query(tableName)
        .insert(args)
        .returning('*');

      return answer;
    },
  },
};

这是我的测试文件。

import { createSandbox } from 'sinon';
import { resolvers } from './answer';
import db from '../../database';
import * as should from 'should';

const sandbox = createSandbox();

describe('Answer', () => {
  afterEach(() => sandbox.restore());

  describe('Query Answer', () => {
    it('should return answer by id', async () => {
      const expected = { id: 'xxx' };
      const firstSpy = sandbox.fake.resolves(expected);
      const whereSpy = sandbox.fake.resolves({
        first: firstSpy,
      });

      // This stub never seems to get called. It doesn't look like the import is ever being replaced with the stub in the implementation file.
      const querySpy = sandbox.stub(db, 'query').callsFake(() => {
        return Promise.resolve({
          where: whereSpy,
        });
      });
      const inputId = '100';

      const result = await resolvers.Query.answer(null, { id: inputId });
      sandbox.assert.calledOnce(querySpy);
      sandbox.assert.calledOnce(whereSpy);
      sandbox.assert.calledOnce(firstSpy);
      result.should.deepEqual(expected);
    });
  });
});

[当我运行测试时,导入文件似乎并没有被实现文件中的存根替换,我也不知道为什么。

typescript testing mocha sinon
1个回答
0
投票

有两个警告:

  1. 当您从测试文件和GraphQL解析器文件中的db文件导入database.ts时,它们是不同的实例。因此,即使您在测试文件中存根db实例的方法。解析程序仍将db实例与原始方法一起使用(未存根)。存在潜在的风险。

  2. 在GraphQL解析器中使用依赖项的最佳实践是根据解析器db参数传递依赖项(您的案例的context实例)。因为它是某种依赖注入,所以它使代码更易于测试。

例如

answer.ts

const tableName = "attempts";

export const typeDef = `
  extend type Query {
    attempt(id: String): Attempt!
  }

  extend type Mutation {
    createAttempt(questionId: String!, attemptId: String!, choiceId: String): Attempt
  }

  type Attempt {
    id: String!
    correctanswers: Int!
    userid: String!
    examid: String!
  }
`;

export const resolvers = {
  Query: {
    attempt(_, { id = "" }, { db }) {
      return db
        .query(tableName)
        .where({ id })
        .first();
    },
  },
  Mutation: {
    async createAttempt(root, args, { db }) {
      const [answer] = await db
        .query(tableName)
        .insert(args)
        .returning("*");

      return answer;
    },
  },
};

anwser.test.ts

import sinon from "sinon";
import { resolvers } from "./answer";
import { expect } from "chai";

describe("Answer", () => {
  describe("Query Answer", () => {
    it("should return answer by id", async () => {
      const expected = { id: "xxx" };
      const inputId = "100";

      const knexInstanceStub = {
        query: sinon.stub().returnsThis(),
        where: sinon.stub().returnsThis(),
        first: sinon.stub().resolves(expected),
      };

      const result = await resolvers.Query.attempt(null, { id: inputId }, { db: knexInstanceStub });
      sinon.assert.calledOnce(knexInstanceStub.query);
      sinon.assert.calledOnce(knexInstanceStub.where);
      sinon.assert.calledOnce(knexInstanceStub.first);
      expect(result).to.be.deep.eq(expected);
    });
  });
});

我们甚至不需要导入db并将其存根。我们可以创建一个存根db并将其传递到解析程序的上下文。

带有覆盖率报告的单元测试结果:

  Answer
    Query Answer
      ✓ should return answer by id


  1 passing (11ms)

----------------|----------|----------|----------|----------|-------------------|
File            |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files       |    84.62 |    33.33 |       80 |    86.36 |                   |
 answer.test.ts |      100 |      100 |      100 |      100 |                   |
 answer.ts      |       60 |    33.33 |       50 |     62.5 |          30,31,36 |
----------------|----------|----------|----------|----------|-------------------|
© www.soinside.com 2019 - 2024. All rights reserved.