当前位置: 首页 > 知识库问答 >
问题:

用Jest模拟mysql连接

从智志
2023-03-14

尝试测试如下所示的代码:

const mysql = require('mysql2/promise');

async myFunction () {

    const db = await mysql.createConnection(options);

    const results = await db.execute('SELECT `something` from `table`;');

    await db.end();
    
    // more code ...

}

我需要模拟mysql连接,使我能够使用它返回的任何内容来模拟对execute函数的调用。

我曾尝试对整个mysql2/promise模块进行模拟,但当然没有起作用,因为被模拟的CreateConnection没有返回任何可以调用execute函数的内容。我还尝试只模仿我需要的这3个函数,而不是模仿整个模块,类似于:

jest.mock('mysql2/promise', () => ({
    createConnection: jest.fn(() => ({
        execute: jest.fn(),
        end: jest.fn(),
    })),
}));

但这也不起作用。如有任何建议,我们将不胜感激。

共有1个答案

邢宏浚
2023-03-14

我会用不同的方法来处理这个问题。当您觉得需要模拟整个第三方库进行测试时,您的应用程序中出现了一些问题。

作为一般的最佳实践,您应该始终包装第三方库。首先,请查看本文的讨论。

基本上,我们的想法是定义您自己的接口以实现所需的功能,然后使用第三方库实现这些接口。在代码的其余部分中,您将只针对接口工作,而不针对第三方实现。

这有几个优点

  1. 您可以自己定义接口。它通常比整个第三方库要小得多,因为您很少使用第三方库的所有功能,您可以为您的具体用例决定什么是最好的接口定义,而不必完全遵循某个库作者的命令。
  2. 如果有一天您决定不想再使用MySQL而要转到Mongo,您可以编写一个DB接口的Mongo实现。
  3. 在您的情况下,最重要的是:您可以轻松地创建DB接口的模拟实现,而不必开始模拟整个第三方API.

那么这怎么能行得通呢?

首先,定义一个接口,因为它在代码中是最有用的。您的DB接口可能如下所示:

interface Database<T> {
  create(object: T): Promise<void>;
  get(id: string): Promise<T>;
  getAll(): Promise<T[]>;
  update(id: string, object: T): Promise<void>;
  delete(id: string): Promise<void>;
}

现在,您可以针对这个database接口开发整个代码库。当您需要从'table'中检索数据时,您可以使用database实现,而不是在整个代码中编写MySQL查询。

这里我只举一个例子ResultRetriever,它非常原始,但也很有用:

class ResultRetriever {
  
    constructor(private database: Database<Something>) {}

    getResults(): Promise<Something[]> {
        return this.database.getAll();
    }
  
}

正如您所看到的,您的代码不需要关心哪个DB实现交付数据。另外,我们在这里反转了依赖关系:ResultReteriver注入了它的Database实例。它不知道它获得的是哪个conrete数据库实现。不需要。它所关心的只是它是一个有效的。

现在可以轻松实现MySQLDatabase类:

class MySqlDatabase<T> implements Database<T> {

  create(object: T): Promise<void> {...}

  get(id: string): Promise<T> {...}

  getAll(): Promise<T[]> {
      const db = await mysql.createConnection(options);
      const results = await db.execute('SELECT `something` from `table`;');
      await db.end();
      return results;
  }

  update(id: string, object: T): Promise<void> {...}

  delete(id: string): Promise<void> {...}

}

现在,我们已经从主代码库中完全抽象出了MySQL特定的实现。当涉及到测试时,您可以编写一个简单的MockDatabase:

export class MockDatabase<T> implements Database<T> {

  private objects: T[] = [];

  async create(object: T): Promise<void> {
    this.objects.push(object);
  }

  async get(id: string): Promise<T> {
    return this.objects.find(o => o.id === id);
  }

  async getAll(): Promise<T[]> {
    return this.objects;
  }

  update(id: string, object: T): Promise<void> {...}

  delete(id: string): Promise<void> {...}
  
}

当涉及到测试时,您现在可以使用MockDatabase来测试ResultRieve,而不是依赖于MySQL库并因此完全模拟它:

describe('ResultRetriever', () => {

    let retriever: ResultRetriever;
    let db: Database;

    beforeEach(() => {
      db = new MockDatabase();
      retriever = new ResultRetriever(db);
    });

    ...

});

如果我超出了问题的范围,我很抱歉,但是我觉得仅仅回应如何模仿MySQL库并不能解决底层的架构问题。

如果您不使用/不想使用TypeScript,可以将相同的逻辑应用于JavaScript。

 类似资料:
  • 问题内容: 我的测试目标中当前已导入: 并在我的相同测试目标中使用它: 在测试中,我正在做以下模拟尖锐函数的操作: 但我得到: 有没有一种方法可以使用带有Jest的功能模拟所有Sharp模块功能? 问题答案: 您需要像这样模拟它: 首先,您需要返回function而不是对象,因为您需要调用。该函数调用将返回带有键的对象,该键包含另一个函数,依此类推。 要测试每个功能,您需要为每个功能创建一个间谍。

  • 问题内容: 我开始认为这是不可能的,但是无论如何我都想问。 我想测试我的一个ES6模块以特定方式调用另一个ES6模块。有了茉莉花,这非常容易- 应用程式码: 和测试代码: 笑话相当于什么?我觉得这是一件很想做的简单的事,但是我一直在努力尝试弄清头发。 我最接近的是将s 替换为s,并将其移入测试/函数中。都不是我想做的事情。 为了获得加分,我希望在其中的功能为默认导出时使整个工作正常进行。但是,我知

  • 我想对我的服务进行单元测试。在我的服务中,我有一个构造函数,它是: ContractService.ts 我的模型看起来是这样的:(模型是来自sequelize-typescript的类) 所以我想用JEST创建我的单元测试。当我试图模仿contractModel时,它找不到方法,即使我试图模仿它。 我在想,怎样才是嘲弄这个合同模型的正确方法。

  • 我试图为这样的函数编写一个单元测试: 为了测试这段代码,我需要模拟服务,因为它调用类外的函数,但问题是它是私有的。 我该如何用玩笑来模拟一个私有变量呢?这个类创建了它自己的实例,所以甚至可以模仿它吗?

  • 问题内容: 模拟按钮单击似乎是非常简单/标准的操作。但是,我无法在Jest.js测试中使用它。 这是我尝试过的(也可以使用jquery进行此操作),但似乎没有触发任何操作: 问题答案: #1使用笑话 这就是我使用笑话嘲笑回调函数来测试click事件的方式 我还使用了一个称为酶的模块 酶是一种测试实用程序,可让您更轻松地断言和选择您的React组件 #2使用Sinon 另外,您可以使用另一个称为si

  • 问题内容: 我正在使用ref为组件编写测试。我想模拟ref元素并更改一些属性,但不知道如何做。有什么建议? 问题答案: 根据https://github.com/airbnb/enzyme/issues/1937中的讨论,这就是解决方案 可以使用非箭头函数对类进行猴子修补,其中“ this”关键字将传递到正确的作用域。

  • 问题内容: 我有一个React组件,其中包含一些其他组件,这些组件依赖于对Redux存储等的访问,这会在执行完整的酶安装时引起问题。假设这样的结构: 我想使用Jest的方法来模拟子组件,因此测试无需担心。 我知道我可以通过以下方式模拟出一个简单的组件: 但是,由于该组件通常会接收道具,因此React会感到不安,并发出有关将未知道具(在本例中为)传递给的警告。 我试图返回一个函数,但是由于悬挂了,所

  • 我正在开发一个React应用程序,我想测试一个模块,我们称之为B,这取决于另一个模块,我们称之为a。 场景可能是这样的: 测试我的组件的核心库是Jest和Ezyme。我的目标是测试模块B,但我想单独测试它,所以我想模拟对模块A的依赖。js。 我知道一种方法是注入helperFn作为道具,而不是导入它,这样在测试期间我就可以注入一个模拟函数,但是这个应用程序上有很大的模块,每个模块都有一些依赖关系。