首页/新闻资讯/正文详情

Keystone 6 测试实战:用 getContext + node:test 为 GraphQL API 编写集成测试

发布时间:2026/9/24 16:38:45 来源:云帆数科 栏目:资讯中心
Keystone 6 测试实战:用 getContext + node:test 为 GraphQL API 编写集成测试
后端【免费下载链接】keystoneThe superpowered headless CMS for Node.js — built with GraphQL and React项目地址https://gitcode.com/gh_mirrors/key/keystone点击查看免费下载导读本指南以仓库中的 examples/testing 示例项目为主线讲解如何为基于 GraphQL 的 Keystone 系统编写自动化测试。你将掌握getContext这一核心测试 API 的用法、如何在测试间重置 SQLite 数据库、如何用context.query/context.db操作数据以及如何通过context.withSession()模拟登录用户来验证访问控制逻辑。读完本文你可以把这套测试模式直接复用到自己的 Keystone 项目中。示例项目概览一个内置认证与访问控制的测试样板examples/testing是 Keystone 仓库中专门用于演示测试能力的示例它建立在withAuth()基于keystone-6/auth的认证包装器示例项目之上其核心目标是用最小的项目结构展示如何对 GraphQL API 编写测试。项目目录结构如下examples/testing/keystone.tsKeystone 配置入口通过createAuth(...)创建withAuth包装器配置 SQLite 数据库与无状态 sessionexamples/testing/schema.ts定义User与Task两个列表Task上带有基于 session 的过滤级访问控制examples/testing/example-test.ts全部测试用例也是本文重点讲解的对象examples/testing/prisma.config.tsPrisma 配置指定 schema 路径、迁移目录与数据库 URLexamples/testing/migrations/20260713000000_init/migration.sql初始数据库迁移examples/testing/package.json项目脚本与依赖。数据模型方面User列表包含name唯一、必填、password必填以及与Task的一对多关系Task列表包含label必填、priority枚举选择、isComplete复选框、assignedTo关联用户与finishBy时间戳。关键的访问控制逻辑位于Task列表的access.filter.update通过isAssignedUserFilter限制只有任务被指派到的用户才能更新该任务见 examples/testing/schema.ts。快速运行从零启动测试项目按照 README 中的说明先在仓库根目录安装依赖然后进入示例目录启动开发服务器# 在仓库根目录 pnpm install # 进入示例目录 cd examples/testing pnpm devpnpm dev会同时启动 Admin UI默认 http://localhost:3000与 GraphQL Playground默认 http://localhost:3000/api/graphql。你可以先在 Admin UI 中创建数据或直接在 GraphQL Playground 中手工执行查询与变更来熟悉数据结构随后再运行自动化测试。运行测试使用仓库提供的脚本见 examples/testing/package.jsontest: node --import tsx --test example-test.ts在示例目录执行pnpm test即可看到基于 Node.js 内置node:test运行器的输出末尾类似✔ Create a User using the Query API (139.404167ms) ✔ Check that trying to create user with no name (required field) fails (96.580875ms) ✔ Check access control by running updateTask as a specific user via context.withSession() (193.86275ms) ℹ tests 3 ℹ suites 0 ℹ pass 3 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 0.072292注意测试命令不需要启动开发服务器测试进程通过getContext直接构造 Keystone Context 并驱动真实的 SQLite 数据库这一点会在下文详细展开。测试基础设施getContext 与数据库重置example-test.ts的开头集中了整套测试基础设施值得逐行拆解import assert from node:assert/strict import { test, beforeEach, afterEach } from node:test import path from node:path import { resetDatabase } from keystone-6/core/testing/sqlite import { getContext } from keystone-6/core/context import config from ./keystone import * as PrismaModule from ./generated/prisma/client const migrationsDirectory path.join(__dirname, migrations) const databaseUrl process.env.DATABASE_URL || file:./keystone-example.db const context getContext(config, PrismaModule) beforeEach(async () { await resetDatabase({ filename: databaseUrl.replace(file:./, ) }, migrationsDirectory) }) afterEach(async () context.prisma.$disconnect())这里有几个关键点1.getContext是测试的核心入口。它接收 Keystone 配置对象与 Prisma Client 模块返回一个完整的KeystoneContext。从源码看getContext在 packages/core/src/lib/system.ts 中实现为先通过createSystem(config)构建系统再调用system.getKeystone(PrismaModule)拿到context并返回。这意味着测试中的 Context 与真实 HTTP 请求共享同一套列表定义、字段解析、访问控制与 hook 管线只是绕过了网络层——这正是它既能贴近真实又足够轻量的原因。2.resetDatabase保证每个测试从干净状态开始。它来自keystone-6/core/testing/sqlite其实现位于 packages/core/src/testing/sqlite.ts若非内存数据库会删除数据库文件及其-shm、-wal附属文件然后新建一个better-sqlite3实例把migrations目录下的迁移脚本逐条执行底层复用applyMigrations。也就是说每个测试用例前都会推倒重建表结构隔离性非常好。beforeEach与afterEach分别负责重置与断开 Prisma 连接避免测试之间相互污染也防止进程因连接未释放而挂起。3. 数据库 URL 可被环境变量覆盖。process.env.DATABASE_URL || file:./keystone-example.db这种写法在 keystone.ts 和 prisma.config.ts 中保持一致方便在 CI 或本地切换到其他数据库。值得一提的是keystone-6/core/testing还提供了 PostgreSQL 与 MySQL 版本的resetDatabase见 packages/core/src/testing/postgresql.ts 与 packages/core/src/testing/mysql.ts说明这套测试模式并不局限于 SQLite。测试用例拆解三层能力的逐步演示用例一用 context.query 创建数据test(Create a User using context.query, async () { const person await context.query.User.createOne({ data: { name: Alice, password: dont-use-me }, query: id name password { isSet }, }) assert.equal(person.name, Alice) assert.equal(person.password.isSet, true) })这个用例展示了context.queryAPI它是一套类型安全的 GraphQL 风格数据操作接口可以像写 GraphQL 一样通过query字符串选择返回字段。值得注意password { isSet }的写法——Keystone 的 password 字段默认不回传明文密码而是通过isSet这样的子选择暴露是否已设置这一信息这在测试认证相关逻辑时非常实用。用例二用 context.db 验证必填校验test(Check that trying to create user with no name (required field) fails, async () { await assert.rejects( async () { await context.db.User.createOne({ data: { password: not-a-password, }, }) }, { message: You provided invalid data for this operation.\n - User.name: value must not be empty, } ) })这里换用了context.dbAPI——与context.query不同context.db是更接近 ORM 风格的操作接口类似直接调用数据库层。用例验证了User.name的必填约束省略name字段会抛出包含精确错误信息的异常assert.rejects同时校验异常类型与 message 内容。对于字段级校验是否生效这类回归测试这种写法既精确又直观。用例三用 withSession 模拟用户验证访问控制第三个用例是整套示例中最有实战价值的场景——验证基于 session 的访问控制是否真的生效test(Check access control by running updateTask as a specific user via context.withSession(), async () { // seed 两个用户用于测试 const [alice, bob] await context.query.User.createMany({ data: [ { name: Alice, password: dont-use-me }, { name: Bob, password: dont-use-me }, ], query: id name, }) // 把任务指派给 Alice const task await context.query.Task.createOne({ data: { label: Experiment with Keystone, priority: high, isComplete: false, assignedTo: { connect: { id: alice.id } }, }, query: id label priority isComplete assignedTo { name }, }) // 匿名无 session更新应被拒绝 await assert.rejects( async () { await context.db.Task.updateOne({ where: { id: task.id }, data: { isComplete: true }, }) }, { message: Access denied: You cannot update that Task - it may not exist } ) // 以 Alice 的 session 更新应成功 { const result await context .withSession({ listKey: User, itemId: alice.id, data: {} }) .db.Task.updateOne({ where: { id: task.id }, data: { isComplete: true }, }) assert.equal(result.id, task.id) } // 以 Bob 的 session 更新应被拒绝 await assert.rejects( async () { await context.withSession({ listKey: User, itemId: bob.id, data: {} }).db.Task.updateOne({ where: { id: task.id }, data: { isComplete: true }, }) }, { message: Access denied: You cannot update that Task - it may not exist } ) })这个用例完整覆盖了三种身份状态匿名状态无 session 时isAssignedUserFilter直接返回false过滤条件使更新被拒绝错误信息为 Access denied: You cannot update that Task - it may not exist出于安全考虑Keystone 对不存在与无权限使用相同的提示合法用户通过context.withSession({ listKey: User, itemId: alice.id, data: {} })手工构造一个以 Alice 身份登录的 Context此时过滤条件匹配assignedTo.id alice.id更新成功非法用户换成 Bob 的 session 后过滤条件不匹配更新再次被拒绝。从源码看withSession是 Context 对象上的方法实现于 packages/core/src/lib/context/createContext.ts它基于当前 Context 构造一个新的 Context并把传入的 session 对象注入其中。这意味着你可以在不经过 HTTP 请求、不真正走登录流程的情况下模拟任意身份的会话状态这对访问控制类测试而言是极其强大的能力。Session 的结构{ listKey, itemId, data }在 schema.ts 中与认证配置保持一致也与withAuth的会话约定兼容。测试策略建议来自官方示例的实践准则README 中给出了几条值得内化为团队规范的测试建议优先聚焦高风险逻辑做单元测试。访问控制Access Control、Hooks、虚拟字段Virtual Fields、自定义 GraphQL 扩展是 Keystone 项目中业务逻辑密度最高、最容易被改坏的地方。官方建议把这些逻辑拆成纯函数直接对函数及其返回值编写单元测试——绝大多数情况下甚至不需要构造 Keystone Context测试成本更低、定位问题更快。用 getContext 做端到端/集成测试。当需要验证某个完整用例或用户流程如本示例中的指派任务→被指派人才能更新时再使用getContext编写集成测试。这样形成单元测试覆盖逻辑、集成测试覆盖流程的分层结构。不要为了测试切换数据库提供商。README 明确指出每个数据库提供商的特性略有差异测试环境与生产环境使用不同的数据库例如生产用 PostgreSQL、测试用 SQLite可能导致测试通过而生产出问题。最稳妥的做法是让测试与生产保持相同的数据库提供商。项目中的真实使用场景这套测试模式并非示例专有仓库自身的测试体系大量采用了相同 API。例如 tests/api-tests/field-groups.test.ts 中多次出现getContext(...)配合临时数据库构造测试环境的写法tests/api-tests目录下还有 hooks、access、relationships 等大量针对字段与关系行为的测试文件tests2/目录中的access.*.test.ts系列则专注于各类访问控制场景。如果你需要参考更复杂、更贴近真实业务的测试组织方式这些目录是很好的后续阅读材料。总结examples/testing示例展示了一条清晰、低成本的 Keystone 测试路径用getContext在测试进程内构造完整 Context用resetDatabase在用例之间重置数据库用context.query/context.db操作数据用context.withSession()模拟登录身份来验证访问控制。这套模式与 Node.js 内置的node:test运行器配合良好无需引入额外的测试框架即可为 GraphQL API 提供可靠的回归保障。赞分享后端【免费下载链接】keystoneThe superpowered headless CMS for Node.js — built with GraphQL and React项目地址https://gitcode.com/gh_mirrors/key/keystone点击查看免费下载相关推荐Keystone 6 测试指南使用 keystone-6/core/testing 与 Vitest 验证 GraphQL API 行为Keystone 6 测试指南使用 keystone 6/core/testing 与 Vitest 验证 GraphQL API 行为 本指南以 Keys后端用 Jest 为 PostGraphile V5 编写数据库与 GraphQL 集成测试事务化测试模式与 Grafast 测试助手实战用 Jest 为 PostGraphile V5 编写数据库与 GraphQL 集成测试事务化测试模式与 Grafast 测试助手实战 本文是 PostGra后端API网关Keystone Admin UI 集成测试指南从 Playwright 测试编写到 CI 接入Keystone Admin UI 集成测试指南从 Playwright 测试编写到 CI 接入 本指南基于 Keystone monorepo 中的 tes后端上一篇GetQzonehistory终极指南5分钟免费备份你的QQ空间所有历史记录下一篇GetQzonehistory终极QQ空间历史数据完整备份解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Dart 分析服务器代码补全(Code Completion)实现指南:从请求处理到候选排序的完整链路
Dart 分析服务器代码补全(Code Completion)实现指南:从请求处理到候选排序的完整链路

Dart 分析服务器代码补全(Code Completion)实现指南:从请求处理到候选排序的完整链路 【免费下载链接】sdk The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more. 项目地址: https://gitcode.com/… · 2026/9/24 16:38:45

Whisper Windows 移植版:基于 DirectCompute 的高性能 GPGPU 推理指南
Whisper Windows 移植版:基于 DirectCompute 的高性能 GPGPU 推理指南

人工智能语音音频本地部署桌面应用 【免费下载链接】Whisper High-performance GPGPU inference of OpenAIs Whisper automatic speech recognition (ASR) model 项目地址: https://gitcode.com/gh_mirrors/wh/Whisper 点击查看 免费下载 本指南以仓库根目录 Readm… · 2026/9/24 16:38:45

B码在无人机,电力领域的重要性(B码解码)
B码在无人机,电力领域的重要性(B码解码)

此图来自成都云智优创科技有限公司www.iyzyc.cnRIG-B解码模块简介IRIG-B解码核心板是专门用于对IRIG-B码进行解码的模块,解码后会生成同步秒脉冲,同时从TTL串口输出时间报文,用于对设备进行校时。模块具有处理速度快,输出精度高的… · 2026/9/24 16:38:19

LiteRT-LM 完整指南:把大语言模型推理搬到手机、电脑和边缘设备
LiteRT-LM 完整指南:把大语言模型推理搬到手机、电脑和边缘设备

LiteRT-LM 完整指南:把大语言模型推理搬到手机、电脑和边缘设备 【免费下载链接】LiteRT-LM LiteRT-LM is Googles production-ready, high-performance, open-source inference framework for deploying Large Language Models on edge devices. 项目地址: https… · 2026/9/24 17:10:01

PocketFlow 向量数据库实战指南:7 大主流向量检索方案选型对比与 Python 接入代码
PocketFlow 向量数据库实战指南:7 大主流向量检索方案选型对比与 Python 接入代码

人工智能大模型AI Agent工作流自动化RAG 【免费下载链接】PocketFlow Pocket Flow: 100-line LLM framework. Let Agents build Agents! 项目地址: https://gitcode.com/gh_mirrors/poc/PocketFlow 点击查看 免费下载 导读 在 LLM 应用中,向量检索是 R… · 2026/9/24 17:10:01

2026年成都GEO服务商横评:五类机构的商业模式、能力边界与适配场景
2026年成都GEO服务商横评:五类机构的商业模式、能力边界与适配场景

打开豆包或者DeepSeek,问一句"成都哪家做品牌营销比较靠谱",回答里出现的几个名字,正在影响一批企业的采购判断。对成都企业来说,问题已经不是要不要做GEO,而是在五种不同形态的服务机构之间,怎么… · 2026/9/24 17:09:55

