mysql - 用 Jest 模拟 mysql 连接

标签 mysql node.js unit-testing jestjs

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

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 ...

}
我需要以一种允许我使用它返回的任何内容来模拟对 execute 的调用的方式来模拟 mysql 连接。功能。
我试过 mock 整个 mysql2/promise模块,但当然这不起作用,因为被 mock 的 createConnection没有返回任何可以调用 execute 的东西功能。
我还尝试只模拟我需要的这 3 个函数,而不是模拟整个模块,例如:
jest.mock('mysql2/promise', () => ({
    createConnection: jest.fn(() => ({
        execute: jest.fn(),
        end: jest.fn(),
    })),
}));
但这也不起作用。
任何建议都非常感谢。

最佳答案

我会以不同的方式处理这个问题。当你觉得你需要模拟整个第三方库进行测试时,你的应用程序中出现了一些问题。
作为一般的最佳实践,您应该始终包装第三方库。退房 this discussion对于初学者。
基本上的想法是定义您自己的接口(interface)到所需的功能,然后使用第三方库实现这些接口(interface)。在您的其余代码中,您将只针对接口(interface)工作,而不是针对第三方实现。
这有几个优点

  • 您可以自己定义接口(interface)。它通常比整个第三方库小得多,因为您很少使用该第三方库的所有功能,并且您可以决定什么是具体用例的最佳接口(interface)定义,而不必完全遵循某些图书馆作者决定你。
  • 如果有一天您决定不想再使用 MySQL 而是转而使用 Mongo,那么您只需编写 DB 接口(interface)的 Mongo 实现即可。
  • 就您而言,最重要的是:您可以轻松创建数据库接口(interface)的模拟实现,而无需开始模拟整个第三方 API。

  • 那么这怎么可能呢?
    首先,定义一个接口(interface),因为它在您的代码中最有用。也许,您的数据库接口(interface)可能如下所示:
    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界面。当您需要从“表”中检索数据时,您可以使用 Database,而不是在整个代码中编写 MySQL 查询。执行。
    我就举个例子ResultRetriever这里很原始,但达到了目的:
    class ResultRetriever {
      
        constructor(private database: Database<Something>) {}
    
        getResults(): Promise<Something[]> {
            return this.database.getAll();
        }
      
    }
    
    如您所见,您的代码不需要关心哪个 DB 实现提供了数据。另外,我们inverted dependencies这里:ResultReteriver注入(inject) 它的 Database实例。它不知 Prop 体是哪个 Database它得到的实现。它不需要。它所关心的只是它是一个有效的。
    您现在可以轻松实现 MySQL Database类(class):
    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> {...}
      
    }
    
    说到测试,您现在可以测试您的 ResultRetrieve使用您的 MockDatabase而不是依赖 MySQL 库,因此完全模拟它:
    describe('ResultRetriever', () => {
    
        let retriever: ResultRetriever;
        let db: Database;
    
        beforeEach(() => {
          db = new MockDatabase();
          retriever = new ResultRetriever(db);
        });
    
        ...
    
    });
    

    如果我有点超出了问题的范围,我很抱歉,但我觉得仅仅回答如何模拟 MySQL 库并不能解决底层的架构问题。
    如果您不使用/不想使用 TypeScript,则可以将相同的逻辑应用于 JavaScript。

    关于mysql - 用 Jest 模拟 mysql 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63596277/

    相关文章:

    php - 如何在 PHP 数据库中存储 2.00 格式的值?

    mysql - 无法理解 koa 和 nodejs 中 promise 的返回

    javascript - Package.json在打包分发中的作用

    node.js - 如何使用 mocha 测试 node.js websocket 服务器?

    angular - NgRx 测试 - 检查分派(dispatch) Action 的顺序

    php - 将 codeigniter 站点集成为 wordpress 插件

    mysql - 是否有数据库约束可以检查插入的父行始终至少有一个子行

    node.js - Nodejs 的独立模块?

    c# - 如何在 C# 中扩展静态类

    php - 使用层次结构管理用户权限