# shouldpy
> *"已有的测试该怎么写就怎么写,AI 再帮你加一道自然语言安检。"*
[](https://pypi.org/project/shouldpy/)
[](https://pypi.org/project/shouldpy/)
[](LICENSE)
[](https://github.com/zhixiangxue/should-ai/actions)
[](https://pypi.org/project/shouldpy/)
## ❓ 这是什么?
仅200多行的小工具:给现有测试函数加一层 **@should("自然语言期望")** 装饰器
AI 会在函数跑完后 **检查** 所有日志、print 输出、返回值,
判断“期望”是否达成 —— **PASS** 安静通过;**FAIL: 原因** 抛 `AssertionError`。
> 不替代 `assert`,不强制 `return`,不改造`testcase`;
> 仅用一个装饰器让 **LLM 再帮你把把关**。
---
## ⚡️ 安装
```bash
pip install shouldpy
# 其他依赖,按需安装
pip install pytest-asyncio
pip install langchain-openai
```
---
## 👨🏻💻 例子
```python
from should import should
from langchain_openai import ChatOpenAI
should.use(ChatOpenAI()) # 指定你的llm
@should("日志里应该明确表示下单成功")
def test_create_order():
... # 原来的测试代码,该怎么写怎么写
logging.info("订单创建成功")
assert resp.status_code == 200
```
跑 `pytest` →
- 原有 `assert` 检查状态码
- AI 额外检查日志/输出/返回值里有没有“订单创建成功”之类的输出
两步都过才算通过。
---
## ⚠️ 注意
1. **需要自备** LangChain 兼容 LLM(OpenAI、DeepSeek、Ollama…)。
2. **不保证确定性** → 适合**教学、脚本、探索式测试、兜底方案**,别拿它当核心断言。
3. **每次都要调模型** → **慢 + 花钱 + 数据安全** → 别塞进高频 CI;本地跑、CR 前抽查更划算;token敏感或者数据敏感的建议用本地模型
---
## 🤝 反馈
[https://github.com/zhixiangxue/should-ai](https://github.com/zhixiangxue/should-ai)
issue、PR、star 欢迎!
---
## 📄 License
MIT © 2025 zx
Raw data
{
"_id": null,
"home_page": null,
"name": "shouldpy",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "testing, ai, assertion, decorator, llm, natural-language",
"author": null,
"author_email": "zx <your-email@example.com>",
"download_url": "https://files.pythonhosted.org/packages/96/54/600a8d43b615e5f1ad1fd2aebd117c26971a9a05d67d72bbd1065cd239b0/shouldpy-1.0.1.tar.gz",
"platform": null,
"description": "# shouldpy\n> *\"\u5df2\u6709\u7684\u6d4b\u8bd5\u8be5\u600e\u4e48\u5199\u5c31\u600e\u4e48\u5199\uff0cAI \u518d\u5e2e\u4f60\u52a0\u4e00\u9053\u81ea\u7136\u8bed\u8a00\u5b89\u68c0\u3002\"*\n\n[](https://pypi.org/project/shouldpy/)\n[](https://pypi.org/project/shouldpy/)\n[](LICENSE)\n[](https://github.com/zhixiangxue/should-ai/actions)\n[](https://pypi.org/project/shouldpy/)\n\n## \u2753 \u8fd9\u662f\u4ec0\u4e48\uff1f\n\u4ec5200\u591a\u884c\u7684\u5c0f\u5de5\u5177\uff1a\u7ed9\u73b0\u6709\u6d4b\u8bd5\u51fd\u6570\u52a0\u4e00\u5c42 **@should(\"\u81ea\u7136\u8bed\u8a00\u671f\u671b\")** \u88c5\u9970\u5668\nAI \u4f1a\u5728\u51fd\u6570\u8dd1\u5b8c\u540e **\u68c0\u67e5** \u6240\u6709\u65e5\u5fd7\u3001print \u8f93\u51fa\u3001\u8fd4\u56de\u503c\uff0c \n\u5224\u65ad\u201c\u671f\u671b\u201d\u662f\u5426\u8fbe\u6210 \u2014\u2014 **PASS** \u5b89\u9759\u901a\u8fc7\uff1b**FAIL: \u539f\u56e0** \u629b `AssertionError`\u3002 \n\n> \u4e0d\u66ff\u4ee3 `assert`\uff0c\u4e0d\u5f3a\u5236 `return`\uff0c\u4e0d\u6539\u9020`testcase`\uff1b \n> \u4ec5\u7528\u4e00\u4e2a\u88c5\u9970\u5668\u8ba9 **LLM \u518d\u5e2e\u4f60\u628a\u628a\u5173**\u3002\n\n---\n\n## \u26a1\ufe0f \u5b89\u88c5\n\n```bash\npip install shouldpy\n\n# \u5176\u4ed6\u4f9d\u8d56\uff0c\u6309\u9700\u5b89\u88c5\npip install pytest-asyncio\npip install langchain-openai\n```\n\n---\n\n## \ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb \u4f8b\u5b50\n\n```python\nfrom should import should\nfrom langchain_openai import ChatOpenAI\n\nshould.use(ChatOpenAI()) # \u6307\u5b9a\u4f60\u7684llm\n\n@should(\"\u65e5\u5fd7\u91cc\u5e94\u8be5\u660e\u786e\u8868\u793a\u4e0b\u5355\u6210\u529f\")\ndef test_create_order():\n ... # \u539f\u6765\u7684\u6d4b\u8bd5\u4ee3\u7801\uff0c\u8be5\u600e\u4e48\u5199\u600e\u4e48\u5199\n logging.info(\"\u8ba2\u5355\u521b\u5efa\u6210\u529f\")\n assert resp.status_code == 200\n```\n\n\u8dd1 `pytest` \u2192 \n- \u539f\u6709 `assert` \u68c0\u67e5\u72b6\u6001\u7801 \n- AI \u989d\u5916\u68c0\u67e5\u65e5\u5fd7/\u8f93\u51fa/\u8fd4\u56de\u503c\u91cc\u6709\u6ca1\u6709\u201c\u8ba2\u5355\u521b\u5efa\u6210\u529f\u201d\u4e4b\u7c7b\u7684\u8f93\u51fa \n\u4e24\u6b65\u90fd\u8fc7\u624d\u7b97\u901a\u8fc7\u3002\n\n---\n\n## \u26a0\ufe0f \u6ce8\u610f\n\n1. **\u9700\u8981\u81ea\u5907** LangChain \u517c\u5bb9 LLM\uff08OpenAI\u3001DeepSeek\u3001Ollama\u2026\uff09\u3002 \n2. **\u4e0d\u4fdd\u8bc1\u786e\u5b9a\u6027** \u2192 \u9002\u5408**\u6559\u5b66\u3001\u811a\u672c\u3001\u63a2\u7d22\u5f0f\u6d4b\u8bd5\u3001\u515c\u5e95\u65b9\u6848**\uff0c\u522b\u62ff\u5b83\u5f53\u6838\u5fc3\u65ad\u8a00\u3002 \n3. **\u6bcf\u6b21\u90fd\u8981\u8c03\u6a21\u578b** \u2192 **\u6162 + \u82b1\u94b1 + \u6570\u636e\u5b89\u5168** \u2192 \u522b\u585e\u8fdb\u9ad8\u9891 CI\uff1b\u672c\u5730\u8dd1\u3001CR \u524d\u62bd\u67e5\u66f4\u5212\u7b97\uff1btoken\u654f\u611f\u6216\u8005\u6570\u636e\u654f\u611f\u7684\u5efa\u8bae\u7528\u672c\u5730\u6a21\u578b\n\n---\n\n## \ud83e\udd1d \u53cd\u9988\n[https://github.com/zhixiangxue/should-ai](https://github.com/zhixiangxue/should-ai) \nissue\u3001PR\u3001star \u6b22\u8fce\uff01\n\n---\n\n## \ud83d\udcc4 License\nMIT \u00a9 2025 zx\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "AI-driven test assertion decorator for natural language testing",
"version": "1.0.1",
"project_urls": {
"Bug Tracker": "https://github.com/zhixiangxue/should-ai/issues",
"Changelog": "https://github.com/zhixiangxue/should-ai/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/zhixiangxue/should-ai#readme",
"Homepage": "https://github.com/zhixiangxue/should-ai",
"Repository": "https://github.com/zhixiangxue/should-ai"
},
"split_keywords": [
"testing",
" ai",
" assertion",
" decorator",
" llm",
" natural-language"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a90e2895112b3e3894a76ee2cecaefea9a59182b938063c1b7abdad4e5b6fbdd",
"md5": "edbcacc7a474a169f75cd6a17873c6fa",
"sha256": "65e839bf6340dcbca82343f3e955bc1a08fca05ae8a31d0f86ad76a90de6c5a8"
},
"downloads": -1,
"filename": "shouldpy-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "edbcacc7a474a169f75cd6a17873c6fa",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 6461,
"upload_time": "2025-09-18T16:28:51",
"upload_time_iso_8601": "2025-09-18T16:28:51.792189Z",
"url": "https://files.pythonhosted.org/packages/a9/0e/2895112b3e3894a76ee2cecaefea9a59182b938063c1b7abdad4e5b6fbdd/shouldpy-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "9654600a8d43b615e5f1ad1fd2aebd117c26971a9a05d67d72bbd1065cd239b0",
"md5": "5fb5ce8548a39be1098d28165d11254a",
"sha256": "4a7cf693daf3968c12c5fcdd2c5a1e661341a8c3600b2a767624052124d5d8ed"
},
"downloads": -1,
"filename": "shouldpy-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "5fb5ce8548a39be1098d28165d11254a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 6662,
"upload_time": "2025-09-18T16:28:53",
"upload_time_iso_8601": "2025-09-18T16:28:53.184100Z",
"url": "https://files.pythonhosted.org/packages/96/54/600a8d43b615e5f1ad1fd2aebd117c26971a9a05d67d72bbd1065cd239b0/shouldpy-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-18 16:28:53",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "zhixiangxue",
"github_project": "should-ai",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "shouldpy"
}