PyMuPDF 版本变更日志全解析:从 1.9.1 到 1.28.2 的功能演进与升级决策指南
PyMuPDF 版本变更日志全解析:从 1.9.1 到 1.28.2 的功能演进与升级决策指南

图像处理 【免费下载链接】PyMuPDF PyMuPDF is a high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents. 项目地址: https://gitcode.com/gh_mirrors/py/PyMuPDF 点击查看 免费下载 PyMuP… · 2026/9/24 17:09:49

【主流移动端 GIS 产品与 AI 相结合】
【主流移动端 GIS 产品与 AI 相结合】

有人说:一个人从1岁活到80岁很平凡,但如果从80岁倒着活,那么一半以上的人都可能不凡。 生活没有捷径,我们踩过的坑都成为了生活的经验,这些经验越早知道,你要走的弯路就会越少。 · 2026/9/24 17:09:49

doccano 常见问题排查与运维实战指南:用户管理、数据导入、端口升级与 CSRF 排错
doccano 常见问题排查与运维实战指南:用户管理、数据导入、端口升级与 CSRF 排错

数据标注后端前端 【免费下载链接】doccano Open source annotation tool for machine learning practitioners. 项目地址: https://gitcode.com/gh_mirrors/do/doccano 点击查看 免费下载 本文是 doccano(开源机器学习标注工具)官方 FAQ 的… · 2026/9/24 17:09:42

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码