Skip to main content

jsdlc-unit-test

Write unit tests for code that already exists, in a Japanese waterfall project — read the source, enumerate its real branches, produce a UT specification and the test code, and run them. Use when the user asks to add unit tests to existing or legacy code, 既存コードの単体テスト, backfill tests, raise coverage, or test a module that has no detail design.

Source facts

Repository
phuc-nt/jsdlc-kit
Last source activity
September 16, 2026 at 16:51
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
jsdlc-unit-test
description
Write unit tests for code that already exists, in a Japanese waterfall project — read the source, enumerate its real branches, produce a UT specification and the test code, and run them. Use when the user asks to add unit tests to existing or legacy code, 既存コードの単体テスト, backfill tests, raise coverage, or test a module that has no detail design.
# Unit Tests for Existing Code (既存コードの単体テスト) Request / arguments: $ARGUMENTS Mode is the first word: `create` (default), `review`, `modify`. The rest names what to test: a file, a class, a package, or a design ID. Read before acting: - the source files under test — this skill's primary input - `docs-jsdlc/templates/test-case.md` - `docs-jsdlc/rules/review-checklist.md` §2 and §6 - the project profile `.kiro/steering/jsdlc-project-profile.md` §2–§4 (source and test roots, framework, mocking approach, test command, trace tag). A `<...>` placeholder in one of those stops this skill: report it, raise `Q-NNN`, write no tests --- ## How this differs from `jsdlc-implement` | | `jsdlc-implement` | this skill | |---|---|---| | Primary input | an approved detail design | the existing source code | | Design required | yes — no parent, no code | no — optional evidence | | Writes product code | yes | **never** | | Produces | product code + its tests | UT specification + test code | Use `jsdlc-implement` when the code is being written now from a design. Use this skill when the code is already running and has no tests, or too few — which is the ordinary case on a system under maintenance. **This skill never edits product code.** Not to make it testable, not to fix a bug the tests expose, not to add a seam. Changing the code under test in the same breath as writing its tests destroys the only evidence the tests provide: that they pass against the code as it stands today. When the code cannot be tested as written, or a test exposes a defect, that is a finding and a `Q-NNN`, and the change goes through `jsdlc-implement` afterwards. --- ## The trap in this work Tests written from code alone cannot tell correct behaviour from current behaviour. Read a branch, write a test asserting it does what it does, and the test passes — while proving nothing except that the code has not changed. A bug gets frozen into an assertion and the next person treats it as the contract. So the whole skill turns on one distinction, kept visible in the output: - **Confirmed** — the expected value comes from a detail design, a basic design message list, an API spec, a client sheet named in profile §8, or a decision recorded in the open-questions register. This is a real test. - **Characterisation** — the expected value comes only from reading the code. It locks in today's behaviour so a refactor cannot change it silently. It is useful, and it is not a correctness proof. Every case carries one of the two. Never silently promote a characterisation case to confirmed because the behaviour looked reasonable. --- ## Mode: create ### Step 1 — Establish what evidence exists Search for a detail design covering this code, by trace tag (`@design DD-...` in the profile §4 form) and by ID. Record which of these you have: - detail design → expected values are Confirmed; follow Step 2b as well - basic design message list → error codes are Confirmed - API spec → the boundary contract is Confirmed - nothing → every case is Characterisation until a person decides otherwise Say which, in one line, at the top of the report. A reader's first question is how much of this is evidence and how much is a snapshot. ### Step 2 — Enumerate the code's real behaviour Read the code completely before writing anything. Enumerate, mechanically — this list is the coverage obligation, and it comes from the code, not from imagination: - every branch: each `if` / `else` / `switch` arm, each ternary, each short-circuit that changes the outcome - every loop: the zero-iteration, one-iteration and many-iteration paths - every `throw` / `raise` / error return, with the code or type it carries - every early return, and the condition that reaches it - every caught exception, and what the handler actually does — a swallowed exception is a finding, whether or not it is designed - every nullable or optional parameter: null / empty / zero behaviour as the code treats it today - every numeric or temporal comparison, with its inclusivity as written (`<` vs `<=`) — this is where characterisation tests earn their keep - every external call to be mocked: what the code does with success, with each failure, and with an unexpected value Count them. The count of error exits in the code is the minimum number of 異常系 cases, exactly as it is in `jsdlc-implement` Step 3. ### Step 2b — Reconcile with the design, when one exists Compare the enumeration against the design, both directions: - in the design, absent from the code → the code is incomplete - in the code, absent from the design → over-implementation (I-04) - in both, but disagreeing on a value, a code, or an inclusivity → a mismatch **Do not choose a side.** Each difference is a `Q-NNN` naming both readings and the provisional one, exactly as `jsdlc-implement` Mode: modify step 3 requires. Write the test against the code's behaviour, mark the case Characterisation, and cite the question in the case's note. When the question is decided, the case is revisited — that is what `modify` mode is for. ### Step 3 — Write the UT specification Use `docs-jsdlc/templates/test-case.md`, phase UT. Build it from the Step 2 enumeration: viewpoints (テスト観点) grouped into 正常系 / 異常系 / 境界値, then one row per case with precondition, operation, expected result. The template's §4 note covers the two adjustments this phase needs: `Target` holds the source location when there is no design item to cite, and each case table carries an `Evidence` column — `Confirmed (DD-RSV-001 §7)` or `Characterisation`. Fill both on every case. Quality bars are the ordinary UT ones (`review-checklist.md` §6): expected results are observable and concrete (T-03, T-04), every error exit appears in a 異常系 case (T-05), every limit gets below / at / above (T-06). Trace: when a design exists, each case cites its `DD-<DOM>-<nnn>` item. When one does not, the case cites the source location instead, and the coverage table (T-02) is against the enumeration rather than against design items. Say so in the document — a coverage table with no stated basis is worthless. Save to `docs-jsdlc/output/test-case/UT-<DOM>-<nnn>-<slug>.md`. Codes come from `docs-jsdlc/project/domain-codes.md` and are fixed once assigned (profile §6). When this code covers an area with no domain code, that file is edited first — raise `Q-NNN` naming the area and stop; never invent a code to get a filename. ### Step 4 — Write the test code Test root and naming come from profile §2; framework, mocking approach and test command from profile §3. Follow the test style already in the repository over any style you prefer (I-06). If no tests exist at all, follow the framework's ordinary convention and say in the report that you set the precedent. - Each test name carries its case ID, so the mapping to the specification is mechanical. - Characterisation cases are marked in the code too — a comment carrying the case ID and the word `characterisation`, so a later reader knows the assertion is a snapshot, not a contract. - Mock only what profile §3 says is mocked. Reaching a real database or network in a UT makes it a different test phase. - No test depends on another's order or leftovers (T-08). - Never weaken an assertion to make a test pass. ### Step 5 — Run them Run the test command from profile §3. Report the actual output. A failing test at this point means one of three things, and which one matters: 1. the test is wrong → fix the test 2. the code has a defect → **leave the test failing**, raise `Q-NNN`, report it as a defect found. Do not adjust the assertion to green, and do not fix the code here 3. the code contradicts the design → Step 2b, a mismatch, not a fix Report coverage if profile §3 names a coverage command — run that command, do not estimate. Where §3 states a coverage expectation, say whether the run meets it. Report the number, not an impression, and state what it was before if that is known. ### Step 6 — Report - What evidence existed (Step 1), in the first line - Enumerated items: branches, error exits, boundaries — the counts - Cases written, split Confirmed / Characterisation - Test results: passed, failed, and for each failure which of the three kinds - Coverage before and after, if measured - Every `Q-NNN` raised, and every defect found in the code under test A run that produces mostly Characterisation cases is a normal outcome on legacy code. Report it plainly. It is not a weaker result — it is an accurate one. --- ## Mode: review Do not edit. Review existing unit tests against the code they cover, in both directions: **Code → tests:** every branch, error exit and boundary from a fresh Step 2 enumeration has at least one case. Gaps are the finding, listed by source location. **Tests → code:** every test still corresponds to code that exists. A test for a deleted branch, or one passing because it asserts nothing meaningful, is a finding. So is a test whose assertion was weakened to pass — check the history when it is available. Also check: assertions are concrete (T-04); Characterisation cases are marked as such, and none has been quietly promoted to Confirmed; mocks match profile §3; no test depends on execution order (T-08); tests actually run under the profile §3 command. Run the tests as part of the review and report the real result. Write to `docs-jsdlc/output/review/UT-<DOM>-<nnn>-review-<YYYYMMDD>.md`. --- ## Mode: modify 1. Fix every NG from a review. Each change traces to a finding number. 2. When a `Q-NNN` from Step 2b has been decided, revisit the cases citing it: the expected value becomes the decided one, the case becomes Confirmed, and the question row is closed with the decider and date. If the decision means the code is wrong, the test now fails — that is correct, and the code change goes through `jsdlc-implement`, not here. 3. Never delete a failing test to close a finding. A test that fails because the code is defective stays, with its `Q-NNN`. 4. Re-run the profile §3 test command and report the real result against the previous one. 5. Report: findings fixed, questions closed, cases promoted from Characterisation to Confirmed, and test results before and after.
View on GitHub