[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-forwardemail--supertest":3,"tool-forwardemail--supertest":61},[4,18,26,36,44,53],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":10,"last_commit_at":24,"category_tags":25,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":27,"name":28,"github_repo":29,"description_zh":30,"stars":31,"difficulty_score":32,"last_commit_at":33,"category_tags":34,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",143909,2,"2026-04-07T11:33:18",[14,13,35],"语言模型",{"id":37,"name":38,"github_repo":39,"description_zh":40,"stars":41,"difficulty_score":32,"last_commit_at":42,"category_tags":43,"status":17},2271,"ComfyUI","Comfy-Org\u002FComfyUI","ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎，专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式，采用直观的节点式流程图界面，让用户通过连接不同的功能模块即可构建个性化的生成管线。\n\n这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景，也能自由组合模型、调整参数并实时预览效果，轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性，不仅支持 Windows、macOS 和 Linux 全平台，还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构，并率先支持 SDXL、Flux、SD3 等前沿模型。\n\n无论是希望深入探索算法潜力的研究人员和开发者，还是追求极致创作自由度的设计师与资深 AI 绘画爱好者，ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能，使其成为当前最灵活、生态最丰富的开源扩散模型工具之一，帮助用户将创意高效转化为现实。",107888,"2026-04-06T11:32:50",[14,15,13],{"id":45,"name":46,"github_repo":47,"description_zh":48,"stars":49,"difficulty_score":32,"last_commit_at":50,"category_tags":51,"status":17},4721,"markitdown","microsoft\u002Fmarkitdown","MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具，专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片（含 OCR）、音频（含语音转录）、HTML 乃至 YouTube 链接等多种格式的解析，能够精准提取文档中的标题、列表、表格和链接等关键结构信息。\n\n在人工智能应用日益普及的今天，大语言模型（LLM）虽擅长处理文本，却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点，它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式，成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外，它还提供了 MCP（模型上下文协议）服务器，可无缝集成到 Claude Desktop 等 LLM 应用中。\n\n这款工具特别适合开发者、数据科学家及 AI 研究人员使用，尤其是那些需要构建文档检索增强生成（RAG）系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性，但其核心优势在于为机器",93400,"2026-04-06T19:52:38",[52,14],"插件",{"id":54,"name":55,"github_repo":56,"description_zh":57,"stars":58,"difficulty_score":10,"last_commit_at":59,"category_tags":60,"status":17},4487,"LLMs-from-scratch","rasbt\u002FLLMs-from-scratch","LLMs-from-scratch 是一个基于 PyTorch 的开源教育项目，旨在引导用户从零开始一步步构建一个类似 ChatGPT 的大型语言模型（LLM）。它不仅是同名技术著作的官方代码库，更提供了一套完整的实践方案，涵盖模型开发、预训练及微调的全过程。\n\n该项目主要解决了大模型领域“黑盒化”的学习痛点。许多开发者虽能调用现成模型，却难以深入理解其内部架构与训练机制。通过亲手编写每一行核心代码，用户能够透彻掌握 Transformer 架构、注意力机制等关键原理，从而真正理解大模型是如何“思考”的。此外，项目还包含了加载大型预训练权重进行微调的代码，帮助用户将理论知识延伸至实际应用。\n\nLLMs-from-scratch 特别适合希望深入底层原理的 AI 开发者、研究人员以及计算机专业的学生。对于不满足于仅使用 API，而是渴望探究模型构建细节的技术人员而言，这是极佳的学习资源。其独特的技术亮点在于“循序渐进”的教学设计：将复杂的系统工程拆解为清晰的步骤，配合详细的图表与示例，让构建一个虽小但功能完备的大模型变得触手可及。无论你是想夯实理论基础，还是为未来研发更大规模的模型做准备",90106,"2026-04-06T11:19:32",[35,15,13,14],{"id":62,"github_repo":63,"name":64,"description_en":65,"description_zh":66,"ai_summary_zh":67,"readme_en":68,"readme_zh":69,"quickstart_zh":70,"use_case_zh":71,"hero_image_url":72,"owner_login":73,"owner_name":74,"owner_avatar_url":75,"owner_bio":76,"owner_company":77,"owner_location":77,"owner_email":78,"owner_twitter":77,"owner_website":79,"owner_url":80,"languages":81,"stars":86,"forks":87,"last_commit_at":88,"license":89,"difficulty_score":90,"env_os":91,"env_gpu":92,"env_ram":91,"env_deps":93,"category_tags":101,"github_topics":102,"view_count":32,"oss_zip_url":77,"oss_zip_packed_at":77,"status":17,"created_at":105,"updated_at":106,"faqs":107,"releases":136},5027,"forwardemail\u002Fsupertest","supertest","🕷 Super-agent driven library for testing node.js HTTP servers using a fluent API.   Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs.","supertest 是一款专为 Node.js 开发者设计的 HTTP 服务器测试工具。它基于强大的 superagent 库构建，旨在通过流畅的链式 API 让后端接口测试变得简单直观。\n\n在开发 Node.js 应用时，验证 API 响应状态、头部信息及数据格式往往需要繁琐的配置。supertest 完美解决了这一痛点：它允许开发者直接传入 Express 等框架的应用实例或 HTTP 服务函数，无需手动启动服务器或管理临时端口，即可自动完成请求发送与断言验证。无论是检查 JSON 内容类型、验证状态码，还是处理 HTTP\u002F2 协议及身份认证场景，supertest 都能轻松应对。\n\n该工具特别适合后端工程师、全栈开发者以及需要进行自动化接口测试的技术人员。其独特亮点在于高度的灵活性：既提供了高层级的抽象方法以简化常见测试用例，又保留了底层 superagent 的全部能力，方便用户在需要时进行深度定制。此外，supertest 能与 Mocha、Jest 等主流测试框架无缝集成，支持异步回调处理，帮助团队高效构建稳定的持续集成流程，是保障 Node.js 服务端代码质量的得力助手","supertest 是一款专为 Node.js 开发者设计的 HTTP 服务器测试工具。它基于强大的 superagent 库构建，旨在通过流畅的链式 API 让后端接口测试变得简单直观。\n\n在开发 Node.js 应用时，验证 API 响应状态、头部信息及数据格式往往需要繁琐的配置。supertest 完美解决了这一痛点：它允许开发者直接传入 Express 等框架的应用实例或 HTTP 服务函数，无需手动启动服务器或管理临时端口，即可自动完成请求发送与断言验证。无论是检查 JSON 内容类型、验证状态码，还是处理 HTTP\u002F2 协议及身份认证场景，supertest 都能轻松应对。\n\n该工具特别适合后端工程师、全栈开发者以及需要进行自动化接口测试的技术人员。其独特亮点在于高度的灵活性：既提供了高层级的抽象方法以简化常见测试用例，又保留了底层 superagent 的全部能力，方便用户在需要时进行深度定制。此外，supertest 能与 Mocha、Jest 等主流测试框架无缝集成，支持异步回调处理，帮助团队高效构建稳定的持续集成流程，是保障 Node.js 服务端代码质量的得力助手。","# [supertest](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F)\n\n[![build status](https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Factions\u002Fworkflows\u002Fci.yml)\n[![code coverage](https:\u002F\u002Fimg.shields.io\u002Fcodecov\u002Fc\u002Fgithub\u002Fladjs\u002Fsupertest.svg)](https:\u002F\u002Fcodecov.io\u002Fgh\u002Fladjs\u002Fsupertest)\n[![code style](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fcode_style-XO-5ed9c7.svg)](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fxo)\n[![styled with prettier](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fstyled_with-prettier-ff69b4.svg)](https:\u002F\u002Fgithub.com\u002Fprettier\u002Fprettier)\n[![made with lass](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmade_with-lass-95CC28.svg)](https:\u002F\u002Flass.js.org)\n[![license](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fladjs\u002Fsupertest.svg)](LICENSE)\n\n> HTTP assertions made easy via [superagent](http:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent).  Maintained for [Forward Email](https:\u002F\u002Fgithub.com\u002Fforwardemail) and [Lad](https:\u002F\u002Fgithub.com\u002Fladjs).\n\n## About\n\nThe motivation with this module is to provide a high-level abstraction for testing\nHTTP, while still allowing you to drop down to the [lower-level API](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F) provided by superagent.\n\n## Getting Started\n\nInstall supertest as an npm module and save it to your package.json file as a development dependency:\n\n```bash\nnpm install supertest --save-dev\n```\n\n  Once installed it can now be referenced by simply calling ```require('supertest');```\n\n## Example\n\nYou may pass an `http.Server`, or a `Function` to `request()` - if the server is not\nalready listening for connections then it is bound to an ephemeral port for you so\nthere is no need to keep track of ports.\n\nsupertest works with any test framework, here is an example without using any\ntest framework at all:\n\n```js\nconst request = require('supertest');\nconst express = require('express');\n\nconst app = express();\n\napp.get('\u002Fuser', function(req, res) {\n  res.status(200).json({ name: 'john' });\n});\n\nrequest(app)\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n```\n\nTo enable http2 protocol, simply append an options to `request` or `request.agent`:\n\n```js\nconst request = require('supertest');\nconst express = require('express');\n\nconst app = express();\n\napp.get('\u002Fuser', function(req, res) {\n  res.status(200).json({ name: 'john' });\n});\n\nrequest(app, { http2: true })\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n\nrequest.agent(app, { http2: true })\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n```\n\nHere's an example with mocha, note how you can pass `done` straight to any of the `.expect()` calls:\n\n```js\ndescribe('GET \u002Fuser', function() {\n  it('responds with json', function(done) {\n    request(app)\n      .get('\u002Fuser')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200, done);\n  });\n});\n```\n\nYou can use `auth` method to pass HTTP username and password in the same way as in the [superagent](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F#authentication):\n\n```js\ndescribe('GET \u002Fuser', function() {\n  it('responds with json', function(done) {\n    request(app)\n      .get('\u002Fuser')\n      .auth('username', 'password')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200, done);\n  });\n});\n```\n\nOne thing to note with the above statement is that superagent now sends any HTTP\nerror (anything other than a 2XX response code) to the callback as the first argument if\nyou do not add a status code expect (i.e. `.expect(302)`).\n\nIf you are using the `.end()` method `.expect()` assertions that fail will\nnot throw - they will return the assertion as an error to the `.end()` callback. In\norder to fail the test case, you will need to rethrow or pass `err` to `done()`, as follows:\n\n```js\ndescribe('POST \u002Fusers', function() {\n  it('responds with json', function(done) {\n    request(app)\n      .post('\u002Fusers')\n      .send({name: 'john'})\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200)\n      .end(function(err, res) {\n        if (err) return done(err);\n        return done();\n      });\n  });\n});\n```\n\nYou can also use promises:\n\n```js\ndescribe('GET \u002Fusers', function() {\n  it('responds with json', function() {\n    return request(app)\n      .get('\u002Fusers')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200)\n      .then(response => {\n         expect(response.body.email).toEqual('foo@bar.com');\n      })\n  });\n});\n```\n\nOr async\u002Fawait syntax:\n\n```js\ndescribe('GET \u002Fusers', function() {\n  it('responds with json', async function() {\n    const response = await request(app)\n      .get('\u002Fusers')\n      .set('Accept', 'application\u002Fjson')\n    expect(response.headers[\"content-type\"]).toMatch(\u002Fjson\u002F);\n    expect(response.status).toEqual(200);\n    expect(response.body.email).toEqual('foo@bar.com');\n  });\n});\n```\n\nExpectations are run in the order of definition. This characteristic can be used\nto modify the response body or headers before executing an assertion.\n\n```js\ndescribe('POST \u002Fuser', function() {\n  it('user.name should be an case-insensitive match for \"john\"', function(done) {\n    request(app)\n      .post('\u002Fuser')\n      .send('name=john') \u002F\u002F x-www-form-urlencoded upload\n      .set('Accept', 'application\u002Fjson')\n      .expect(function(res) {\n        res.body.id = 'some fixed id';\n        res.body.name = res.body.name.toLowerCase();\n      })\n      .expect(200, {\n        id: 'some fixed id',\n        name: 'john'\n      }, done);\n  });\n});\n```\n\nAnything you can do with superagent, you can do with supertest - for example multipart file uploads!\n\n```js\nrequest(app)\n  .post('\u002F')\n  .field('name', 'my awesome avatar')\n  .field('complex_object', '{\"attribute\": \"value\"}', {contentType: 'application\u002Fjson'})\n  .attach('avatar', 'test\u002Ffixtures\u002Favatar.jpg')\n  ...\n```\n\nPassing the app or url each time is not necessary, if you're testing\nthe same host you may simply re-assign the request variable with the\ninitialization app or url, a new `Test` is created per `request.VERB()` call.\n\n```js\nrequest = request('http:\u002F\u002Flocalhost:5555');\n\nrequest.get('\u002F').expect(200, function(err){\n  console.log(err);\n});\n\nrequest.get('\u002F').expect('heya', function(err){\n  console.log(err);\n});\n```\n\nHere's an example with mocha that shows how to persist a request and its cookies:\n\n```js\nconst request = require('supertest');\nconst should = require('should');\nconst express = require('express');\nconst cookieParser = require('cookie-parser');\n\ndescribe('request.agent(app)', function() {\n  const app = express();\n  app.use(cookieParser());\n\n  app.get('\u002F', function(req, res) {\n    res.cookie('cookie', 'hey');\n    res.send();\n  });\n\n  app.get('\u002Freturn', function(req, res) {\n    if (req.cookies.cookie) res.send(req.cookies.cookie);\n    else res.send(':(')\n  });\n\n  const agent = request.agent(app);\n\n  it('should save cookies', function(done) {\n    agent\n    .get('\u002F')\n    .expect('set-cookie', 'cookie=hey; Path=\u002F', done);\n  });\n\n  it('should send cookies', function(done) {\n    agent\n    .get('\u002Freturn')\n    .expect('hey', done);\n  });\n});\n```\n\nThere is another example that is introduced by the file [agency.js](https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent\u002Fblob\u002Fmaster\u002Ftest\u002Fnode\u002Fagency.js)\n\nHere is an example where 2 cookies are set on the request.\n\n```js\nagent(app)\n  .get('\u002Fapi\u002Fcontent')\n  .set('Cookie', ['nameOne=valueOne;nameTwo=valueTwo'])\n  .send()\n  .expect(200)\n  .end((err, res) => {\n    if (err) {\n      return done(err);\n    }\n    expect(res.text).to.be.equal('hey');\n    return done();\n  });\n```\n\n## API\n\nYou may use any [superagent](http:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent) methods,\nincluding `.write()`, `.pipe()` etc and perform assertions in the `.end()` callback\nfor lower-level needs.\n\n### .expect(status[, fn])\n\nAssert response `status` code.\n\n### .expect(status, body[, fn])\n\nAssert response `status` code and `body`.\n\n### .expect(body[, fn])\n\nAssert response `body` text with a string, regular expression, or\nparsed body object.\n\n### .expect(field, value[, fn])\n\nAssert header `field` `value` with a string or regular expression.\n\n### .expect(function(res) {})\n\nPass a custom assertion function. It'll be given the response object to check. If the check fails, throw an error.\n\n```js\nrequest(app)\n  .get('\u002F')\n  .expect(hasPreviousAndNextKeys)\n  .end(done);\n\nfunction hasPreviousAndNextKeys(res) {\n  if (!('next' in res.body)) throw new Error(\"missing next key\");\n  if (!('prev' in res.body)) throw new Error(\"missing prev key\");\n}\n```\n\n### .end(fn)\n\nPerform the request and invoke `fn(err, res)`.\n\n## Cookies\n\nHere is an example of using the `set` and `not` cookie assertions:\n\n```js\n\u002F\u002F setup super-test\nconst request = require('supertest');\nconst express = require('express');\nconst cookies = request.cookies;\n\n\u002F\u002F setup express test service\nconst app = express();\n\napp.get('\u002Fusers', function(req, res) {\n  res.cookie('alpha', 'one', { domain: 'domain.com', path: '\u002F', httpOnly: true });\n  res.send(200, { name: 'tobi' });\n});\n\n\u002F\u002F test request to service\nrequest(app)\n  .get('\u002Fusers')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  \u002F\u002F assert 'alpha' cookie is set with domain, path, and httpOnly options\n  .expect(cookies.set({ name: 'alpha', options: ['domain', 'path', 'httponly'] }))\n  \u002F\u002F assert 'bravo' cookie is NOT set\n  .expect(cookies.not('set', { name: 'bravo' }))\n  .end(function(err, res) {\n    if (err) {\n      throw err;\n    }\n  });\n```\n\nIt is also possible to chain assertions:\n\n```js\ncookies.set({\u002F* ... *\u002F}).not('set', {\u002F* ... *\u002F})\n```\n\n### Cookie assertions\n\nFunctions and methods are chainable.\n\n#### cookies([secret], [asserts])\n\nGet assertion function for [super-test](https:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest) `.expect()` method.\n\n*Arguments*\n\n- `secret` - String or array of strings. Cookie signature secrets.\n- `asserts(req, res)` - Function or array of functions. Failed custom assertions should throw.\n\n#### .set(expects, [assert])\n\nAssert that cookie and options are set.\n\n*Arguments*\n\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n  - `options` - *Optional* array of options.\n- `assert` - *Optional* boolean \"assert true\" modifier. Default: `true`.\n\n#### .reset(expects, [assert])\n\nAssert that cookie is set and was already set (in request headers).\n\n*Arguments*\n\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n- `assert` - *Optional* boolean \"assert true\" modifier. Default: `true`.\n\n#### .new(expects, [assert])\n\nAssert that cookie is set and was NOT already set (NOT in request headers).\n\n*Arguments*\n\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n- `assert` - *Optional* boolean \"assert true\" modifier. Default: `true`.\n\n#### .renew(expects, [assert])\n\nAssert that cookie is set with a strictly greater `expires` or `max-age` than the given value.\n\n*Arguments*\n\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n  - `options` - Object of options. `use one of two options below`\n  - `options`.`expires` - String UTC expiration for original cookie (in request headers).\n  - `options`.`max-age` - Integer ttl in seconds for original cookie (in request headers).\n- `assert` - *Optional* boolean \"assert true\" modifier. Default: `true`.\n\n#### .contain(expects, [assert])\n\nAssert that cookie is set with value and contains options.\n\nRequires `cookies(secret)` initialization if cookie is signed.\n\n*Arguments*\n\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n  - `value` - *Optional* string unsigned value of cookie.\n  - `options` - *Optional* object of options.\n  - `options`.`domain` - *Optional* string domain.\n  - `options`.`path` - *Optional* string path.\n  - `options`.`expires` - *Optional* string UTC expiration.\n  - `options`.`max-age` - *Optional* integer ttl, in seconds.\n  - `options`.`secure` - *Optional* boolean secure flag.\n  - `options`.`httponly` - *Optional* boolean httpOnly flag.\n- `assert` - *Optional* boolean \"assert true\" modifier. Default: `true`.\n\n#### .not(method, expects)\n\nCall any cookies assertion method with \"assert true\" modifier set to `false`.\n\nSyntactic sugar.\n\n*Arguments*\n\n- `method` - String method name. Arguments of method name apply in `expects`.\n- `expects` - Object or array of objects.\n  - `name` - String name of cookie.\n  - `value` - *Optional* string unsigned value of cookie.\n  - `options` - *Optional* object of options.\n\n## Notes\n\nInspired by [api-easy](https:\u002F\u002Fgithub.com\u002Fflatiron\u002Fapi-easy) minus vows coupling.\n\n## License\n\nMIT\n\n[coverage-badge]: https:\u002F\u002Fimg.shields.io\u002Fcodecov\u002Fc\u002Fgithub\u002Fladjs\u002Fsupertest.svg\n[coverage]: https:\u002F\u002Fcodecov.io\u002Fgh\u002Fladjs\u002Fsupertest\n[travis-badge]: https:\u002F\u002Ftravis-ci.org\u002Fladjs\u002Fsupertest.svg?branch=master\n[travis]: https:\u002F\u002Ftravis-ci.org\u002Fladjs\u002Fsupertest\n[dependencies-badge]: https:\u002F\u002Fdavid-dm.org\u002Fladjs\u002Fsupertest\u002Fstatus.svg\n[dependencies]: https:\u002F\u002Fdavid-dm.org\u002Fladjs\u002Fsupertest\n[prs-badge]: https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FPRs-welcome-brightgreen.svg?style=flat-square\n[prs]: http:\u002F\u002Fmakeapullrequest.com\n[license-badge]: https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg?style=flat-square\n[license]: https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fblob\u002Fmaster\u002FLICENSE\n","# [supertest](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F)\n\n[![构建状态](https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Factions\u002Fworkflows\u002Fci.yml)\n[![代码覆盖率](https:\u002F\u002Fimg.shields.io\u002Fcodecov\u002Fc\u002Fgithub\u002Fladjs\u002Fsupertest.svg)](https:\u002F\u002Fcodecov.io\u002Fgh\u002Fladjs\u002Fsupertest)\n[![代码风格](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fcode_style-XO-5ed9c7.svg)](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fxo)\n[![使用 prettier 格式化](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fstyled_with-prettier-ff69b4.svg)](https:\u002F\u002Fgithub.com\u002Fprettier\u002Fprettier)\n[![由 lass 构建](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmade_with-lass-95CC28.svg)](https:\u002F\u002Flass.js.org)\n[![许可证](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fladjs\u002Fsupertest.svg)](LICENSE)\n\n> 通过 [superagent](http:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent) 轻松实现 HTTP 断言。由 [Forward Email](https:\u002F\u002Fgithub.com\u002Fforwardemail) 和 [Lad](https:\u002F\u002Fgithub.com\u002Fladjs) 维护。\n\n## 关于\n\n本模块的初衷是为测试 HTTP 提供高层次的抽象，同时仍然允许你回退到 superagent 提供的[低层次 API](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F)。\n\n## 快速开始\n\n将 supertest 作为 npm 模块安装，并将其保存为开发依赖项到你的 package.json 文件中：\n\n```bash\nnpm install supertest --save-dev\n```\n\n安装完成后，只需调用 ```require('supertest');``` 即可引用它。\n\n## 示例\n\n你可以将 `http.Server` 或 `Function` 传递给 `request()` —— 如果服务器尚未开始监听连接，它会为你绑定到一个临时端口，因此无需手动管理端口号。\n\nsupertest 可以与任何测试框架配合使用，以下是一个完全不依赖测试框架的示例：\n\n```js\nconst request = require('supertest');\nconst express = require('express');\n\nconst app = express();\n\napp.get('\u002Fuser', function(req, res) {\n  res.status(200).json({ name: 'john' });\n});\n\nrequest(app)\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n```\n\n要启用 HTTP\u002F2 协议，只需在 `request` 或 `request.agent` 中添加一个选项即可：\n\n```js\nconst request = require('supertest');\nconst express = require('express');\n\nconst app = express();\n\napp.get('\u002Fuser', function(req, res) {\n  res.status(200).json({ name: 'john' });\n});\n\nrequest(app, { http2: true })\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n\nrequest.agent(app, { http2: true })\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n  });\n```\n\n以下是使用 Mocha 的示例，请注意，你可以直接将 `done` 回调函数传递给任意 `.expect()` 调用：\n\n```js\ndescribe('GET \u002Fuser', function() {\n  it('响应 JSON 数据', function(done) {\n    request(app)\n      .get('\u002Fuser')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200, done);\n  });\n});\n```\n\n你可以使用 `auth` 方法来传递 HTTP 用户名和密码，方式与 [superagent](https:\u002F\u002Fforwardemail.github.io\u002Fsuperagent\u002F#authentication) 相同：\n\n```js\ndescribe('GET \u002Fuser', function() {\n  it('响应 JSON 数据', function(done) {\n    request(app)\n      .get('\u002Fuser')\n      .auth('username', 'password')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200, done);\n  });\n});\n```\n\n需要注意的是，如果未添加状态码断言（例如 `.expect(302)`），superagent 现在会将所有 HTTP 错误（即非 2XX 响应码）作为第一个参数传递给回调函数。\n\n如果你使用 `.end()` 方法，失败的 `.expect()` 断言不会抛出错误；它们会将断言结果作为错误对象返回给 `.end()` 回调。为了使测试用例失败，你需要重新抛出错误或将 `err` 传递给 `done()`，如下所示：\n\n```js\ndescribe('POST \u002Fusers', function() {\n  it('响应 JSON 数据', function(done) {\n    request(app)\n      .post('\u002Fusers')\n      .send({name: 'john'})\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200)\n      .end(function(err, res) {\n        if (err) return done(err);\n        return done();\n      });\n  });\n});\n```\n\n你也可以使用 Promise：\n\n```js\ndescribe('GET \u002Fusers', function() {\n  it('响应 JSON 数据', function() {\n    return request(app)\n      .get('\u002Fusers')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200)\n      .then(response => {\n         expect(response.body.email).toEqual('foo@bar.com');\n      })\n  });\n});\n```\n\n或者使用 async\u002Fawait 语法：\n\n```js\ndescribe('GET \u002Fusers', function() {\n  it('响应 JSON 数据', async function() {\n    const response = await request(app)\n      .get('\u002Fusers')\n      .set('Accept', 'application\u002Fjson')\n    expect(response.headers[\"content-type\"]).toMatch(\u002Fjson\u002F);\n    expect(response.status).toEqual(200；\n    expect(response.body.email).toEqual('foo@bar.com');\n  });\n});\n```\n\n断言会按照定义的顺序依次执行。这一特性可用于在执行断言之前修改响应体或响应头。\n\n```js\ndescribe('POST \u002Fuser', function() {\n  it('用户名称应为不区分大小写的 \"john\"', function(done) {\n    request(app)\n      .post('\u002Fuser')\n      .send('name=john') \u002F\u002F x-www-form-urlencoded 格式上传\n      .set('Accept', 'application\u002Fjson')\n      .expect(function(res) {\n        res.body.id = '某个固定 ID';\n        res.body.name = res.body.name.toLowerCase();\n      })\n      .expect(200, {\n        id: '某个固定 ID',\n        name: 'john'\n      }, done);\n  });\n});\n```\n\n凡是 superagent 能做到的事情，supertest 也能做到——例如多部分文件上传！\n\n```js\nrequest(app)\n  .post('\u002F')\n  .field('name', '我的超赞头像')\n  .field('complex_object', '{\"attribute\": \"value\"}', {contentType: 'application\u002Fjson'})\n  .attach('avatar', 'test\u002Ffixtures\u002Favatar.jpg')\n  ...\n```\n\n每次都不必重复传递应用实例或 URL；如果你测试的是同一台主机，只需将 `request` 变量重新赋值为初始的应用实例或 URL 即可，每次调用 `request.VERB()` 都会创建一个新的测试对象。\n\n```js\nrequest = request('http:\u002F\u002Flocalhost:5555');\n\nrequest.get('\u002F').expect(200, function(err){\n  console.log(err);\n});\n\nrequest.get('\u002F').expect('heya', function(err){\n  console.log(err);\n});\n```\n\n以下是使用 Mocha 的示例，展示了如何保持请求及其 Cookie 的持久性：\n\n```js\nconst request = require('supertest');\nconst should = require('should');\nconst express = require('express');\nconst cookieParser = require('cookie-parser');\n\ndescribe('request.agent(app)', function() {\n  const app = express();\n  app.use(cookieParser());\n\n  app.get('\u002F', function(req, res) {\n    res.cookie('cookie', 'hey');\n    res.send();\n  });\n\n  app.get('\u002Freturn', function(req, res) {\n    if (req.cookies.cookie) res.send(req.cookies.cookie);\n    else res.send(':(')\n  });\n\n  const agent = request.agent(app);\n\n  it('应该保存 Cookie', function(done) {\n    agent\n    .get('\u002F')\n    .expect('set-cookie', 'cookie=hey; Path=\u002F', done);\n  });\n\n  it('应该发送 Cookie', function(done) {\n    agent\n    .get('\u002Freturn')\n    .expect('hey', done);\n  });\n});\n```\n\n还有一个由文件 [agency.js](https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent\u002Fblob\u002Fmaster\u002Ftest\u002Fnode\u002Fagency.js) 引入的示例。\n\n以下是一个在请求中设置两个 Cookie 的示例：\n\n```js\nagent(app)\n  .get('\u002Fapi\u002Fcontent')\n  .set('Cookie', ['nameOne=valueOne;nameTwo=valueTwo'])\n  .send()\n  .expect(200)\n  .end((err, res) => {\n    if (err) {\n      return done(err);\n    }\n    expect(res.text).to.be.equal('hey');\n    return done();\n  });\n```\n\n## API\n\n你可以使用任何 [superagent](http:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent) 的方法，包括 `.write()`、`.pipe()` 等，并在 `.end()` 回调中进行低层次的断言。\n\n### .expect(status[, fn])\n\n断言响应的状态码。\n\n### .expect(status, body[, fn])\n\n断言响应的状态码和响应体。\n\n### .expect(body[, fn])\n\n断言响应体文本，可以是字符串、正则表达式或解析后的对象。\n\n### .expect(field, value[, fn])\n\n断言响应头中的字段值，可以是字符串或正则表达式。\n\n### .expect(function(res) {})\n\n传递一个自定义断言函数。该函数会接收响应对象进行检查。如果检查失败，抛出错误。\n\n```js\nrequest(app)\n  .get('\u002F')\n  .expect(hasPreviousAndNextKeys)\n  .end(done);\n\nfunction hasPreviousAndNextKeys(res) {\n  if (!('next' in res.body)) throw new Error(\"missing next key\");\n  if (!('prev' in res.body)) throw new Error(\"missing prev key\");\n}\n```\n\n### .end(fn)\n\n执行请求并调用 `fn(err, res)`。\n\n## Cookies\n\n以下是使用 `set` 和 `not` cookie 断言的示例：\n\n```js\n\u002F\u002F 设置 super-test\nconst request = require('supertest');\nconst express = require('express');\nconst cookies = request.cookies;\n\n\u002F\u002F 设置 express 测试服务\nconst app = express();\n\napp.get('\u002Fusers', function(req, res) {\n  res.cookie('alpha', 'one', { domain: 'domain.com', path: '\u002F', httpOnly: true });\n  res.send(200, { name: 'tobi' });\n});\n\n\u002F\u002F 向服务发送测试请求\nrequest(app)\n  .get('\u002Fusers')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect('Content-Length', '15')\n  .expect(200)\n  \u002F\u002F 断言 'alpha' cookie 已设置，并且具有 domain、path 和 httpOnly 选项\n  .expect(cookies.set({ name: 'alpha', options: ['domain', 'path', 'httponly'] }))\n  \u002F\u002F 断言 'bravo' cookie 未被设置\n  .expect(cookies.not('set', { name: 'bravo' }))\n  .end(function(err, res) {\n    if (err) {\n      throw err;\n    }\n  });\n```\n\n也可以链式调用断言：\n\n```js\ncookies.set({\u002F* ... *\u002F}).not('set', {\u002F* ... *\u002F})\n```\n\n### Cookie 断言\n\n函数和方法可以链式调用。\n\n#### cookies([secret], [asserts])\n\n获取用于 [super-test](https:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest) `.expect()` 方法的断言函数。\n\n*参数*\n\n- `secret` - 字符串或字符串数组。Cookie 签名密钥。\n- `asserts(req, res)` - 函数或函数数组。自定义断言失败时应抛出错误。\n\n#### .set(expects, [assert])\n\n断言 cookie 及其选项已设置。\n\n*参数*\n\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n  - `options` - *可选* 的选项数组。\n- `assert` - *可选* 的“断言为真”修饰符。默认值：`true`。\n\n#### .reset(expects, [assert])\n\n断言 cookie 已设置，并且之前已经设置过（在请求头中）。\n\n*参数*\n\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n- `assert` - *可选* 的“断言为真”修饰符。默认值：`true`。\n\n#### .new(expects, [assert])\n\n断言 cookie 已设置，但之前未设置过（不在请求头中）。\n\n*参数*\n\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n- `assert` - *可选* 的“断言为真”修饰符。默认值：`true`。\n\n#### .renew(expects, [assert])\n\n断言 cookie 的 `expires` 或 `max-age` 值严格大于给定的值。\n\n*参数*\n\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n  - `options` - 选项对象。需从以下两个选项中选择其一：\n    - `options`.`expires` - 原始 cookie 的 UTC 到期时间（在请求头中）。\n    - `options`.`max-age` - 原始 cookie 的存活时间（以秒为单位，也在请求头中）。\n- `assert` - *可选* 的“断言为真”修饰符。默认值：`true`。\n\n#### .contain(expects, [assert])\n\n断言 cookie 已设置，并且包含指定的选项。\n\n如果 cookie 是签名的，则需要先初始化 `cookies(secret)`。\n\n*参数*\n\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n  - `value` - *可选* 的 unsigned cookie 值（字符串）。\n  - `options` - *可选* 的选项对象。\n    - `options`.`domain` - *可选* 的域名。\n    - `options`.`path` - *可选* 的路径。\n    - `options`.`expires` - *可选* 的 UTC 到期时间。\n    - `options`.`max-age` - *可选* 的存活时间（以秒为单位）。\n    - `options`.`secure` - *可选* 的安全标志。\n    - `options`.`httponly` - *可选* 的 httpOnly 标志。\n- `assert` - *可选* 的“断言为真”修饰符。默认值：`true`。\n\n#### .not(method, expects)\n\n调用任何 cookie 断言方法，并将“断言为真”修饰符设为 `false`。\n\n语法糖。\n\n*参数*\n\n- `method` - 字符串方法名。方法名对应的参数适用于 `expects`。\n- `expects` - 对象或对象数组。\n  - `name` - Cookie 的名称（字符串）。\n  - `value` - *可选* 的 unsigned cookie 值（字符串）。\n  - `options` - *可选* 的选项对象。\n\n## 注释\n\n灵感来自 [api-easy](https:\u002F\u002Fgithub.com\u002Fflatiron\u002Fapi-easy)，去除了与 vows 的耦合。\n\n## 许可证\n\nMIT\n\n[coverage-badge]: https:\u002F\u002Fimg.shields.io\u002Fcodecov\u002Fc\u002Fgithub\u002Fladjs\u002Fsupertest.svg\n[coverage]: https:\u002F\u002Fcodecov.io\u002Fgh\u002Fladjs\u002Fsupertest\n[travis-badge]: https:\u002F\u002Ftravis-ci.org\u002Fladjs\u002Fsupertest.svg?branch=master\n[travis]: https:\u002F\u002Ftravis-ci.org\u002Fladjs\u002Fsupertest\n[dependencies-badge]: https:\u002F\u002Fdavid-dm.org\u002Fladjs\u002Fsupertest\u002Fstatus.svg\n[dependencies]: https:\u002F\u002Fdavid-dm.org\u002Fladjs\u002Fsupertest\n[prs-badge]: https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FPRs-welcome-brightgreen.svg?style=flat-square\n[prs]: http:\u002F\u002Fmakeapullrequest.com\n[license-badge]: https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg?style=flat-square\n[license]: https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fblob\u002Fmaster\u002FLICENSE","# Supertest 快速上手指南\n\nSupertest 是一个基于 [superagent](https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsuperagent) 的高层抽象库，专为测试 HTTP 服务而生。它允许你轻松编写断言，同时保留访问底层 API 的能力，完美适配 Express、Koa 等 Node.js Web 框架。\n\n## 环境准备\n\n*   **系统要求**：Node.js (推荐 LTS 版本)\n*   **前置依赖**：\n    *   一个已初始化的 Node.js 项目 (`package.json`)\n    *   待测试的 Web 应用（如 Express app 实例）或正在运行的 HTTP 服务地址\n    *   可选：测试运行器（如 Mocha, Jest, Vitest 等），虽然 Supertest 也可独立运行\n\n## 安装步骤\n\n使用 npm 将 supertest 安装为开发依赖：\n\n```bash\nnpm install supertest --save-dev\n```\n\n> **国内加速提示**：如果下载速度较慢，可配置淘宝镜像源：\n> ```bash\n> npm config set registry https:\u002F\u002Fregistry.npmmirror.com\n> npm install supertest --save-dev\n> ```\n\n## 基本使用\n\nSupertest 可以直接接收一个 Express `app` 实例或一个 URL 字符串。以下是最基础的用法示例，展示了如何发起请求并进行状态码和响应内容的断言。\n\n### 1. 基础示例（无测试框架）\n\n```js\nconst request = require('supertest');\nconst express = require('express');\n\n\u002F\u002F 创建一个简单的 Express 应用\nconst app = express();\n\napp.get('\u002Fuser', function(req, res) {\n  res.status(200).json({ name: 'john' });\n});\n\n\u002F\u002F 发起请求并断言\nrequest(app)\n  .get('\u002Fuser')\n  .expect('Content-Type', \u002Fjson\u002F)\n  .expect(200)\n  .end(function(err, res) {\n    if (err) throw err;\n    console.log('测试通过！');\n  });\n```\n\n### 2. 结合 Mocha 使用\n\n在实际项目中，通常配合 Mocha 等测试框架使用。你可以直接将 `done` 回调传递给 `.expect()` 或 `.end()`。\n\n```js\nconst request = require('supertest');\nconst app = require('..\u002Fapp'); \u002F\u002F 引入你的应用\n\ndescribe('GET \u002Fuser', function() {\n  it('应返回 JSON 数据', function(done) {\n    request(app)\n      .get('\u002Fuser')\n      .set('Accept', 'application\u002Fjson')\n      .expect('Content-Type', \u002Fjson\u002F)\n      .expect(200, done); \u002F\u002F 自动处理完成信号\n  });\n});\n```\n\n### 3. 使用 async\u002Fawait (现代写法)\n\n如果你使用的是支持 Promise 的测试框架（如 Jest 或新版 Mocha），可以使用 `async\u002Fawait` 语法，代码更简洁：\n\n```js\ndescribe('GET \u002Fusers', function() {\n  it('应返回用户数据', async function() {\n    const response = await request(app)\n      .get('\u002Fusers')\n      .set('Accept', 'application\u002Fjson')\n      .expect(200);\n    \n    \u002F\u002F 使用断言库进行详细检查\n    expect(response.body.email).toEqual('foo@bar.com');\n    expect(response.headers[\"content-type\"]).toMatch(\u002Fjson\u002F);\n  });\n});\n```\n\n### 4. 高级功能：保持会话 (Cookie)\n\n如果需要测试涉及登录状态或 Cookie 的场景，可以使用 `request.agent()` 来自动管理 Cookie。\n\n```js\nconst agent = request.agent(app);\n\n\u002F\u002F 第一次请求：登录并保存 Cookie\nawait agent\n  .post('\u002Flogin')\n  .send({ username: 'test', password: '123' })\n  .expect(200);\n\n\u002F\u002F 第二次请求：自动携带之前的 Cookie\nawait agent\n  .get('\u002Fprofile')\n  .expect(200)\n  .then(res => {\n    console.log('已登录用户:', res.body.name);\n  });\n```","某电商团队正在开发基于 Node.js 的订单微服务，需要在每次代码提交后快速验证 API 接口的正确性与稳定性。\n\n### 没有 supertest 时\n- **环境搭建繁琐**：测试前必须手动启动服务器并占用特定端口，测试结束后还需清理进程，容易因端口冲突导致测试失败。\n- **断言逻辑分散**：需要分别编写代码检查 HTTP 状态码、响应头格式及 JSON 数据内容，代码冗长且难以维护。\n- **异步处理复杂**：在处理异步请求回调时，缺乏统一的错误捕获机制，一旦断言失败往往导致测试进程静默退出或报错信息模糊。\n- **认证模拟困难**：模拟带 Token 或 Basic Auth 的请求时，需手动构造复杂的 Header 对象，增加了测试用例的编写成本。\n\n### 使用 supertest 后\n- **自动端口管理**：supertest 可直接接收 Express 应用实例，自动绑定临时端口进行测试，彻底消除了手动启停服务和端口占用的烦恼。\n- **链式流畅断言**：通过 `.expect()` 方法链式调用，能在一条语句中同时验证状态码、响应头类型及数据长度，代码简洁直观。\n- **智能错误反馈**：内置的断言失败机制会自动将错误传递给测试框架（如 Mocha），确保任何接口异常都能立即中断测试并输出清晰堆栈。\n- **便捷身份模拟**：提供专用的 `.auth()` 方法，仅需一行代码即可模拟用户登录态，大幅简化了受保护接口的测试流程。\n\nsupertest 通过将底层 HTTP 细节封装为高层抽象，让 Node.js 接口测试从繁琐的配置工作中解放出来，实现了高效、可靠的质量保障。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fforwardemail_supertest_66cd11d2.png","forwardemail","Forward Email - Open-source & Privacy-focused Email Service","https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002Fforwardemail_1e975bca.png","The 100% open-source and privacy-focused email service.  All-in-one alternative to Gmail + Mailchimp + Sendgrid. Built with @spamscanner, @breejs, @cabinjs, ...",null,"support@forwardemail.net","https:\u002F\u002Fforwardemail.net","https:\u002F\u002Fgithub.com\u002Fforwardemail",[82],{"name":83,"color":84,"percentage":85},"JavaScript","#f1e05a",100,14343,782,"2026-04-06T15:24:06","MIT",1,"未说明","不需要",{"notes":94,"python":91,"dependencies":95},"该工具是一个基于 Node.js 的 HTTP 测试库，需通过 npm 安装。它不依赖 Python、GPU 或特定操作系统，适用于任何支持 Node.js 的环境。主要配合 superagent 使用，可集成到任意测试框架（如 Mocha）中，支持 Promise 和 async\u002Fawait 语法。",[96,97,98,99,100],"superagent","express (示例依赖)","mocha (示例测试框架)","should (示例断言库)","cookie-parser (示例中间件)",[14,13],[64,96,103,104],"assertions","node","2026-03-27T02:49:30.150509","2026-04-07T22:49:56.976206",[108,113,118,123,128,132],{"id":109,"question_zh":110,"answer_zh":111,"source_url":112},22851,"遇到 'Error: listen EADDRINUSE: address already in use' 端口被占用错误怎么办？","常见原因及解决方案：\n1. 检查 .env 文件：如果在变量值末尾误加了分号（;），会导致端口解析错误。请移除分号后重试。\n2. Windows 用户专用脚本：可以添加以下函数来检测并杀死占用端口的进程：\n   - 使用 `net` 模块检测端口是否被占用。\n   - 使用 `child_process` 执行 `netstat -ano | findstr :\u003C端口>` 找到进程 ID (PID)。\n   - 执行 `taskkill \u002FPID \u003CPID> \u002FF` 强制结束进程。\n3. Jest 配置：在 `package.json` 的 scripts 中设置 `--maxWorkers=1`，防止并行测试导致端口冲突，例如：`\"test\": \"jest --watchAll --verbose --coverage --maxWorkers=1\"`。","https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Fissues\u002F568",{"id":114,"question_zh":115,"answer_zh":116,"source_url":117},22852,"测试时出现 'Cannot read property status of undefined' 错误是什么原因？","该错误通常发生在响应对象为 undefined 时，常见场景包括：\n1. TLS\u002FSSL 证书问题：当测试本地 HTTPS 服务时，如果证书不包含 '127.0.0.1'，会导致连接失败且无有效响应。解决方法是在运行测试时设置环境变量：`NODE_TLS_REJECT_UNAUTHORIZED=0`。\n2. 中间件或认证头未正确传递：确保在请求中正确设置了 `Authorization` 等头部信息，并且服务端中间件能正确读取。\n3. 异步错误处理：检查代码中是否正确捕获了 Promise  rejection，避免在未定义的 error.response 上访问 status 属性。","https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Fissues\u002F352",{"id":119,"question_zh":120,"answer_zh":121,"source_url":122},22853,"运行大量测试时随机出现 'ECONNRESET' 错误如何解决？","这是由于并发连接数过多或连接复用不当导致的。解决方案包括：\n1. 升级版本：该问题已在 supertest v7.1.0 中得到修复，请升级到此版本或更高版本。\n2. 连接复用策略：确保在测试套件中复用 supertest 实例（agent），而不是为每个请求创建新实例。可以使用 Map 存储不同应用实例对应的 agent。\n3. 串行执行：在 CI 环境中使用 Jest 的 `--runInBand` 参数，确保测试串行运行，减少并发压力。","https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Fissues\u002F709",{"id":124,"question_zh":125,"answer_zh":126,"source_url":127},22854,"如何在 POST 请求中正确设置 Authorization 头部？","直接使用 `.set()` 方法即可，但推荐结合 `agent` 使用以更好地管理状态和头部：\n\n方法一（直接设置）：\n```javascript\nrequest(app)\n  .post('\u002Fapi\u002Fcategories')\n  .send(category)\n  .set('Authorization', token)\n  .end((err, res) => { ... });\n```\n\n方法二（推荐，使用 agent 复用）：\n```javascript\nconst server = request.agent(app).set('Authorization', 'abc123');\n\u002F\u002F 后续请求自动携带该头部\nserver.post('\u002Fapi').expect(201);\nserver.get('\u002Fother-api').expect(200);\n```\n注意：不要使用 `.setHeader()`，这不是 supertest\u002Fsuperagent 的有效方法。","https:\u002F\u002Fgithub.com\u002Fforwardemail\u002Fsupertest\u002Fissues\u002F398",{"id":129,"question_zh":130,"answer_zh":131,"source_url":112},22855,"Supertest 测试中如何正确处理服务器关闭以避免端口占用？","在测试生命周期中必须显式关闭服务器。如果在 `beforeEach` 或测试文件中启动了服务器，务必在 `afterEach` 中调用 `server.close()`。\n示例代码：\n```javascript\nconst server = require('..\u002Findex'); \u002F\u002F 假设 index.js 导出了 app 并调用了 listen\nconst request = require('supertest').agent(server.listen());\n\nafterEach(() => {\n  server.close(); \u002F\u002F 关键步骤：关闭服务器释放端口\n});\n```\n如果忘记关闭，多次运行测试时会报 'EADDRINUSE' 错误。",{"id":133,"question_zh":134,"answer_zh":135,"source_url":117},22856,"为什么我的 Supertest 请求返回的 response 是 undefined？","这通常是因为底层 HTTP 请求失败（如证书验证失败、连接被重置等），导致没有生成有效的响应对象。排查步骤：\n1. 检查是否是 HTTPS 本地测试：如果是，设置 `NODE_TLS_REJECT_UNAUTHORIZED=0` 环境变量跳过证书验证。\n2. 检查服务器是否正常运行且监听正确端口。\n3. 查看是否有未捕获的异常或 Promise 拒绝，导致流程中断。\n4. 确保请求 URL 和方法正确，且服务器端路由已定义。",[137,142,147,152,157,162,167,172,177,182,187,192,197,202,207,212,217,222,227,232],{"id":138,"version":139,"summary_zh":140,"released_at":141},136581,"v7.2.2","- 修复：在 cookies 模块中用原生断言替换 &#39;should&#39; 依赖 1954bcf\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.2.1...v7.2.2","2026-01-06T09:26:32",{"id":143,"version":144,"summary_zh":145,"released_at":146},136582,"v7.2.1","- 修复：修正断言模块的大小写敏感 `require` 路径  d4f04fb\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.2.0...v7.2.1","2026-01-06T08:43:01",{"id":148,"version":149,"summary_zh":150,"released_at":151},136583,"v7.2.0","- 修复：修复了包锁定 c4b08a6\n- 修复：从测试中移除了 v14 和 v16 d084ce2\n- 合并拉取请求 #872，来自 forwardemail\u002Fdependabot\u002Fnpm_and_yarn，js-yaml 3.14.2 61f3ddf\n- 合并拉取请求 #873，来自 forwardemail\u002Fdependabot\u002Fnpm_and_yarn，multi-6d05d0e569 bd2fe45\n- 杂项（依赖）：升级 qs、body-parser 和 express 07bf4fb\n- 合并拉取请求 #866，来自 SchroederSteffen，使用小写头名称 0666797\n- 合并拉取请求 #868，来自 dmurvihill，添加了 cookie 断言 953eca7\n- 杂项（开发依赖）：将 js-yaml 从 3.14.1 升级到 3.14.2 81ab94c\n- 合并拉取请求 #870，来自 kudlav，补丁-1 14d905d\n- 更新 README 中的文档链接 f508b30\n- 功能（cookies）：添加 cookie 断言 4f89680\n- 杂项（README）：使用小写头名称 1e642b0\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.1.4...v7.2.0","2026-01-06T07:46:51",{"id":153,"version":154,"summary_zh":155,"released_at":156},136584,"v7.1.4","- chore: 更新依赖 6021ec8\n- 合并拉取请求 #865，来自 forwardemail\u002Fdependabot\u002Fnpm_and_yarn\u002Fform-data-4.0.4 ceca508\n- chore(deps): 将 form-data 从 4.0.3 升级到 4.0.4 b1fb983\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.1.3...v7.1.4","2025-07-22T23:22:11",{"id":158,"version":159,"summary_zh":160,"released_at":161},136585,"v7.1.3","- 修复：恢复自动关闭服务器的功能（根据 #651） b6fd281\n- 修复：重命名 CI 配置文件 75bcbc9\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.1.2...v7.1.3","2025-07-07T23:27:36",{"id":163,"version":164,"summary_zh":165,"released_at":166},136586,"v7.1.2","- 功能：修复服务器生命周期、回调处理以及与 SuperAgent v10 的兼容性 6d060e3\n- 合并拉取请求 #862，来自 noritaka1166 的分支 fix-typo-test 0413057\n- 合并拉取请求 #841，来自 viniciusamc 的 master 分支 e2be0e1\n- 构建：修复测试中的拼写错误 b7f985b\n- 功能（Bearer）：添加 Bearer 认证令牌方法 411f7ce\n\n---\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.1.1...v7.1.2","2025-07-07T18:49:01",{"id":168,"version":169,"summary_zh":170,"released_at":171},136587,"v7.1.1","- 合并拉取请求 #858，来自 ByteOPCode\u002Fmaster  f553845\n- 特性：更新 superagent 版本  d37d197\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.1.0...v7.1.1","2025-05-12T11:27:10",{"id":173,"version":174,"summary_zh":175,"released_at":176},136588,"v7.1.0","- 合并拉取请求 #852，来自 alxndrsn\u002Feconnreset  de045d4\n- 合并拉取请求 #845，来自 ikonst\u002Fmaster  be0d5c7\n- 合并拉取请求 #854，来自 alxndrsn\u002Fcommitlint  35ac86f\n- 修复（开发）：commitlint 的开发依赖及配置  e67fcb2\n- 修复：避免服务器复用的竞态条件  96c06e3\n- 测试：修复因 localhost 同时为 IPv6 地址而导致的测试失败  92079a0\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv7.0.0...v7.1.0","2025-03-20T15:48:12",{"id":178,"version":179,"summary_zh":180,"released_at":181},136589,"v7.0.0","- 合并拉取请求 #834，来自 Bruception\u002Fmaster 225118c\n- 修复 TestAgent 未能继承 Agent 属性的问题 f290431\n- 修复：升级依赖项，移除 Node.js v\u003C14.16.0 1e18c20\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv6.3.4...v7.0.0","2024-04-24T15:19:45",{"id":183,"version":184,"summary_zh":185,"released_at":186},136590,"v6.3.4","- 构建任务：升级依赖，移除 yarn.lock bc4398a\n- 构建任务：升级依赖 c823515\n- 合并拉取请求 #811，来自 ladjs\u002Fdependabot\u002Fnpm_and_yarn\u002Fcookiejar-2.1.4 37017b3\n- 合并拉取请求 #814，来自 siddtheone\u002Fpatch-1 6b41374\n- 合并拉取请求 #828，来自 9renpoto\u002F9renpoto-patch-1 0ff9c02\n- si\u002Fvisionmedia\u002Fladjs\u002F 2cba6d4\n- 更新 package.json 79a69b6\n- 合并拉取请求 #821，来自 yunnysunny\u002Ffeature\u002Fci-fix c1b8f9d\n- CI：修复 GitHub Actions 缓存保存失败的问题 5d48749\n- 合并拉取请求 #818，来自 lamweili\u002Fpatch-1 25920e7\n- 文档：修复链接（针对 #621）3767f9e\n- 更新 README.md b81d3a4\n- 移除未使用的导入 4b372eb\n- 构建任务（依赖）：将 cookiejar 从 2.1.3 升级到 2.1.4 ac9327f\n- 合并拉取请求 #646，来自 dtom90\u002Fpatch-1 44d5d72\n- 合并拉取请求 #621，来自 RichieRunner\u002Fpatch-1 d91ff37\n- 更新 index.js 函数定义 9ee6a1b\n- 更新 README.md 13a2b44\n\nhttps:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest\u002Fcompare\u002Fv6.3.3...v6.3.4","2024-01-14T16:41:57",{"id":188,"version":189,"summary_zh":190,"released_at":191},136591,"v6.3.3","- chore: bump deps  2910f73\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.3.2...v6.3.3","2022-12-07T23:27:02",{"id":193,"version":194,"summary_zh":195,"released_at":196},136592,"v6.3.2","- docs: added maintainer note  e24875d\r\n- docs: fixed links  5b59ddc\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.3.1...v6.3.2","2022-12-02T21:35:19",{"id":198,"version":199,"summary_zh":200,"released_at":201},136593,"v6.3.1","- fix: bump superagent (closes #798)  25122e6\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.3.0...v6.3.1","2022-10-24T20:31:11",{"id":203,"version":204,"summary_zh":205,"released_at":206},136594,"v6.3.0","- fix: lint package.json, added files array  20adbc1\r\n- Merge pull request #793 from lamweili\u002Ffeat\u002Fhttp2  22b89b6\r\n- Merge pull request #795 from lamweili\u002Fcode-coverage  a225e95\r\n- Merge pull request #794 from lamweili\u002Fmigrate-cov  fa129dc\r\n- test(#767): 100% code coverage  9c5bd92\r\n- ci: migrated code coverage from coveralls to codecov  46e6feb\r\n- feat: supports http2  da57804\r\n- Merge pull request #767 from alexandre-abrioux\u002Fexpect-stacktrace  e064b5a\r\n- Merge pull request #774 from jmccure\u002Fupdate-promise-example-readme  ba4b43b\r\n- Merge pull request #792 from lamweili\u002Frefactor\u002Ftest  b8c2e29\r\n- ci: added ci  da1e842\r\n- refactor(test): do not hardcode any ports, use ephemeral ports  8847310\r\n- refactor(test): do not have both `s` and `server`, renamed to `server` for consistency  c1c4402\r\n- test: 100% test coverage  8bf4c14\r\n- refactor(TestAgent): removed the host param when creating `Test` object  1c8930d\r\n- feat(TestAgent): decoupled TestAgent and superagent&#39;s Agent to support more than `ca`, `key`, `cert`  5e23869\r\n- fix(TestAgent): attach cookies to agent after plugin is used  6d9b9cb\r\n- Update promise example to work with Mocha  5862fe6\r\n- fix: add stacktrace to failed expect function call  3dba4e9\r\n- test: add failing test  b24da85\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.2.4...v6.3.0","2022-10-04T02:55:12",{"id":208,"version":209,"summary_zh":210,"released_at":211},136595,"v6.2.4","- chore: bump deps  f411b02\r\n- chore: bump deps  5e54761\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.2.3...v6.2.4","2022-07-01T23:19:48",{"id":213,"version":214,"summary_zh":215,"released_at":216},136596,"v6.2.3","- chore: bump deps  6329e2d\r\n- Merge pull request #759 from francescobarbera\u002Fmaster  e6d371c\r\n- docs: add an example for complex object in multipart upload  729e818\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.2.2...v6.2.3","2022-04-26T23:55:26",{"id":218,"version":219,"summary_zh":220,"released_at":221},136597,"v6.2.2","- chore: bump deps  8f4dd1e\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.2.1...v6.2.2","2022-01-18T22:00:34",{"id":223,"version":224,"summary_zh":225,"released_at":226},136598,"v6.2.1","- chore: bump deps  9a87305\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.2.0...v6.2.1","2022-01-11T22:26:32",{"id":228,"version":229,"summary_zh":230,"released_at":231},136599,"v6.2.0","- chore: bump deps  e741cab\r\n- Merge pull request #653 from torhovland\u002Fpatch-1  88dc262\r\n- Merge pull request #713 from jtomaszewski\u002Fpatch-1  70a3d00\r\n- Merge pull request #749 from jimmywarting\u002Fmisc  a70c1a0\r\n- var to const  aae4d8e\r\n- add missing comma  f17be2a\r\n- use explicit path  cdb0ad9\r\n- only use require in the top of the file  19dcd1e\r\n- don&#39;t mixup superagent.Response \\w fetch.Response  1dc25a1\r\n- user super and arrow fn  d2d9616\r\n- move exports to the bottom  17cf193\r\n- more const  a0a5d57\r\n- Merge pull request #748 from jimmywarting\u002Fvar  1d70492\r\n- destruct assert  dc60d5d\r\n- refactor: convert var to const \u002F let  03f0084\r\n- Merge pull request #747 from jimmywarting\u002Fclassify  afecc3f\r\n- refactor: Convert test to a class  d94ffbc\r\n- Update README.md  ad699ed\r\n- docs: Add async\u002Fawait example to README  fc269b6\r\n- Add missing require.  3537e4e\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.1.6...v6.2.0","2022-01-10T22:43:15",{"id":233,"version":234,"summary_zh":235,"released_at":236},136600,"v6.1.6","- Merge pull request #735 from benjosantony\u002Fmaster  28116f9\r\n- Add on to the mitigation in https:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fpull\u002F728\u002Fcommits to support empty array response body  ed0f68d\r\n\r\nhttps:\u002F\u002Fgithub.com\u002Fvisionmedia\u002Fsupertest\u002Fcompare\u002Fv6.1.5...v6.1.6","2021-08-17T20:16:38"]