implement-unit-testing-script
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplement Unit Testing Script
实现单元测试脚本
This skill produces a single executable script that runs the unit tests for a generated build folder, following a consistent, language-agnostic pattern.
The reference implementation is assets/run_unittests_java.sh. Read it first — every script you produce must be a faithful translation of that pattern into the target language's tooling and the user's shell environment. There are also Windows PowerShell equivalents of these scripts in assets/run_unittests_*.ps1.
本方案会生成一个可执行脚本,用于运行生成构建文件夹中的单元测试,遵循一致的、与语言无关的模式。
参考实现为 assets/run_unittests_java.sh。请先阅读该脚本——你编写的每个脚本都必须将该模式忠实地转换为目标语言的工具以及用户的Shell环境。assets目录中还有这些脚本对应的Windows PowerShell版本:assets/run_unittests_*.ps1。
Pick the Shell First
先选择Shell
Before writing anything, decide which shell flavor the script must target — it depends on the user's environment, not on the language:
- Bash () — macOS, Linux, WSL, CI runners on Linux. Default unless the user is on native Windows.
.sh - PowerShell () — native Windows / PowerShell-only environments.
.ps1
If you can't tell from the project (no obvious OS hints, no existing scripts), ask the user.
The same seven-step pattern applies to both. Only the syntax changes.
编写脚本前,先确定脚本必须适配的Shell类型——这取决于用户的环境,而非编程语言:
- Bash()——适用于macOS、Linux、WSL、Linux环境下的CI运行器。除非用户使用原生Windows环境,否则默认使用该类型。
.sh - PowerShell()——适用于原生Windows / 仅支持PowerShell的环境。
.ps1
如果无法从项目中判断(无明显的系统提示、无现有脚本),请询问用户。
两种Shell都适用相同的七步模式,仅语法有所不同。
The Pattern
核心模式
Every testing script must implement these steps in this order:
- Toolchain check. Verify that the required language runtime / build tool (and the required version, if any) is installed. If not, print an error and exit with code .
69 - Argument validation. Require exactly one positional argument: the source build folder name. If missing, print usage and exit with code .
1 - Working directory setup. Define a working folder at . If it exists, wipe its contents; otherwise create it. This folder — and only this folder — is where every subsequent write must land.
.tmp/<lang>_<arg> - Copy the build. Recursively copy everything from the source folder into the working folder. After this step the source folder () is treated as read-only for the rest of the script.
$1 - Enter the working directory. /
cdintoSet-Location. If that fails, exit with code.tmp/<lang>_<arg>. All remaining steps run from inside the working folder; they must never write back to the source build folder.2 - Install dependencies into an isolated environment inside . Set up a per-working-folder dependency location (a Python venv at
.tmp/<lang>_<arg>, a local./.venv, a project-scoped Maven repo at./node_modules, etc.) and install/resolve all dependencies into it. Never install into the source build folder, the user's global cache (./.m2, system-wide~/.m2,pip,~/.cargo, ...), or anywhere outside~/.npm. If the install command fails, propagate its exit code immediately and do not proceed to step 7. See Dependency isolation for per-language specifics..tmp/<lang>_<arg> - Run the tests. Invoke the language's standard test command (e.g. ,
mvn test,pytest,npm test,go test ./...), pointed at the same isolated environment from step 6. The script's final exit code is whatever the test command returns.cargo test
每个测试脚本都必须按顺序实现以下步骤:
- 工具链检查。验证所需的语言运行时/构建工具(以及所需版本,如果有要求)已安装。若未安装,打印错误信息并以代码退出。
69 - 参数验证。要求必须传入一个位置参数:源构建文件夹名称。若缺失,打印使用说明并以代码退出。
1 - 工作目录设置。在路径下定义工作文件夹。若该文件夹已存在,清空其内容;若不存在,则创建它。后续所有写入操作必须仅在该文件夹内进行。
.tmp/<lang>_<arg> - 复制构建内容。将源文件夹中的所有内容递归复制到工作文件夹中。完成此步骤后,源文件夹()在脚本剩余部分中被视为只读。
$1 - 进入工作目录。使用/
cd命令进入Set-Location。若操作失败,以代码.tmp/<lang>_<arg>退出。剩余所有步骤均在工作文件夹内执行;绝对不能写回源构建文件夹。2 - 在内的隔离环境中安装依赖。设置一个基于工作文件夹的依赖位置(如Python的venv位于
.tmp/<lang>_<arg>,本地./.venv,项目范围的Maven仓库位于./node_modules等),并将所有依赖安装/解析到该位置。绝对不能将依赖安装到源构建文件夹、用户的全局缓存(./.m2、系统级~/.m2、pip、~/.cargo等)或~/.npm之外的任何位置。若安装命令失败,立即传递其退出代码,不要继续执行步骤7。有关各语言的具体细节,请查看依赖隔离部分。.tmp/<lang>_<arg> - 运行测试。调用语言的标准测试命令(如、
mvn test、pytest、npm test、go test ./...),指向步骤6中创建的隔离环境。脚本的最终退出代码即为测试命令返回的代码。cargo test
The build folder is read-only — hard rule
构建文件夹为只读——硬性规则
The source build folder passed in as is input only. The script must never:
$1- install dependencies into it (no inside
pip install, no$1insidenpm install, no$1writing intomvn install, no Cargo build artifacts ending up under$1),$1 - write a virtualenv / /
node_modules/.m2/.gocachedirectory inside it,.cargo - run the test command from inside it (every test command runs from inside after the
.tmp/<lang>_<arg>in step 5),cd - create logs, caches, build outputs, or temp files inside it.
The build folder is shared with the renderer ( by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. Every write must go into — the whole point of staging via is so the source build folder stays a clean, reproducible artifact of the render.
plain_modules/....tmp/<lang>_<arg>.tmpIf you find yourself about to issue any command whose is the source folder, or whose target path starts with , stop. Either move the operation into , or you're doing something the script must not do.
cwd$1/.tmp/<lang>_<arg>作为传入的源构建文件夹仅作为输入。脚本绝对不能:
$1- 在其中安装依赖(不能在内执行
$1、pip install、npm install,不能让Cargo构建产物存放在mvn install下),$1 - 在其中创建虚拟环境//
node_modules/.m2/.gocache目录,.cargo - 在其中运行测试命令(所有测试命令必须在步骤5的操作后,在
cd内执行),.tmp/<lang>_<arg> - 在其中创建日志、缓存、构建输出或临时文件。
构建文件夹与渲染器(默认是)和下游工具共享。向其中写入内容会破坏渲染器对“已生成内容”的认知,并导致后续渲染失败。所有写入操作必须进入——通过目录暂存的核心目的就是让源构建文件夹保持为干净、可复现的渲染产物。
plain_modules/....tmp/<lang>_<arg>.tmp如果你发现自己要执行的命令的当前工作目录是源文件夹,或者目标路径以开头,请立即停止。要么将操作移至内,要么该操作属于脚本禁止的行为。
$1/.tmp/<lang>_<arg>Conventions
约定
Shared across both shell flavors:
- Exit codes:
- — bad usage (missing argument).
1 - — filesystem problem (couldn't enter the working folder).
2 - — required toolchain / runtime is not installed.
69 - Any other non-zero code — propagated from the underlying test command.
- Working folder naming: where
.tmp/<lang>_<arg>is a short identifier for the language (<lang>,java,python,node,go, ...). All dependency installs, build outputs, caches, and the test run itself live inside this folder. Nothing the script does should touch the source build folder after step 4.rust - Logging: print short progress lines (,
"Copied from ... to ...","Installing dependencies into ...") so failures are easy to triage."Running <lang> unittests in ..."
两种Shell类型共享以下约定:
- 退出代码:
- ——使用错误(缺失参数)。
1 - ——文件系统问题(无法进入工作文件夹)。
2 - ——所需工具链/运行时未安装。
69 - 其他非零代码——从底层测试命令传递而来。
- 工作文件夹命名:,其中
.tmp/<lang>_<arg>是编程语言的短标识符(<lang>、java、python、node、go等)。所有依赖安装、构建输出、缓存和测试运行本身都存放在该文件夹内。脚本执行的任何操作都不得在步骤4之后触碰源构建文件夹。rust - 日志:打印简短的进度信息(如、
"已从...复制到..."、"正在向...安装依赖"),以便快速排查故障。"正在...中运行<lang>单元测试"
Dependency isolation
依赖隔离
The dependency environment must live inside so the test run can't be polluted by — or pollute — the user's global caches. Pick the most idiomatic isolation mechanism for the language:
$WORKING_FOLDER| Language | Isolation mechanism | Install command (run inside | Test command |
|---|---|---|---|
| Python | | | |
| Node.js | local | | |
| Java | project-scoped Maven repo at | | |
| Go | module cache at | | |
| Rust | cargo home at | | |
Notes:
- Every path in the install command and test command is relative to . That's why the script
.tmp/<lang>_<arg>s into the working folder in step 5 — from that point on,cd,./.venv,./node_modules, etc. all resolve under./.m2, never under the source build folder..tmp/<lang>_<arg> - Always pass the isolation flag/env var to both the install command and the test command — they must agree on where deps live, otherwise the test command will silently fall back to the global cache or (worse) the source build folder.
- Python is the only ecosystem where the venv is mandatory to satisfy "into a virtual environment" literally. The others use language-native equivalents that achieve the same isolation.
- Pre-warming is optional for Java/Go/Rust — their test commands will fetch deps on demand. Doing it as a separate step makes failures easier to diagnose and gives a clean "install failed vs test failed" signal.
- Don't activate the venv in Bash via — call
source .venv/bin/activatedirectly. It's more portable and avoids subshell weirdness. In PowerShell, use./.venv/bin/<tool>similarly.& .\.venv\Scripts\<tool>.exe - Propagate the install exit code immediately. In Bash: . In PowerShell: check
<install cmd> || exit $?and$LASTEXITCODEif non-zero.exit $LASTEXITCODE
依赖环境必须位于内部,这样测试运行就不会被用户的全局缓存污染,也不会污染全局缓存。为每种语言选择最符合惯例的隔离机制:
$WORKING_FOLDER| 语言 | 隔离机制 | 安装命令(在 | 测试命令 |
|---|---|---|---|
| Python | | | |
| Node.js | 本地 | | |
| Java | 项目范围的Maven仓库位于 | | |
| Go | 模块缓存位于 | | |
| Rust | Cargo主目录位于 | | |
注意事项:
- 安装命令和测试命令中的所有路径均相对于。这就是脚本在步骤5中要进入工作文件夹的原因——从此时起,
.tmp/<lang>_<arg>、./.venv、./node_modules等路径都指向./.m2下,绝不会指向源构建文件夹。.tmp/<lang>_<arg> - 始终将隔离标志/环境变量同时传递给安装命令和测试命令——它们必须一致依赖的存储位置,否则测试命令会静默回退到全局缓存或(更糟的情况)源构建文件夹。
- Python是唯一强制要求使用venv的生态系统,以严格满足“在虚拟环境中”的要求。其他语言使用原生的等效机制来实现相同的隔离效果。
- Java/Go/Rust的预加载是可选的——它们的测试命令会按需获取依赖。将其作为单独步骤执行可以更轻松地诊断故障,并清晰区分“安装失败”与“测试失败”。
- 不要在Bash中通过激活venv——直接调用
source .venv/bin/activate。这种方式更具可移植性,避免子Shell的异常问题。在PowerShell中,类似地使用./.venv/bin/<tool>。& .\\.venv\\Scripts\\<tool>.exe - 立即传递安装命令的退出代码。在Bash中:。在PowerShell中:检查
<install cmd> || exit $?,若不为零则执行$LASTEXITCODE。exit $LASTEXITCODE
Bash specifics
Bash特定规则
- Shebang: .
#!/bin/bash - File naming: , placed in
run_unittests_<lang>.sh.assets/ - Argument: .
$1 - Make it executable: .
chmod +x assets/run_unittests_<lang>.sh
- Shebang:。
#!/bin/bash - 文件命名:,存放在
run_unittests_<lang>.sh目录下。assets/ - 参数:。
$1 - 设置可执行权限:。
chmod +x assets/run_unittests_<lang>.sh
PowerShell specifics
PowerShell特定规则
- No shebang. Use a block at the top instead.
param([Parameter(Mandatory=$true)][string]$Subfolder) - File naming: , placed in
run_unittests_<lang>.ps1.assets/ - Exit codes: use etc. (PowerShell honors them just like Bash).
exit 69 - Toolchain check: prefer and, where a specific version is needed, parse the tool's
Get-Command <tool> -ErrorAction SilentlyContinueoutput.--version - Filesystem: use ,
Test-Path,Remove-Item -Recurse -Force,New-Item -ItemType Directory,Copy-Item -Recurse. Quote paths to handle spaces.Set-Location - No step needed. If execution policy is likely to block the script, mention
chmodto the user — don't bake it into the script.Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
- 无需Shebang。在脚本顶部使用块。
param([Parameter(Mandatory=$true)][string]$Subfolder) - 文件命名:,存放在
run_unittests_<lang>.ps1目录下。assets/ - 退出代码:使用等(PowerShell与Bash一样支持退出代码)。
exit 69 - 工具链检查:优先使用,若需要特定版本,则解析工具的
Get-Command <tool> -ErrorAction SilentlyContinue输出。--version - 文件系统操作:使用、
Test-Path、Remove-Item -Recurse -Force、New-Item -ItemType Directory、Copy-Item -Recurse。对路径添加引号以处理空格。Set-Location - 无需执行步骤。若执行策略可能阻止脚本运行,请告知用户执行
chmod——不要将此步骤写入脚本。Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
Workflow
工作流程
- Confirm the target language, shell flavor (Bash or PowerShell), and dependency manifest (,
pom.xml/requirements.txt,pyproject.toml,package.json,go.mod, ...). Ask if any is unclear.Cargo.toml - Read assets/run_unittests_java.sh to refresh the exact structure.
- Translate each of the seven steps above into the equivalent commands for the target language and shell. The toolchain check, dependency install, and test invocation are the language-specific parts; the rest is mechanical translation between Bash and PowerShell syntax.
- Pick the dependency-isolation mechanism from the Dependency isolation table and use it consistently in both step 6 and step 7.
- Save the new script to or
assets/run_unittests_<lang>.sh. For Bash,assets/run_unittests_<lang>.ps1it.chmod +x
- 确认目标语言、Shell类型(Bash或PowerShell)和依赖清单(、
pom.xml/requirements.txt、pyproject.toml、package.json、go.mod等)。若有不明确之处,请询问用户。Cargo.toml - 阅读assets/run_unittests_java.sh以明确准确的结构。
- 将上述七个步骤转换为目标语言以及对应Shell的等效命令。工具链检查、依赖安装和测试调用是与语言相关的部分;其余部分是Bash与PowerShell语法之间的机械转换。
- 从依赖隔离表中选择依赖隔离机制,并在步骤6和步骤7中一致使用。
- 将新脚本保存到或
assets/run_unittests_<lang>.sh。对于Bash脚本,执行assets/run_unittests_<lang>.ps1设置可执行权限。chmod +x
Anti-Patterns
反模式
- (Hard mistake) Don't install into, build into, or otherwise write to the source build folder. The build folder passed as is read-only input. Every install, cache, build artifact, log, and temp file must land in
$1. This includes never running.tmp/<lang>_<arg>,pip install,npm install, ormvn installwith the source folder as theircargo buildor target, never letting a venv /cwd/node_modules/.m2/.gocachedirectory appear inside the source folder, and never running the test command from inside it. The whole point of staging the build into.cargois so the source folder remains a clean, reproducible artifact of the render — writing to it corrupts the renderer's view and breaks subsequent renders..tmp/ - Don't skip the toolchain check, even when "everyone has it installed" — exit code is what the calling system relies on to detect a missing runtime.
69 - Don't reuse the source folder in place. Always copy into first; the renderer relies on this isolation.
.tmp/<lang>_<arg> - Don't change the exit-code contract. Other parts of the system branch on ,
1, and2specifically — and these codes must be identical between the Bash and PowerShell variants.69 - Don't write a cross-shell hybrid (e.g. a that detects PowerShell, or vice versa). Ship one script per shell, named with the appropriate extension.
.sh - Don't install dependencies into the user's global location (, system-wide
~/.m2,pip, etc.). Always isolate inside~/.cargoso concurrent runs and other projects can't interfere.$WORKING_FOLDER - Don't run the test command without first verifying the install step succeeded. A failed install followed by a "test" run produces misleading errors that look like test failures.
- (严重错误)不要向源构建文件夹中安装、构建或写入任何内容。作为传入的构建文件夹是只读输入。所有安装、缓存、构建产物、日志和临时文件必须存放在
$1中。这包括绝对不能在源文件夹作为当前工作目录或目标路径的情况下执行.tmp/<lang>_<arg>、pip install、npm install或mvn install,绝对不能让虚拟环境/cargo build/node_modules/.m2/.gocache目录出现在源文件夹内,绝对不能在源文件夹内运行测试命令。将构建内容暂存到.cargo目录的核心目的就是让源文件夹保持为干净、可复现的渲染产物——向其中写入内容会破坏渲染器的认知,并导致后续渲染失败。.tmp/ - 不要跳过工具链检查,即使“所有人都已安装”——退出代码是调用系统检测缺失运行时的依据。
69 - 不要直接复用源文件夹。始终先将内容复制到中;渲染器依赖这种隔离机制。
.tmp/<lang>_<arg> - 不要修改退出代码约定。系统的其他部分会根据、
1和2进行分支处理——且这些代码在Bash和PowerShell版本中必须完全一致。69 - 不要编写跨Shell的混合脚本(例如检测PowerShell的脚本,反之亦然)。为每种Shell单独提供一个脚本,并使用相应的扩展名命名。
.sh - 不要将依赖安装到用户的全局位置(、系统级
~/.m2、pip等)。始终在~/.cargo内进行隔离,这样并发运行和其他项目就不会相互干扰。$WORKING_FOLDER - 不要在未验证安装步骤成功的情况下运行测试命令。安装失败后执行“测试”运行会产生误导性错误,看起来像是测试失败。",