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:44 阅读更多 →
AWS SDK for C++ 跨服务示例全解析:从 Aurora Serverless 任务追踪器到 SNS/SQS 发布订阅

AWS SDK for C++ 跨服务示例全解析:从 Aurora Serverless 任务追踪器到 SNS/SQS 发布订阅

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地…

2026/9/24 16:37:44 阅读更多 →

最新新闻

基于 Java Spring Boot 的灾害应急救援平台设计与实现

基于 Java Spring Boot 的灾害应急救援平台设计与实现

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 1. 引言 随着自然灾害和突发公共事件的频发,传统应急救援模式在信息传递、资源调度和协同指挥等方面暴露出响应慢、信息孤岛、资源调配不透明等问题。本文基…

2026/9/24 17:23:28 阅读更多 →
计算机毕业设计之基于springboot➕vue的旅游信息推荐系统

计算机毕业设计之基于springboot➕vue的旅游信息推荐系统

本系统为用户而设计制作旅游信息推荐系统,旨在实现旅游信息推荐智能化、现代化管理。本旅游信息推荐管理自动化系统的开发和研制的最终目的是将旅游信息推荐的运作模式从手工记录数据转变为网络信息查询管理,从而为现代管理人员的使用提供更多的便利和条…

2026/9/24 17:23:27 阅读更多 →
权限提升(提权)从入门到精通:为什么“低权限Shell“也能翻盘

权限提升(提权)从入门到精通:为什么“低权限Shell“也能翻盘

黑客打进一台服务器,只拿到普通用户权限——然后呢?游戏才刚刚开始。 提权(Privilege Escalation):从低权限"升"到高权限——普通用户→管理员→系统/域管。 它是渗透测试的"临门一脚"、红队必备、…

2026/9/24 17:23:27 阅读更多 →
Linux运维踩坑实录:压缩文件夹报错“zip error: Nothing to do!”

Linux运维踩坑实录:压缩文件夹报错“zip error: Nothing to do!”

Linux运维踩坑实录:压缩文件夹报错“zip error: Nothing to do!”的深度剖析与最佳实践 引言:文件打包,运维与开发的必经之路 在当今的软件开发和系统运维领域,Linux 操作系统凭借其卓越的稳定性和强大的命令行工具生态&#xff…

2026/9/24 17:23:27 阅读更多 →
攻克 Open Event Theme 9大技术难题:从安装到定制的全方位解决方案

攻克 Open Event Theme 9大技术难题:从安装到定制的全方位解决方案

攻克 Open Event Theme 9大技术难题:从安装到定制的全方位解决方案 【免费下载链接】open-event-theme Open Event Standard Theme http://next.eventyay.com 项目地址: https://gitcode.com/gh_mirrors/op/open-event-theme 你是否在使用 Open Event Theme …

2026/9/24 17:23:27 阅读更多 →
bpftrace 贡献指南:从编写工具、RFC 提案到代码合入的完整实践

bpftrace 贡献指南:从编写工具、RFC 提案到代码合入的完整实践

可观测性性能剖析eBPF 【免费下载链接】bpftrace High-level tracing language for Linux 项目地址: https://gitcode.com/gh_mirrors/bp/bpftrace 点击查看 免费下载 bpftrace 是一个面向 Linux 的高层跟踪语言,致力于让开发者用极简的单行命令快速编写…

2026/9/24 17:22:27 阅读更多 →

日新闻

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

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

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

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →