ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Express 数据库操作测试实战:独立测试库、环境变量切换与数据隔离方案

Express 数据库操作测试实战:独立测试库、环境变量切换与数据隔离方案 Express 数据库操作测试实战独立测试库、环境变量切换与数据隔离方案【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum本文围绕《Testing Database Operations》一课展开讲解在 Express 服务中测试涉及数据库的代码时如何通过独立测试数据库、NODE_ENV驱动的连接串切换、beforeEach数据重置与--runInBand串行执行构建安全可靠的单元测试与集成测试体系。读完本文你将掌握一套可直接复用的数据库测试环境搭建方案并理解其背后的设计动机与实现细节。为什么数据库测试的准备工作更复杂当被测试的代码需要触碰数据库时测试前的准备工作会明显变多。最直接的原因是绝对不能在生产数据库上运行测试代码否则一旦测试数据写入、修改或删除生产记录就可能破坏真实用户的数据。因此测试环境的搭建核心要解决两件事一是把测试流量导向一个隔离的数据库二是保证测试之间互不影响。在深入方案之前先明确本文的两个知识目标在 Express 服务器语境下理解单元测试与集成测试的边界创建并使用一个独立的数据库来完成集成测试。单元测试数据库操作真的需要测吗动手之前先冷静评估一下你正在测试的数据库操作是否真的值得写单元测试如果你只是通过pgnode-postgres或其他数据库模块直接读写数据那么这部分代码通常不需要再写单元测试。原因在于pg以及绝大多数流行的数据库驱动模块本身已经为其所有操作编写了大量测试如果你只是在 JSON API 中调用另一个模块提供的函数那么这些操作的可靠性已经被上游覆盖了。这个课程的作业也建议你抽空浏览一下pg仓库的tests目录看看这些被广泛使用的库测试得有多充分。那什么时候值得为数据库操作补充单元测试呢主要看两点查询逻辑足够复杂时值得通过单元测试确认你正确使用了 API并验证你写的代码确实在做你预期的事你用自己的代码对数据做了过滤、排序或其他加工时这部分自定义逻辑非常值得测试。不过对于你自己的加工逻辑更好的做法是把它从数据库操作中抽离出来放到独立的模块里。这样你就可以在不触碰数据库的情况下直接测试纯函数既快又稳这也是依赖注入和纯函数思想在测试中的典型应用。集成测试为 Express 服务准备独立测试数据库有些场景则必须让测试真正触达数据库比如在上一篇《Testing Routes and Controllers》课程中我们用supertest对服务器做了集成测试——请求会穿过路由、控制器最终落到数据层这种端到端的链路只有真实数据库才能验证。为此需要建立一套独立的测试数据库方案步骤如下创建一个独立测试库为它加上test_前缀方便一眼识别。例如开发库叫inventory_application测试库就叫test_inventory_application。迁移表结构通过替换数据库 URL 指向测试库对测试库执行prisma migrate如果你使用 Prisma ORM迁移记录会保存在代码库的migrations目录中相关原理可参考 Prisma ORM 课程。准备初始数据可以对该测试库运行一个 seed 脚本填充基础数据也可以在测试套件里用beforeAll手动插入数据。在上一门课程中完成的 Inventory Application 项目一个包含分类与商品 CRUD 的库存管理应用正是本文示例数据库名inventory_application的由来你可以拿这个项目的表结构作为实践对象。环境变量驱动开发库与测试库自动切换要让同一份代码在不同环境下连接不同的数据库最优雅的方式是借助环境变量。首先在.env文件中同时声明开发库与测试库NODE_ENVdevelopment DATABASE_URLpostgresql://user:passwordlocalhost:5432/inventory_application TEST_DATABASE_URLpostgresql://user:passwordlocalhost:5432/test_inventory_application关于环境变量的背景知识为什么用它存敏感配置、为什么.env要加入.gitignore、UPPER_SNAKE_CASE命名约定等可参考 Environment Variables 课程。随后在package.json中配置相应的 npm 脚本{ // other stuff scripts: { dev: node app.js, test: jest }, // even more stuff }接下来在代码中根据NODE_ENV的值程序化地切换数据库连接串。下面是 Prisma 结合 PrismaPg 驱动适配器的写法const connectionString process.env.NODE_ENV test ? process.env.TEST_DATABASE_URL : process.env.DATABASE_URL; const adapter new PrismaPg({ connectionString }); const prisma new PrismaClient({ adapter });这样应用在开发环境运行npm run dev时连接开发库在npm test测试环境下则自动指向隔离的测试库全程无需改动业务代码。在 Jest 中加载环境变量app.js可以通过node --env-file.env app.js或node --env-file-if-exists.env app.js这类命令行方式加载.envNode 原生支持详见 Environment Variables 课程但Jest 环境下无法使用--env-file或--env-file-if-exists因为测试时并不是你直接调用node。此时必须改用process.loadEnvFile()在代码中程序化地加载环境变量典型位置是Jest 的全局 setup 文件对应 Jest 配置项globalSetup。例如// jest.global-setup.js process.loadEnvFile(); // 将 .env 中的变量加载进测试进程值得庆幸的是Jest 默认会把NODE_ENV设置为test这会覆盖.env文件里的NODE_ENVdevelopment所以你不需要为环境判断做任何额外的特殊处理——上一节中基于NODE_ENV test的三元表达式会自动生效。这种框架替你设好环境标记的行为恰好让测试环境必然连测试库成为一个强约束。测试之间的数据库重置与数据隔离集成测试的关键纪律是任何测试都不能依赖其他测试的执行结果。设想一个场景——某个测试没有增删某个表的数据另一个测试却因此失败而真正被测的行为本身是正常的这就是测试耦合造成的假失败。因此只要测试在读、建、改、删数据库记录就应该在下一个测试开始前把数据库重置回初始状态。最方便的做法是放在beforeEach钩子里。假设数据库中有users和projects两张表beforeEach(async () { await prisma.$transaction([ prisma.user.deleteMany(), prisma.project.deleteMany(), ]); });这段代码在每个测试运行之前清空users与projects表保证每个测试都从干净、已知的状态开始。值得注意的细节是这里用prisma.$transaction把两个deleteMany包裹在同一个事务里确保清空操作要么全部成功、要么全部回滚避免出现只清了一半表的中间状态。deleteMany是 Prisma Client 的批量删除方法Prisma Client 会根据schema.prisma中的模型定义自动生成对应模型的增删改查 API详见 Prisma ORM 课程。如果你用 seed 脚本预先填充了基础数据例如供所有测试共享的参考数据重置逻辑则应调整为恢复到 seed 后的状态而不是简单清空——这通常意味着在beforeAll中跑一次 seed再在beforeEach中只清空那些被测试写入的数据。多测试文件的串行执行--runInBand数据隔离还有一个容易被忽视的维度测试文件之间的执行顺序。如果测试被拆分到多个文件就必须让测试运行器按顺序一个文件接着一个文件执行而不是并行执行否则不同测试文件的数据库操作会混在一起、互相干扰。由于Jest 默认按文件并行执行你需要在package.json的test脚本中加入--runInBand标志来强制串行{ scripts: { test: jest --runInBand } }--runInBand会让 Jest 在当前进程中按顺序运行所有测试文件虽然整体耗时可能变长但对于共享同一个测试数据库的集成测试来说这是保证结果确定性的必要代价。至此一套完整的数据库测试环境就搭建完毕了环节方案作用隔离环境TEST_DATABASE_URLtest_前缀测试流量与开发/生产数据物理隔离结构准备对测试库执行prisma migrate/ seed 脚本让测试库具备与开发库一致的表结构连接切换NODE_ENV test三元表达式同一份代码自动选择正确数据库变量加载process.loadEnvFile() JestglobalSetup绕过 Jest 无法使用--env-file的限制数据隔离beforeEach$transactiondeleteMany每个测试从干净状态开始串行执行jest --runInBand避免多文件并行带来的数据竞争知识自检什么时候才值得为数据库操作编写单元测试什么情况下可以直接依赖数据库驱动模块自身的测试覆盖如何为集成测试创建并使用独立的测试数据库为什么 Jest 环境无法使用--env-fileprocess.loadEnvFile()应该放在哪里执行beforeEach中为什么要用$transaction包裹deleteMany多测试文件并行执行会带来什么风险--runInBand如何解决如果想继续深入建议先阅读上一课 Testing Routes and Controllerssupertest 路由/控制器测试了解如何用supertest构造对 Express 应用的 HTTP 级测试再结合 Environment Variables 与 Prisma ORM 两课补齐环境变量与 ORM 原理最后在你的 Inventory Application 项目 中亲手实践这套测试数据库方案。【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表