universal-dev-standards 3.5.1-beta.1 → 3.5.1-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/uds.js +2 -0
- package/bundled/core/ai-instruction-standards.md +205 -0
- package/bundled/core/anti-hallucination.md +684 -0
- package/bundled/core/changelog-standards.md +556 -0
- package/bundled/core/checkin-standards.md +935 -0
- package/bundled/core/code-review-checklist.md +684 -0
- package/bundled/core/commit-message-guide.md +915 -0
- package/bundled/core/documentation-structure.md +1117 -0
- package/bundled/core/documentation-writing-standards.md +487 -0
- package/bundled/core/error-code-standards.md +382 -0
- package/bundled/core/git-workflow.md +859 -0
- package/bundled/core/logging-standards.md +323 -0
- package/bundled/core/project-structure.md +354 -0
- package/bundled/core/refactoring-standards.md +636 -0
- package/bundled/core/spec-driven-development.md +207 -0
- package/bundled/core/test-completeness-dimensions.md +536 -0
- package/bundled/core/test-driven-development.md +995 -0
- package/bundled/core/testing-standards.md +3061 -0
- package/bundled/core/versioning.md +902 -0
- package/bundled/locales/README.md +88 -0
- package/bundled/locales/zh-CN/CHANGELOG.md +705 -0
- package/bundled/locales/zh-CN/CLAUDE.md +213 -0
- package/bundled/locales/zh-CN/MAINTENANCE.md +689 -0
- package/bundled/locales/zh-CN/README.md +707 -0
- package/bundled/locales/zh-CN/STANDARDS-MAPPING.md +177 -0
- package/bundled/locales/zh-CN/adoption/ADOPTION-GUIDE.md +392 -0
- package/bundled/locales/zh-CN/adoption/STATIC-DYNAMIC-GUIDE.md +302 -0
- package/bundled/locales/zh-CN/adoption/checklists/enterprise.md +332 -0
- package/bundled/locales/zh-CN/adoption/checklists/minimal.md +141 -0
- package/bundled/locales/zh-CN/adoption/checklists/recommended.md +272 -0
- package/bundled/locales/zh-CN/ai/MAINTENANCE.md +739 -0
- package/bundled/locales/zh-CN/ai/options/changelog/auto-generated.ai.yaml +76 -0
- package/bundled/locales/zh-CN/ai/options/changelog/keep-a-changelog.ai.yaml +99 -0
- package/bundled/locales/zh-CN/ai/options/code-review/automated-review.ai.yaml +120 -0
- package/bundled/locales/zh-CN/ai/options/code-review/pair-programming.ai.yaml +109 -0
- package/bundled/locales/zh-CN/ai/options/code-review/pr-review.ai.yaml +104 -0
- package/bundled/locales/zh-CN/ai/options/commit-message/bilingual.ai.yaml +105 -0
- package/bundled/locales/zh-CN/ai/options/commit-message/english.ai.yaml +79 -0
- package/bundled/locales/zh-CN/ai/options/commit-message/traditional-chinese.ai.yaml +100 -0
- package/bundled/locales/zh-CN/ai/options/documentation/api-docs.ai.yaml +140 -0
- package/bundled/locales/zh-CN/ai/options/documentation/markdown-docs.ai.yaml +89 -0
- package/bundled/locales/zh-CN/ai/options/documentation/wiki-style.ai.yaml +119 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/gitflow.ai.yaml +133 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/github-flow.ai.yaml +73 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/merge-commit.ai.yaml +88 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/rebase-ff.ai.yaml +113 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/squash-merge.ai.yaml +85 -0
- package/bundled/locales/zh-CN/ai/options/git-workflow/trunk-based.ai.yaml +117 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/dotnet.ai.yaml +108 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/go.ai.yaml +115 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/java.ai.yaml +113 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/kotlin.ai.yaml +123 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/nodejs.ai.yaml +101 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/php.ai.yaml +147 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/python.ai.yaml +116 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/ruby.ai.yaml +140 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/rust.ai.yaml +117 -0
- package/bundled/locales/zh-CN/ai/options/project-structure/swift.ai.yaml +143 -0
- package/bundled/locales/zh-CN/ai/options/testing/contract-testing.ai.yaml +254 -0
- package/bundled/locales/zh-CN/ai/options/testing/e2e-testing.ai.yaml +116 -0
- package/bundled/locales/zh-CN/ai/options/testing/industry-pyramid.ai.yaml +144 -0
- package/bundled/locales/zh-CN/ai/options/testing/integration-testing.ai.yaml +90 -0
- package/bundled/locales/zh-CN/ai/options/testing/istqb-framework.ai.yaml +108 -0
- package/bundled/locales/zh-CN/ai/options/testing/performance-testing.ai.yaml +272 -0
- package/bundled/locales/zh-CN/ai/options/testing/security-testing.ai.yaml +160 -0
- package/bundled/locales/zh-CN/ai/options/testing/system-testing.ai.yaml +101 -0
- package/bundled/locales/zh-CN/ai/options/testing/unit-testing.ai.yaml +82 -0
- package/bundled/locales/zh-CN/ai/standards/anti-hallucination.ai.yaml +145 -0
- package/bundled/locales/zh-CN/ai/standards/changelog.ai.yaml +146 -0
- package/bundled/locales/zh-CN/ai/standards/checkin-standards.ai.yaml +170 -0
- package/bundled/locales/zh-CN/ai/standards/code-review.ai.yaml +148 -0
- package/bundled/locales/zh-CN/ai/standards/commit-message.ai.yaml +175 -0
- package/bundled/locales/zh-CN/ai/standards/documentation-structure.ai.yaml +124 -0
- package/bundled/locales/zh-CN/ai/standards/documentation-writing-standards.ai.yaml +190 -0
- package/bundled/locales/zh-CN/ai/standards/error-codes.ai.yaml +139 -0
- package/bundled/locales/zh-CN/ai/standards/git-workflow.ai.yaml +95 -0
- package/bundled/locales/zh-CN/ai/standards/logging.ai.yaml +128 -0
- package/bundled/locales/zh-CN/ai/standards/project-structure.ai.yaml +134 -0
- package/bundled/locales/zh-CN/ai/standards/spec-driven-development.ai.yaml +169 -0
- package/bundled/locales/zh-CN/ai/standards/test-completeness-dimensions.ai.yaml +220 -0
- package/bundled/locales/zh-CN/ai/standards/testing.ai.yaml +137 -0
- package/bundled/locales/zh-CN/ai/standards/versioning.ai.yaml +211 -0
- package/bundled/locales/zh-CN/core/ai-instruction-standards.md +213 -0
- package/bundled/locales/zh-CN/core/anti-hallucination.md +691 -0
- package/bundled/locales/zh-CN/core/changelog-standards.md +147 -0
- package/bundled/locales/zh-CN/core/checkin-standards.md +943 -0
- package/bundled/locales/zh-CN/core/code-review-guide.md +693 -0
- package/bundled/locales/zh-CN/core/commit-message-guide.md +129 -0
- package/bundled/locales/zh-CN/core/documentation-structure.md +173 -0
- package/bundled/locales/zh-CN/core/documentation-writing-standards.md +495 -0
- package/bundled/locales/zh-CN/core/error-code-standards.md +180 -0
- package/bundled/locales/zh-CN/core/git-workflow.md +193 -0
- package/bundled/locales/zh-CN/core/logging-standards.md +174 -0
- package/bundled/locales/zh-CN/core/project-structure.md +184 -0
- package/bundled/locales/zh-CN/core/refactoring-standards.md +642 -0
- package/bundled/locales/zh-CN/core/spec-driven-development.md +215 -0
- package/bundled/locales/zh-CN/core/test-completeness-dimensions.md +544 -0
- package/bundled/locales/zh-CN/core/test-driven-development.md +1002 -0
- package/bundled/locales/zh-CN/core/testing-standards.md +170 -0
- package/bundled/locales/zh-CN/core/versioning.md +177 -0
- package/bundled/locales/zh-CN/docs/AI-AGENT-ROADMAP.md +303 -0
- package/bundled/locales/zh-CN/docs/CLI-INIT-OPTIONS.md +990 -0
- package/bundled/locales/zh-CN/docs/OPERATION-WORKFLOW.md +1064 -0
- package/bundled/locales/zh-CN/docs/USAGE-MODES-COMPARISON.md +333 -0
- package/bundled/locales/zh-CN/docs/WINDOWS-GUIDE.md +215 -0
- package/bundled/locales/zh-CN/integrations/codex/AGENTS.md +115 -0
- package/bundled/locales/zh-CN/integrations/codex/README.md +67 -0
- package/bundled/locales/zh-CN/integrations/gemini-cli/GEMINI.md +102 -0
- package/bundled/locales/zh-CN/integrations/gemini-cli/README.md +140 -0
- package/bundled/locales/zh-CN/integrations/github-copilot/COPILOT-CHAT-REFERENCE.md +269 -0
- package/bundled/locales/zh-CN/integrations/github-copilot/README.md +167 -0
- package/bundled/locales/zh-CN/integrations/github-copilot/copilot-instructions.md +263 -0
- package/bundled/locales/zh-CN/integrations/github-copilot/skills-mapping.md +192 -0
- package/bundled/locales/zh-CN/integrations/google-antigravity/INSTRUCTIONS.md +61 -0
- package/bundled/locales/zh-CN/integrations/google-antigravity/README.md +84 -0
- package/bundled/locales/zh-CN/integrations/opencode/AGENTS.md +111 -0
- package/bundled/locales/zh-CN/integrations/opencode/README.md +176 -0
- package/bundled/locales/zh-CN/integrations/opencode/skills-mapping.md +396 -0
- package/bundled/locales/zh-CN/integrations/openspec/AGENTS.md +301 -0
- package/bundled/locales/zh-CN/integrations/openspec/README.md +43 -0
- package/bundled/locales/zh-CN/integrations/spec-kit/AGENTS.md +184 -0
- package/bundled/locales/zh-CN/integrations/spec-kit/README.md +43 -0
- package/bundled/locales/zh-CN/options/commit-message/bilingual.md +161 -0
- package/bundled/locales/zh-CN/options/commit-message/english.md +137 -0
- package/bundled/locales/zh-CN/options/commit-message/traditional-chinese.md +162 -0
- package/bundled/locales/zh-CN/options/git-workflow/gitflow.md +147 -0
- package/bundled/locales/zh-CN/options/git-workflow/github-flow.md +124 -0
- package/bundled/locales/zh-CN/options/git-workflow/merge-commit.md +124 -0
- package/bundled/locales/zh-CN/options/git-workflow/rebase-ff.md +160 -0
- package/bundled/locales/zh-CN/options/git-workflow/squash-merge.md +117 -0
- package/bundled/locales/zh-CN/options/git-workflow/trunk-based.md +125 -0
- package/bundled/locales/zh-CN/options/project-structure/dotnet.md +183 -0
- package/bundled/locales/zh-CN/options/project-structure/go.md +226 -0
- package/bundled/locales/zh-CN/options/project-structure/java.md +213 -0
- package/bundled/locales/zh-CN/options/project-structure/nodejs.md +185 -0
- package/bundled/locales/zh-CN/options/project-structure/python.md +229 -0
- package/bundled/locales/zh-CN/options/testing/e2e-testing.md +207 -0
- package/bundled/locales/zh-CN/options/testing/integration-testing.md +230 -0
- package/bundled/locales/zh-CN/options/testing/system-testing.md +183 -0
- package/bundled/locales/zh-CN/options/testing/unit-testing.md +165 -0
- package/bundled/locales/zh-CN/skills/INTEGRATION-GUIDE.md +218 -0
- package/bundled/locales/zh-CN/skills/README.md +134 -0
- package/bundled/locales/zh-CN/skills/_shared/README.md +68 -0
- package/bundled/locales/zh-CN/skills/claude-code/CONTRIBUTING.template.md +151 -0
- package/bundled/locales/zh-CN/skills/claude-code/README.md +174 -0
- package/bundled/locales/zh-CN/skills/claude-code/ai-collaboration-standards/SKILL.md +175 -0
- package/bundled/locales/zh-CN/skills/claude-code/ai-collaboration-standards/anti-hallucination.md +223 -0
- package/bundled/locales/zh-CN/skills/claude-code/ai-collaboration-standards/certainty-labels.md +132 -0
- package/bundled/locales/zh-CN/skills/claude-code/changelog-guide/SKILL.md +237 -0
- package/bundled/locales/zh-CN/skills/claude-code/checkin-assistant/SKILL.md +407 -0
- package/bundled/locales/zh-CN/skills/claude-code/code-review-assistant/SKILL.md +154 -0
- package/bundled/locales/zh-CN/skills/claude-code/code-review-assistant/checkin-checklist.md +257 -0
- package/bundled/locales/zh-CN/skills/claude-code/code-review-assistant/review-checklist.md +246 -0
- package/bundled/locales/zh-CN/skills/claude-code/commands/bdd.md +142 -0
- package/bundled/locales/zh-CN/skills/claude-code/commands/methodology.md +274 -0
- package/bundled/locales/zh-CN/skills/claude-code/commit-standards/SKILL.md +191 -0
- package/bundled/locales/zh-CN/skills/claude-code/commit-standards/conventional-commits.md +264 -0
- package/bundled/locales/zh-CN/skills/claude-code/commit-standards/language-options.md +172 -0
- package/bundled/locales/zh-CN/skills/claude-code/documentation-guide/SKILL.md +421 -0
- package/bundled/locales/zh-CN/skills/claude-code/documentation-guide/documentation-structure.md +357 -0
- package/bundled/locales/zh-CN/skills/claude-code/documentation-guide/readme-template.md +412 -0
- package/bundled/locales/zh-CN/skills/claude-code/error-code-guide/SKILL.md +269 -0
- package/bundled/locales/zh-CN/skills/claude-code/git-workflow-guide/SKILL.md +218 -0
- package/bundled/locales/zh-CN/skills/claude-code/git-workflow-guide/branch-naming.md +220 -0
- package/bundled/locales/zh-CN/skills/claude-code/git-workflow-guide/git-workflow.md +321 -0
- package/bundled/locales/zh-CN/skills/claude-code/logging-guide/SKILL.md +285 -0
- package/bundled/locales/zh-CN/skills/claude-code/methodology-system/SKILL.md +131 -0
- package/bundled/locales/zh-CN/skills/claude-code/methodology-system/create-methodology.md +350 -0
- package/bundled/locales/zh-CN/skills/claude-code/methodology-system/runtime.md +279 -0
- package/bundled/locales/zh-CN/skills/claude-code/project-structure-guide/SKILL.md +143 -0
- package/bundled/locales/zh-CN/skills/claude-code/project-structure-guide/language-patterns.md +271 -0
- package/bundled/locales/zh-CN/skills/claude-code/refactoring-assistant/SKILL.md +162 -0
- package/bundled/locales/zh-CN/skills/claude-code/release-standards/SKILL.md +191 -0
- package/bundled/locales/zh-CN/skills/claude-code/release-standards/changelog-format.md +247 -0
- package/bundled/locales/zh-CN/skills/claude-code/release-standards/release-workflow.md +345 -0
- package/bundled/locales/zh-CN/skills/claude-code/release-standards/semantic-versioning.md +250 -0
- package/bundled/locales/zh-CN/skills/claude-code/requirement-assistant/SKILL.md +227 -0
- package/bundled/locales/zh-CN/skills/claude-code/requirement-assistant/requirement-checklist.md +325 -0
- package/bundled/locales/zh-CN/skills/claude-code/requirement-assistant/requirement-writing.md +399 -0
- package/bundled/locales/zh-CN/skills/claude-code/spec-driven-dev/SKILL.md +243 -0
- package/bundled/locales/zh-CN/skills/claude-code/tdd-assistant/SKILL.md +332 -0
- package/bundled/locales/zh-CN/skills/claude-code/tdd-assistant/language-examples.md +639 -0
- package/bundled/locales/zh-CN/skills/claude-code/tdd-assistant/tdd-workflow.md +486 -0
- package/bundled/locales/zh-CN/skills/claude-code/test-coverage-assistant/SKILL.md +282 -0
- package/bundled/locales/zh-CN/skills/claude-code/testing-guide/SKILL.md +234 -0
- package/bundled/locales/zh-CN/skills/claude-code/testing-guide/testing-pyramid.md +448 -0
- package/bundled/locales/zh-CN/skills/cline/README.md +58 -0
- package/bundled/locales/zh-CN/skills/copilot/README.md +61 -0
- package/bundled/locales/zh-CN/skills/copilot/copilot-instructions.md +79 -0
- package/bundled/locales/zh-CN/skills/cursor/README.md +58 -0
- package/bundled/locales/zh-CN/skills/windsurf/README.md +59 -0
- package/bundled/locales/zh-TW/CHANGELOG.md +707 -0
- package/bundled/locales/zh-TW/CLAUDE.md +213 -0
- package/bundled/locales/zh-TW/MAINTENANCE.md +683 -0
- package/bundled/locales/zh-TW/README.md +687 -0
- package/bundled/locales/zh-TW/STANDARDS-MAPPING.md +177 -0
- package/bundled/locales/zh-TW/adoption/ADOPTION-GUIDE.md +392 -0
- package/bundled/locales/zh-TW/adoption/STATIC-DYNAMIC-GUIDE.md +299 -0
- package/bundled/locales/zh-TW/adoption/checklists/enterprise.md +332 -0
- package/bundled/locales/zh-TW/adoption/checklists/minimal.md +141 -0
- package/bundled/locales/zh-TW/adoption/checklists/recommended.md +272 -0
- package/bundled/locales/zh-TW/ai/MAINTENANCE.md +754 -0
- package/bundled/locales/zh-TW/ai/options/changelog/auto-generated.ai.yaml +76 -0
- package/bundled/locales/zh-TW/ai/options/changelog/keep-a-changelog.ai.yaml +99 -0
- package/bundled/locales/zh-TW/ai/options/code-review/automated-review.ai.yaml +120 -0
- package/bundled/locales/zh-TW/ai/options/code-review/pair-programming.ai.yaml +109 -0
- package/bundled/locales/zh-TW/ai/options/code-review/pr-review.ai.yaml +104 -0
- package/bundled/locales/zh-TW/ai/options/commit-message/bilingual.ai.yaml +105 -0
- package/bundled/locales/zh-TW/ai/options/commit-message/english.ai.yaml +79 -0
- package/bundled/locales/zh-TW/ai/options/commit-message/traditional-chinese.ai.yaml +100 -0
- package/bundled/locales/zh-TW/ai/options/documentation/api-docs.ai.yaml +140 -0
- package/bundled/locales/zh-TW/ai/options/documentation/markdown-docs.ai.yaml +89 -0
- package/bundled/locales/zh-TW/ai/options/documentation/wiki-style.ai.yaml +119 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/gitflow.ai.yaml +133 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/github-flow.ai.yaml +73 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/merge-commit.ai.yaml +88 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/rebase-ff.ai.yaml +113 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/squash-merge.ai.yaml +85 -0
- package/bundled/locales/zh-TW/ai/options/git-workflow/trunk-based.ai.yaml +117 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/dotnet.ai.yaml +108 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/go.ai.yaml +115 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/java.ai.yaml +113 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/kotlin.ai.yaml +123 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/nodejs.ai.yaml +101 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/php.ai.yaml +147 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/python.ai.yaml +116 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/ruby.ai.yaml +140 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/rust.ai.yaml +117 -0
- package/bundled/locales/zh-TW/ai/options/project-structure/swift.ai.yaml +143 -0
- package/bundled/locales/zh-TW/ai/options/testing/contract-testing.ai.yaml +254 -0
- package/bundled/locales/zh-TW/ai/options/testing/e2e-testing.ai.yaml +116 -0
- package/bundled/locales/zh-TW/ai/options/testing/industry-pyramid.ai.yaml +144 -0
- package/bundled/locales/zh-TW/ai/options/testing/integration-testing.ai.yaml +90 -0
- package/bundled/locales/zh-TW/ai/options/testing/istqb-framework.ai.yaml +108 -0
- package/bundled/locales/zh-TW/ai/options/testing/performance-testing.ai.yaml +272 -0
- package/bundled/locales/zh-TW/ai/options/testing/security-testing.ai.yaml +160 -0
- package/bundled/locales/zh-TW/ai/options/testing/system-testing.ai.yaml +101 -0
- package/bundled/locales/zh-TW/ai/options/testing/unit-testing.ai.yaml +82 -0
- package/bundled/locales/zh-TW/ai/standards/anti-hallucination.ai.yaml +145 -0
- package/bundled/locales/zh-TW/ai/standards/changelog.ai.yaml +146 -0
- package/bundled/locales/zh-TW/ai/standards/checkin-standards.ai.yaml +170 -0
- package/bundled/locales/zh-TW/ai/standards/code-review.ai.yaml +148 -0
- package/bundled/locales/zh-TW/ai/standards/commit-message.ai.yaml +175 -0
- package/bundled/locales/zh-TW/ai/standards/documentation-structure.ai.yaml +124 -0
- package/bundled/locales/zh-TW/ai/standards/documentation-writing-standards.ai.yaml +190 -0
- package/bundled/locales/zh-TW/ai/standards/error-codes.ai.yaml +139 -0
- package/bundled/locales/zh-TW/ai/standards/git-workflow.ai.yaml +95 -0
- package/bundled/locales/zh-TW/ai/standards/logging.ai.yaml +128 -0
- package/bundled/locales/zh-TW/ai/standards/project-structure.ai.yaml +134 -0
- package/bundled/locales/zh-TW/ai/standards/spec-driven-development.ai.yaml +169 -0
- package/bundled/locales/zh-TW/ai/standards/test-completeness-dimensions.ai.yaml +220 -0
- package/bundled/locales/zh-TW/ai/standards/testing.ai.yaml +137 -0
- package/bundled/locales/zh-TW/ai/standards/versioning.ai.yaml +211 -0
- package/bundled/locales/zh-TW/core/ai-instruction-standards.md +213 -0
- package/bundled/locales/zh-TW/core/anti-hallucination.md +691 -0
- package/bundled/locales/zh-TW/core/changelog-standards.md +564 -0
- package/bundled/locales/zh-TW/core/checkin-standards.md +943 -0
- package/bundled/locales/zh-TW/core/code-review-checklist.md +693 -0
- package/bundled/locales/zh-TW/core/commit-message-guide.md +809 -0
- package/bundled/locales/zh-TW/core/documentation-structure.md +1125 -0
- package/bundled/locales/zh-TW/core/documentation-writing-standards.md +495 -0
- package/bundled/locales/zh-TW/core/error-code-standards.md +384 -0
- package/bundled/locales/zh-TW/core/git-workflow.md +860 -0
- package/bundled/locales/zh-TW/core/logging-standards.md +325 -0
- package/bundled/locales/zh-TW/core/project-structure.md +362 -0
- package/bundled/locales/zh-TW/core/refactoring-standards.md +642 -0
- package/bundled/locales/zh-TW/core/spec-driven-development.md +215 -0
- package/bundled/locales/zh-TW/core/test-completeness-dimensions.md +544 -0
- package/bundled/locales/zh-TW/core/test-driven-development.md +1004 -0
- package/bundled/locales/zh-TW/core/testing-standards.md +2158 -0
- package/bundled/locales/zh-TW/core/versioning.md +909 -0
- package/bundled/locales/zh-TW/docs/AI-AGENT-ROADMAP.md +303 -0
- package/bundled/locales/zh-TW/docs/CLI-INIT-OPTIONS.md +990 -0
- package/bundled/locales/zh-TW/docs/OPERATION-WORKFLOW.md +1064 -0
- package/bundled/locales/zh-TW/docs/USAGE-MODES-COMPARISON.md +333 -0
- package/bundled/locales/zh-TW/docs/WINDOWS-GUIDE.md +215 -0
- package/bundled/locales/zh-TW/integrations/codex/AGENTS.md +115 -0
- package/bundled/locales/zh-TW/integrations/codex/README.md +113 -0
- package/bundled/locales/zh-TW/integrations/gemini-cli/GEMINI.md +102 -0
- package/bundled/locales/zh-TW/integrations/gemini-cli/README.md +140 -0
- package/bundled/locales/zh-TW/integrations/github-copilot/COPILOT-CHAT-REFERENCE.md +269 -0
- package/bundled/locales/zh-TW/integrations/github-copilot/README.md +167 -0
- package/bundled/locales/zh-TW/integrations/github-copilot/copilot-instructions.md +263 -0
- package/bundled/locales/zh-TW/integrations/github-copilot/skills-mapping.md +192 -0
- package/bundled/locales/zh-TW/integrations/google-antigravity/INSTRUCTIONS.md +61 -0
- package/bundled/locales/zh-TW/integrations/google-antigravity/README.md +84 -0
- package/bundled/locales/zh-TW/integrations/opencode/AGENTS.md +111 -0
- package/bundled/locales/zh-TW/integrations/opencode/README.md +176 -0
- package/bundled/locales/zh-TW/integrations/opencode/skills-mapping.md +396 -0
- package/bundled/locales/zh-TW/integrations/openspec/AGENTS.md +301 -0
- package/bundled/locales/zh-TW/integrations/openspec/README.md +43 -0
- package/bundled/locales/zh-TW/integrations/spec-kit/AGENTS.md +184 -0
- package/bundled/locales/zh-TW/integrations/spec-kit/README.md +43 -0
- package/bundled/locales/zh-TW/options/commit-message/bilingual.md +161 -0
- package/bundled/locales/zh-TW/options/commit-message/english.md +137 -0
- package/bundled/locales/zh-TW/options/commit-message/traditional-chinese.md +162 -0
- package/bundled/locales/zh-TW/options/git-workflow/gitflow.md +147 -0
- package/bundled/locales/zh-TW/options/git-workflow/github-flow.md +124 -0
- package/bundled/locales/zh-TW/options/git-workflow/merge-commit.md +124 -0
- package/bundled/locales/zh-TW/options/git-workflow/rebase-ff.md +160 -0
- package/bundled/locales/zh-TW/options/git-workflow/squash-merge.md +117 -0
- package/bundled/locales/zh-TW/options/git-workflow/trunk-based.md +125 -0
- package/bundled/locales/zh-TW/options/project-structure/dotnet.md +183 -0
- package/bundled/locales/zh-TW/options/project-structure/go.md +226 -0
- package/bundled/locales/zh-TW/options/project-structure/java.md +213 -0
- package/bundled/locales/zh-TW/options/project-structure/nodejs.md +185 -0
- package/bundled/locales/zh-TW/options/project-structure/python.md +229 -0
- package/bundled/locales/zh-TW/options/testing/e2e-testing.md +207 -0
- package/bundled/locales/zh-TW/options/testing/integration-testing.md +230 -0
- package/bundled/locales/zh-TW/options/testing/system-testing.md +183 -0
- package/bundled/locales/zh-TW/options/testing/unit-testing.md +165 -0
- package/bundled/locales/zh-TW/skills/INTEGRATION-GUIDE.md +218 -0
- package/bundled/locales/zh-TW/skills/README.md +132 -0
- package/bundled/locales/zh-TW/skills/_shared/README.md +68 -0
- package/bundled/locales/zh-TW/skills/claude-code/CONTRIBUTING.template.md +151 -0
- package/bundled/locales/zh-TW/skills/claude-code/README.md +174 -0
- package/bundled/locales/zh-TW/skills/claude-code/ai-collaboration-standards/SKILL.md +175 -0
- package/bundled/locales/zh-TW/skills/claude-code/ai-collaboration-standards/anti-hallucination.md +223 -0
- package/bundled/locales/zh-TW/skills/claude-code/ai-collaboration-standards/certainty-labels.md +132 -0
- package/bundled/locales/zh-TW/skills/claude-code/changelog-guide/SKILL.md +237 -0
- package/bundled/locales/zh-TW/skills/claude-code/checkin-assistant/SKILL.md +407 -0
- package/bundled/locales/zh-TW/skills/claude-code/code-review-assistant/SKILL.md +154 -0
- package/bundled/locales/zh-TW/skills/claude-code/code-review-assistant/checkin-checklist.md +257 -0
- package/bundled/locales/zh-TW/skills/claude-code/code-review-assistant/review-checklist.md +246 -0
- package/bundled/locales/zh-TW/skills/claude-code/commands/bdd.md +142 -0
- package/bundled/locales/zh-TW/skills/claude-code/commands/methodology.md +274 -0
- package/bundled/locales/zh-TW/skills/claude-code/commit-standards/SKILL.md +191 -0
- package/bundled/locales/zh-TW/skills/claude-code/commit-standards/conventional-commits.md +264 -0
- package/bundled/locales/zh-TW/skills/claude-code/commit-standards/language-options.md +172 -0
- package/bundled/locales/zh-TW/skills/claude-code/documentation-guide/SKILL.md +421 -0
- package/bundled/locales/zh-TW/skills/claude-code/documentation-guide/documentation-structure.md +357 -0
- package/bundled/locales/zh-TW/skills/claude-code/documentation-guide/readme-template.md +412 -0
- package/bundled/locales/zh-TW/skills/claude-code/error-code-guide/SKILL.md +269 -0
- package/bundled/locales/zh-TW/skills/claude-code/git-workflow-guide/SKILL.md +218 -0
- package/bundled/locales/zh-TW/skills/claude-code/git-workflow-guide/branch-naming.md +220 -0
- package/bundled/locales/zh-TW/skills/claude-code/git-workflow-guide/git-workflow.md +321 -0
- package/bundled/locales/zh-TW/skills/claude-code/logging-guide/SKILL.md +285 -0
- package/bundled/locales/zh-TW/skills/claude-code/methodology-system/SKILL.md +131 -0
- package/bundled/locales/zh-TW/skills/claude-code/methodology-system/create-methodology.md +350 -0
- package/bundled/locales/zh-TW/skills/claude-code/methodology-system/runtime.md +279 -0
- package/bundled/locales/zh-TW/skills/claude-code/project-structure-guide/SKILL.md +143 -0
- package/bundled/locales/zh-TW/skills/claude-code/project-structure-guide/language-patterns.md +271 -0
- package/bundled/locales/zh-TW/skills/claude-code/refactoring-assistant/SKILL.md +162 -0
- package/bundled/locales/zh-TW/skills/claude-code/release-standards/SKILL.md +191 -0
- package/bundled/locales/zh-TW/skills/claude-code/release-standards/changelog-format.md +247 -0
- package/bundled/locales/zh-TW/skills/claude-code/release-standards/release-workflow.md +345 -0
- package/bundled/locales/zh-TW/skills/claude-code/release-standards/semantic-versioning.md +250 -0
- package/bundled/locales/zh-TW/skills/claude-code/requirement-assistant/SKILL.md +227 -0
- package/bundled/locales/zh-TW/skills/claude-code/requirement-assistant/requirement-checklist.md +325 -0
- package/bundled/locales/zh-TW/skills/claude-code/requirement-assistant/requirement-writing.md +399 -0
- package/bundled/locales/zh-TW/skills/claude-code/spec-driven-dev/SKILL.md +243 -0
- package/bundled/locales/zh-TW/skills/claude-code/tdd-assistant/SKILL.md +332 -0
- package/bundled/locales/zh-TW/skills/claude-code/tdd-assistant/language-examples.md +639 -0
- package/bundled/locales/zh-TW/skills/claude-code/tdd-assistant/tdd-workflow.md +486 -0
- package/bundled/locales/zh-TW/skills/claude-code/test-coverage-assistant/SKILL.md +282 -0
- package/bundled/locales/zh-TW/skills/claude-code/testing-guide/SKILL.md +234 -0
- package/bundled/locales/zh-TW/skills/claude-code/testing-guide/testing-pyramid.md +448 -0
- package/bundled/locales/zh-TW/skills/cline/README.md +58 -0
- package/bundled/locales/zh-TW/skills/copilot/README.md +61 -0
- package/bundled/locales/zh-TW/skills/copilot/copilot-instructions.md +79 -0
- package/bundled/locales/zh-TW/skills/cursor/README.md +58 -0
- package/bundled/locales/zh-TW/skills/windsurf/README.md +59 -0
- package/bundled/skills/claude-code/CONTRIBUTING.template.md +141 -0
- package/bundled/skills/claude-code/README.md +196 -0
- package/bundled/skills/claude-code/ai/standards/checkin.ai.yaml +21 -0
- package/bundled/skills/claude-code/ai/standards/commit.ai.yaml +20 -0
- package/bundled/skills/claude-code/ai/standards/refactoring.ai.yaml +34 -0
- package/bundled/skills/claude-code/ai/standards/testing.ai.yaml +41 -0
- package/bundled/skills/claude-code/ai-collaboration-standards/SKILL.md +175 -0
- package/bundled/skills/claude-code/ai-collaboration-standards/anti-hallucination.md +215 -0
- package/bundled/skills/claude-code/ai-collaboration-standards/certainty-labels.md +124 -0
- package/bundled/skills/claude-code/changelog-guide/SKILL.md +232 -0
- package/bundled/skills/claude-code/checkin-assistant/SKILL.md +402 -0
- package/bundled/skills/claude-code/code-review-assistant/SKILL.md +220 -0
- package/bundled/skills/claude-code/code-review-assistant/checkin-checklist.md +249 -0
- package/bundled/skills/claude-code/code-review-assistant/review-checklist.md +238 -0
- package/bundled/skills/claude-code/commands/README.md +78 -0
- package/bundled/skills/claude-code/commands/bdd.md +142 -0
- package/bundled/skills/claude-code/commands/changelog.md +57 -0
- package/bundled/skills/claude-code/commands/check.md +91 -0
- package/bundled/skills/claude-code/commands/commit.md +48 -0
- package/bundled/skills/claude-code/commands/config.md +97 -0
- package/bundled/skills/claude-code/commands/coverage.md +58 -0
- package/bundled/skills/claude-code/commands/docs.md +75 -0
- package/bundled/skills/claude-code/commands/init.md +88 -0
- package/bundled/skills/claude-code/commands/methodology.md +268 -0
- package/bundled/skills/claude-code/commands/release.md +50 -0
- package/bundled/skills/claude-code/commands/requirement.md +54 -0
- package/bundled/skills/claude-code/commands/review.md +50 -0
- package/bundled/skills/claude-code/commands/spec.md +69 -0
- package/bundled/skills/claude-code/commands/tdd.md +86 -0
- package/bundled/skills/claude-code/commands/update.md +122 -0
- package/bundled/skills/claude-code/commit-standards/SKILL.md +249 -0
- package/bundled/skills/claude-code/commit-standards/conventional-commits.md +256 -0
- package/bundled/skills/claude-code/commit-standards/language-options.md +164 -0
- package/bundled/skills/claude-code/documentation-guide/SKILL.md +416 -0
- package/bundled/skills/claude-code/documentation-guide/documentation-structure.md +349 -0
- package/bundled/skills/claude-code/documentation-guide/readme-template.md +404 -0
- package/bundled/skills/claude-code/error-code-guide/SKILL.md +264 -0
- package/bundled/skills/claude-code/git-workflow-guide/SKILL.md +226 -0
- package/bundled/skills/claude-code/git-workflow-guide/branch-naming.md +212 -0
- package/bundled/skills/claude-code/git-workflow-guide/git-workflow.md +313 -0
- package/bundled/skills/claude-code/logging-guide/SKILL.md +280 -0
- package/bundled/skills/claude-code/methodology-system/SKILL.md +215 -0
- package/bundled/skills/claude-code/methodology-system/create-methodology.md +450 -0
- package/bundled/skills/claude-code/methodology-system/runtime.md +271 -0
- package/bundled/skills/claude-code/project-structure-guide/SKILL.md +143 -0
- package/bundled/skills/claude-code/project-structure-guide/language-patterns.md +263 -0
- package/bundled/skills/claude-code/refactoring-assistant/SKILL.md +205 -0
- package/bundled/skills/claude-code/release-standards/SKILL.md +191 -0
- package/bundled/skills/claude-code/release-standards/changelog-format.md +239 -0
- package/bundled/skills/claude-code/release-standards/release-workflow.md +337 -0
- package/bundled/skills/claude-code/release-standards/semantic-versioning.md +242 -0
- package/bundled/skills/claude-code/requirement-assistant/SKILL.md +222 -0
- package/bundled/skills/claude-code/requirement-assistant/requirement-checklist.md +317 -0
- package/bundled/skills/claude-code/requirement-assistant/requirement-writing.md +391 -0
- package/bundled/skills/claude-code/spec-driven-dev/SKILL.md +238 -0
- package/bundled/skills/claude-code/tdd-assistant/SKILL.md +384 -0
- package/bundled/skills/claude-code/tdd-assistant/language-examples.md +1276 -0
- package/bundled/skills/claude-code/tdd-assistant/tdd-workflow.md +659 -0
- package/bundled/skills/claude-code/test-coverage-assistant/SKILL.md +277 -0
- package/bundled/skills/claude-code/testing-guide/SKILL.md +317 -0
- package/bundled/skills/claude-code/testing-guide/testing-pyramid.md +440 -0
- package/package.json +4 -2
- package/src/commands/check.js +38 -6
- package/src/commands/init.js +20 -6
- package/src/commands/list.js +7 -2
- package/src/commands/skills.js +0 -2
- package/src/commands/update.js +271 -0
- package/src/i18n/messages.js +161 -11
- package/src/utils/copier.js +63 -10
- package/src/utils/skills-installer.js +47 -11
- package/standards-registry.json +6 -3
|
@@ -0,0 +1,1276 @@
|
|
|
1
|
+
# TDD Language Examples
|
|
2
|
+
|
|
3
|
+
> **Language**: English | [繁體中文](../../../locales/zh-TW/skills/claude-code/tdd-assistant/language-examples.md)
|
|
4
|
+
|
|
5
|
+
**Version**: 1.0.0
|
|
6
|
+
**Last Updated**: 2026-01-07
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
This document provides complete TDD examples in six major programming languages:
|
|
13
|
+
|
|
14
|
+
1. [JavaScript/TypeScript](#javascripttypescript)
|
|
15
|
+
2. [Python](#python)
|
|
16
|
+
3. [C#](#c)
|
|
17
|
+
4. [Go](#go)
|
|
18
|
+
5. [Java](#java)
|
|
19
|
+
6. [Ruby](#ruby)
|
|
20
|
+
|
|
21
|
+
Each section includes:
|
|
22
|
+
- Complete Red-Green-Refactor example
|
|
23
|
+
- Test framework setup
|
|
24
|
+
- Mock/Stub usage
|
|
25
|
+
- BDD example (where applicable)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## JavaScript/TypeScript
|
|
30
|
+
|
|
31
|
+
### Test Framework: Jest/Vitest
|
|
32
|
+
|
|
33
|
+
#### Setup
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Jest
|
|
37
|
+
npm install --save-dev jest @types/jest ts-jest
|
|
38
|
+
|
|
39
|
+
# Vitest
|
|
40
|
+
npm install --save-dev vitest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
// package.json
|
|
45
|
+
{
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "jest",
|
|
48
|
+
"test:watch": "jest --watch",
|
|
49
|
+
"test:coverage": "jest --coverage"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Complete TDD Example: Shopping Cart
|
|
55
|
+
|
|
56
|
+
**Step 1: RED - Write failing test**
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// cart.test.ts
|
|
60
|
+
import { ShoppingCart } from './cart';
|
|
61
|
+
|
|
62
|
+
describe('ShoppingCart', () => {
|
|
63
|
+
describe('calculateTotal', () => {
|
|
64
|
+
test('should return 0 for empty cart', () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
const cart = new ShoppingCart();
|
|
67
|
+
|
|
68
|
+
// Act
|
|
69
|
+
const total = cart.calculateTotal();
|
|
70
|
+
|
|
71
|
+
// Assert
|
|
72
|
+
expect(total).toBe(0);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Run test - it fails because `ShoppingCart` doesn't exist.
|
|
79
|
+
|
|
80
|
+
**Step 2: GREEN - Minimum implementation**
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// cart.ts
|
|
84
|
+
export class ShoppingCart {
|
|
85
|
+
calculateTotal(): number {
|
|
86
|
+
return 0; // Fake it!
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Test passes.
|
|
92
|
+
|
|
93
|
+
**Step 3: RED - Add next test**
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
test('should return sum of item prices', () => {
|
|
97
|
+
const cart = new ShoppingCart();
|
|
98
|
+
cart.addItem({ name: 'Widget', price: 10 });
|
|
99
|
+
cart.addItem({ name: 'Gadget', price: 20 });
|
|
100
|
+
|
|
101
|
+
const total = cart.calculateTotal();
|
|
102
|
+
|
|
103
|
+
expect(total).toBe(30);
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Step 4: GREEN - Implement**
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
export class ShoppingCart {
|
|
111
|
+
private items: { name: string; price: number }[] = [];
|
|
112
|
+
|
|
113
|
+
addItem(item: { name: string; price: number }): void {
|
|
114
|
+
this.items.push(item);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
calculateTotal(): number {
|
|
118
|
+
return this.items.reduce((sum, item) => sum + item.price, 0);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Step 5: RED - Test with discount**
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
test('should apply percentage discount', () => {
|
|
127
|
+
const cart = new ShoppingCart();
|
|
128
|
+
cart.addItem({ name: 'Widget', price: 100 });
|
|
129
|
+
cart.applyDiscount(20); // 20% off
|
|
130
|
+
|
|
131
|
+
const total = cart.calculateTotal();
|
|
132
|
+
|
|
133
|
+
expect(total).toBe(80);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Step 6: GREEN & REFACTOR**
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
export class ShoppingCart {
|
|
141
|
+
private items: Array<{ name: string; price: number }> = [];
|
|
142
|
+
private discountPercent = 0;
|
|
143
|
+
|
|
144
|
+
addItem(item: { name: string; price: number }): void {
|
|
145
|
+
this.items.push(item);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
applyDiscount(percent: number): void {
|
|
149
|
+
this.discountPercent = percent;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
calculateTotal(): number {
|
|
153
|
+
const subtotal = this.items.reduce((sum, item) => sum + item.price, 0);
|
|
154
|
+
const discount = subtotal * (this.discountPercent / 100);
|
|
155
|
+
return subtotal - discount;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### Mocking Example
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// orderService.test.ts
|
|
164
|
+
import { OrderService } from './orderService';
|
|
165
|
+
import { PaymentGateway } from './paymentGateway';
|
|
166
|
+
|
|
167
|
+
jest.mock('./paymentGateway');
|
|
168
|
+
|
|
169
|
+
describe('OrderService', () => {
|
|
170
|
+
let orderService: OrderService;
|
|
171
|
+
let mockPaymentGateway: jest.Mocked<PaymentGateway>;
|
|
172
|
+
|
|
173
|
+
beforeEach(() => {
|
|
174
|
+
mockPaymentGateway = new PaymentGateway() as jest.Mocked<PaymentGateway>;
|
|
175
|
+
orderService = new OrderService(mockPaymentGateway);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('should process payment and return order confirmation', async () => {
|
|
179
|
+
// Arrange
|
|
180
|
+
mockPaymentGateway.charge.mockResolvedValue({
|
|
181
|
+
success: true,
|
|
182
|
+
transactionId: 'TXN123'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Act
|
|
186
|
+
const result = await orderService.checkout({
|
|
187
|
+
amount: 100,
|
|
188
|
+
cardNumber: '4111111111111111'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Assert
|
|
192
|
+
expect(result.confirmed).toBe(true);
|
|
193
|
+
expect(result.transactionId).toBe('TXN123');
|
|
194
|
+
expect(mockPaymentGateway.charge).toHaveBeenCalledWith(100, '4111111111111111');
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### BDD with Cucumber.js
|
|
200
|
+
|
|
201
|
+
```gherkin
|
|
202
|
+
# features/shopping_cart.feature
|
|
203
|
+
Feature: Shopping Cart
|
|
204
|
+
As a customer
|
|
205
|
+
I want to add items to my cart
|
|
206
|
+
So that I can purchase them
|
|
207
|
+
|
|
208
|
+
Scenario: Add item to empty cart
|
|
209
|
+
Given I have an empty shopping cart
|
|
210
|
+
When I add a "Widget" priced at $10
|
|
211
|
+
Then the cart total should be $10
|
|
212
|
+
|
|
213
|
+
Scenario: Apply discount code
|
|
214
|
+
Given I have a cart with items totaling $100
|
|
215
|
+
When I apply discount code "SAVE20"
|
|
216
|
+
Then the cart total should be $80
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// features/step_definitions/cart_steps.ts
|
|
221
|
+
import { Given, When, Then } from '@cucumber/cucumber';
|
|
222
|
+
import { expect } from 'chai';
|
|
223
|
+
import { ShoppingCart } from '../../src/cart';
|
|
224
|
+
|
|
225
|
+
let cart: ShoppingCart;
|
|
226
|
+
|
|
227
|
+
Given('I have an empty shopping cart', function () {
|
|
228
|
+
cart = new ShoppingCart();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
When('I add a {string} priced at ${int}', function (name: string, price: number) {
|
|
232
|
+
cart.addItem({ name, price });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
Then('the cart total should be ${int}', function (expected: number) {
|
|
236
|
+
expect(cart.calculateTotal()).to.equal(expected);
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Python
|
|
243
|
+
|
|
244
|
+
### Test Framework: pytest
|
|
245
|
+
|
|
246
|
+
#### Setup
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pip install pytest pytest-cov pytest-mock
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```ini
|
|
253
|
+
# pytest.ini
|
|
254
|
+
[pytest]
|
|
255
|
+
testpaths = tests
|
|
256
|
+
python_files = test_*.py
|
|
257
|
+
python_functions = test_*
|
|
258
|
+
addopts = -v --tb=short
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Complete TDD Example: Calculator
|
|
262
|
+
|
|
263
|
+
**Step 1: RED**
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
# tests/test_calculator.py
|
|
267
|
+
import pytest
|
|
268
|
+
from calculator import Calculator
|
|
269
|
+
|
|
270
|
+
class TestCalculator:
|
|
271
|
+
def test_add_two_positive_numbers(self):
|
|
272
|
+
# Arrange
|
|
273
|
+
calc = Calculator()
|
|
274
|
+
|
|
275
|
+
# Act
|
|
276
|
+
result = calc.add(2, 3)
|
|
277
|
+
|
|
278
|
+
# Assert
|
|
279
|
+
assert result == 5
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Step 2: GREEN**
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
# calculator.py
|
|
286
|
+
class Calculator:
|
|
287
|
+
def add(self, a: float, b: float) -> float:
|
|
288
|
+
return 5 # Fake it!
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Step 3: RED - Force generalization**
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
def test_add_different_numbers(self):
|
|
295
|
+
calc = Calculator()
|
|
296
|
+
result = calc.add(10, 20)
|
|
297
|
+
assert result == 30
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Step 4: GREEN - Implement**
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
class Calculator:
|
|
304
|
+
def add(self, a: float, b: float) -> float:
|
|
305
|
+
return a + b
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Step 5: RED - Add more operations**
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
def test_subtract_returns_difference(self):
|
|
312
|
+
calc = Calculator()
|
|
313
|
+
result = calc.subtract(10, 3)
|
|
314
|
+
assert result == 7
|
|
315
|
+
|
|
316
|
+
def test_divide_by_zero_raises_error(self):
|
|
317
|
+
calc = Calculator()
|
|
318
|
+
with pytest.raises(ValueError, match="Cannot divide by zero"):
|
|
319
|
+
calc.divide(10, 0)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Step 6: GREEN & REFACTOR**
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
class Calculator:
|
|
326
|
+
def add(self, a: float, b: float) -> float:
|
|
327
|
+
return a + b
|
|
328
|
+
|
|
329
|
+
def subtract(self, a: float, b: float) -> float:
|
|
330
|
+
return a - b
|
|
331
|
+
|
|
332
|
+
def divide(self, a: float, b: float) -> float:
|
|
333
|
+
if b == 0:
|
|
334
|
+
raise ValueError("Cannot divide by zero")
|
|
335
|
+
return a / b
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Mocking Example
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
# tests/test_user_service.py
|
|
342
|
+
import pytest
|
|
343
|
+
from unittest.mock import Mock, patch
|
|
344
|
+
from user_service import UserService
|
|
345
|
+
|
|
346
|
+
class TestUserService:
|
|
347
|
+
@patch('user_service.EmailClient')
|
|
348
|
+
def test_register_user_sends_welcome_email(self, mock_email_client):
|
|
349
|
+
# Arrange
|
|
350
|
+
mock_client = Mock()
|
|
351
|
+
mock_email_client.return_value = mock_client
|
|
352
|
+
service = UserService()
|
|
353
|
+
|
|
354
|
+
# Act
|
|
355
|
+
service.register_user("john@example.com", "John")
|
|
356
|
+
|
|
357
|
+
# Assert
|
|
358
|
+
mock_client.send.assert_called_once_with(
|
|
359
|
+
to="john@example.com",
|
|
360
|
+
subject="Welcome!",
|
|
361
|
+
body="Hello John, welcome to our service!"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def test_get_user_returns_user_from_repository(self):
|
|
365
|
+
# Arrange
|
|
366
|
+
mock_repo = Mock()
|
|
367
|
+
mock_repo.find_by_id.return_value = {"id": 1, "name": "John"}
|
|
368
|
+
service = UserService(repository=mock_repo)
|
|
369
|
+
|
|
370
|
+
# Act
|
|
371
|
+
user = service.get_user(1)
|
|
372
|
+
|
|
373
|
+
# Assert
|
|
374
|
+
assert user["name"] == "John"
|
|
375
|
+
mock_repo.find_by_id.assert_called_once_with(1)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### BDD with Behave
|
|
379
|
+
|
|
380
|
+
```gherkin
|
|
381
|
+
# features/calculator.feature
|
|
382
|
+
Feature: Calculator
|
|
383
|
+
As a user
|
|
384
|
+
I want to perform calculations
|
|
385
|
+
So that I can solve math problems
|
|
386
|
+
|
|
387
|
+
Scenario: Add two numbers
|
|
388
|
+
Given I have a calculator
|
|
389
|
+
When I add 5 and 3
|
|
390
|
+
Then the result should be 8
|
|
391
|
+
|
|
392
|
+
Scenario Outline: Multiple additions
|
|
393
|
+
Given I have a calculator
|
|
394
|
+
When I add <a> and <b>
|
|
395
|
+
Then the result should be <result>
|
|
396
|
+
|
|
397
|
+
Examples:
|
|
398
|
+
| a | b | result |
|
|
399
|
+
| 1 | 1 | 2 |
|
|
400
|
+
| 10 | 20 | 30 |
|
|
401
|
+
| -5 | 5 | 0 |
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
# features/steps/calculator_steps.py
|
|
406
|
+
from behave import given, when, then
|
|
407
|
+
from calculator import Calculator
|
|
408
|
+
|
|
409
|
+
@given('I have a calculator')
|
|
410
|
+
def step_impl(context):
|
|
411
|
+
context.calculator = Calculator()
|
|
412
|
+
|
|
413
|
+
@when('I add {a:d} and {b:d}')
|
|
414
|
+
def step_impl(context, a, b):
|
|
415
|
+
context.result = context.calculator.add(a, b)
|
|
416
|
+
|
|
417
|
+
@then('the result should be {expected:d}')
|
|
418
|
+
def step_impl(context, expected):
|
|
419
|
+
assert context.result == expected
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## C#
|
|
425
|
+
|
|
426
|
+
### Test Framework: xUnit
|
|
427
|
+
|
|
428
|
+
#### Setup
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
dotnet add package xunit
|
|
432
|
+
dotnet add package xunit.runner.visualstudio
|
|
433
|
+
dotnet add package Moq
|
|
434
|
+
dotnet add package FluentAssertions
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### Complete TDD Example: Order Processor
|
|
438
|
+
|
|
439
|
+
**Step 1: RED**
|
|
440
|
+
|
|
441
|
+
```csharp
|
|
442
|
+
// OrderProcessorTests.cs
|
|
443
|
+
using Xunit;
|
|
444
|
+
using FluentAssertions;
|
|
445
|
+
|
|
446
|
+
public class OrderProcessorTests
|
|
447
|
+
{
|
|
448
|
+
[Fact]
|
|
449
|
+
public void ProcessOrder_WithValidOrder_ReturnsSuccess()
|
|
450
|
+
{
|
|
451
|
+
// Arrange
|
|
452
|
+
var processor = new OrderProcessor();
|
|
453
|
+
var order = new Order { Id = 1, Amount = 100 };
|
|
454
|
+
|
|
455
|
+
// Act
|
|
456
|
+
var result = processor.Process(order);
|
|
457
|
+
|
|
458
|
+
// Assert
|
|
459
|
+
result.IsSuccess.Should().BeTrue();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Step 2: GREEN**
|
|
465
|
+
|
|
466
|
+
```csharp
|
|
467
|
+
// OrderProcessor.cs
|
|
468
|
+
public class OrderProcessor
|
|
469
|
+
{
|
|
470
|
+
public ProcessResult Process(Order order)
|
|
471
|
+
{
|
|
472
|
+
return new ProcessResult { IsSuccess = true }; // Fake it!
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
public class Order
|
|
477
|
+
{
|
|
478
|
+
public int Id { get; set; }
|
|
479
|
+
public decimal Amount { get; set; }
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
public class ProcessResult
|
|
483
|
+
{
|
|
484
|
+
public bool IsSuccess { get; set; }
|
|
485
|
+
public string? ErrorMessage { get; set; }
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Step 3: RED - Add validation**
|
|
490
|
+
|
|
491
|
+
```csharp
|
|
492
|
+
[Fact]
|
|
493
|
+
public void ProcessOrder_WithZeroAmount_ReturnsFailure()
|
|
494
|
+
{
|
|
495
|
+
var processor = new OrderProcessor();
|
|
496
|
+
var order = new Order { Id = 1, Amount = 0 };
|
|
497
|
+
|
|
498
|
+
var result = processor.Process(order);
|
|
499
|
+
|
|
500
|
+
result.IsSuccess.Should().BeFalse();
|
|
501
|
+
result.ErrorMessage.Should().Be("Order amount must be greater than zero");
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Step 4: GREEN & REFACTOR**
|
|
506
|
+
|
|
507
|
+
```csharp
|
|
508
|
+
public class OrderProcessor
|
|
509
|
+
{
|
|
510
|
+
public ProcessResult Process(Order order)
|
|
511
|
+
{
|
|
512
|
+
if (order.Amount <= 0)
|
|
513
|
+
{
|
|
514
|
+
return ProcessResult.Failure("Order amount must be greater than zero");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return ProcessResult.Success();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
public class ProcessResult
|
|
522
|
+
{
|
|
523
|
+
public bool IsSuccess { get; private set; }
|
|
524
|
+
public string? ErrorMessage { get; private set; }
|
|
525
|
+
|
|
526
|
+
public static ProcessResult Success() =>
|
|
527
|
+
new() { IsSuccess = true };
|
|
528
|
+
|
|
529
|
+
public static ProcessResult Failure(string message) =>
|
|
530
|
+
new() { IsSuccess = false, ErrorMessage = message };
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
#### Mocking Example with Moq
|
|
535
|
+
|
|
536
|
+
```csharp
|
|
537
|
+
using Moq;
|
|
538
|
+
using Xunit;
|
|
539
|
+
using FluentAssertions;
|
|
540
|
+
|
|
541
|
+
public class PaymentServiceTests
|
|
542
|
+
{
|
|
543
|
+
[Fact]
|
|
544
|
+
public async Task ProcessPayment_WithValidCard_ChargesAndSendsReceipt()
|
|
545
|
+
{
|
|
546
|
+
// Arrange
|
|
547
|
+
var mockGateway = new Mock<IPaymentGateway>();
|
|
548
|
+
var mockEmailService = new Mock<IEmailService>();
|
|
549
|
+
|
|
550
|
+
mockGateway
|
|
551
|
+
.Setup(g => g.ChargeAsync(It.IsAny<decimal>(), It.IsAny<string>()))
|
|
552
|
+
.ReturnsAsync(new ChargeResult { TransactionId = "TXN123" });
|
|
553
|
+
|
|
554
|
+
var service = new PaymentService(mockGateway.Object, mockEmailService.Object);
|
|
555
|
+
|
|
556
|
+
// Act
|
|
557
|
+
var result = await service.ProcessPaymentAsync(100, "4111111111111111", "user@example.com");
|
|
558
|
+
|
|
559
|
+
// Assert
|
|
560
|
+
result.TransactionId.Should().Be("TXN123");
|
|
561
|
+
|
|
562
|
+
mockGateway.Verify(
|
|
563
|
+
g => g.ChargeAsync(100, "4111111111111111"),
|
|
564
|
+
Times.Once);
|
|
565
|
+
|
|
566
|
+
mockEmailService.Verify(
|
|
567
|
+
e => e.SendReceiptAsync("user@example.com", It.Is<Receipt>(r => r.TransactionId == "TXN123")),
|
|
568
|
+
Times.Once);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
#### BDD with SpecFlow
|
|
574
|
+
|
|
575
|
+
```gherkin
|
|
576
|
+
# Features/OrderProcessing.feature
|
|
577
|
+
Feature: Order Processing
|
|
578
|
+
As an e-commerce system
|
|
579
|
+
I want to process customer orders
|
|
580
|
+
So that customers can receive their products
|
|
581
|
+
|
|
582
|
+
Scenario: Process valid order
|
|
583
|
+
Given I have an order with amount $100
|
|
584
|
+
When I process the order
|
|
585
|
+
Then the order should be marked as processed
|
|
586
|
+
And a confirmation email should be sent
|
|
587
|
+
|
|
588
|
+
Scenario: Reject order with zero amount
|
|
589
|
+
Given I have an order with amount $0
|
|
590
|
+
When I process the order
|
|
591
|
+
Then the order should be rejected
|
|
592
|
+
And the error message should be "Order amount must be greater than zero"
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
```csharp
|
|
596
|
+
// StepDefinitions/OrderProcessingSteps.cs
|
|
597
|
+
using TechTalk.SpecFlow;
|
|
598
|
+
using FluentAssertions;
|
|
599
|
+
|
|
600
|
+
[Binding]
|
|
601
|
+
public class OrderProcessingSteps
|
|
602
|
+
{
|
|
603
|
+
private Order _order;
|
|
604
|
+
private ProcessResult _result;
|
|
605
|
+
private readonly OrderProcessor _processor = new();
|
|
606
|
+
|
|
607
|
+
[Given(@"I have an order with amount \$(.*)")]
|
|
608
|
+
public void GivenIHaveAnOrderWithAmount(decimal amount)
|
|
609
|
+
{
|
|
610
|
+
_order = new Order { Id = 1, Amount = amount };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
[When(@"I process the order")]
|
|
614
|
+
public void WhenIProcessTheOrder()
|
|
615
|
+
{
|
|
616
|
+
_result = _processor.Process(_order);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
[Then(@"the order should be marked as processed")]
|
|
620
|
+
public void ThenTheOrderShouldBeMarkedAsProcessed()
|
|
621
|
+
{
|
|
622
|
+
_result.IsSuccess.Should().BeTrue();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
[Then(@"the error message should be ""(.*)""")]
|
|
626
|
+
public void ThenTheErrorMessageShouldBe(string expectedMessage)
|
|
627
|
+
{
|
|
628
|
+
_result.ErrorMessage.Should().Be(expectedMessage);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## Go
|
|
636
|
+
|
|
637
|
+
### Test Framework: testing + testify
|
|
638
|
+
|
|
639
|
+
#### Setup
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
go get github.com/stretchr/testify
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
#### Complete TDD Example: User Repository
|
|
646
|
+
|
|
647
|
+
**Step 1: RED**
|
|
648
|
+
|
|
649
|
+
```go
|
|
650
|
+
// user_repository_test.go
|
|
651
|
+
package repository
|
|
652
|
+
|
|
653
|
+
import (
|
|
654
|
+
"testing"
|
|
655
|
+
"github.com/stretchr/testify/assert"
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
func TestUserRepository_FindById_ReturnsUser(t *testing.T) {
|
|
659
|
+
// Arrange
|
|
660
|
+
repo := NewUserRepository()
|
|
661
|
+
repo.Save(&User{ID: 1, Name: "John"})
|
|
662
|
+
|
|
663
|
+
// Act
|
|
664
|
+
user, err := repo.FindById(1)
|
|
665
|
+
|
|
666
|
+
// Assert
|
|
667
|
+
assert.NoError(t, err)
|
|
668
|
+
assert.Equal(t, "John", user.Name)
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
**Step 2: GREEN**
|
|
673
|
+
|
|
674
|
+
```go
|
|
675
|
+
// user_repository.go
|
|
676
|
+
package repository
|
|
677
|
+
|
|
678
|
+
import "errors"
|
|
679
|
+
|
|
680
|
+
type User struct {
|
|
681
|
+
ID int
|
|
682
|
+
Name string
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
type UserRepository struct {
|
|
686
|
+
users map[int]*User
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
func NewUserRepository() *UserRepository {
|
|
690
|
+
return &UserRepository{
|
|
691
|
+
users: make(map[int]*User),
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
func (r *UserRepository) Save(user *User) {
|
|
696
|
+
r.users[user.ID] = user
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
func (r *UserRepository) FindById(id int) (*User, error) {
|
|
700
|
+
user, exists := r.users[id]
|
|
701
|
+
if !exists {
|
|
702
|
+
return nil, errors.New("user not found")
|
|
703
|
+
}
|
|
704
|
+
return user, nil
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**Step 3: RED - Error case**
|
|
709
|
+
|
|
710
|
+
```go
|
|
711
|
+
func TestUserRepository_FindById_ReturnsErrorWhenNotFound(t *testing.T) {
|
|
712
|
+
repo := NewUserRepository()
|
|
713
|
+
|
|
714
|
+
user, err := repo.FindById(999)
|
|
715
|
+
|
|
716
|
+
assert.Nil(t, user)
|
|
717
|
+
assert.EqualError(t, err, "user not found")
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
#### Table-Driven Tests (Go Idiom)
|
|
722
|
+
|
|
723
|
+
```go
|
|
724
|
+
func TestUserRepository_Save(t *testing.T) {
|
|
725
|
+
tests := []struct {
|
|
726
|
+
name string
|
|
727
|
+
user *User
|
|
728
|
+
expected int
|
|
729
|
+
}{
|
|
730
|
+
{
|
|
731
|
+
name: "save single user",
|
|
732
|
+
user: &User{ID: 1, Name: "Alice"},
|
|
733
|
+
expected: 1,
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
name: "save user with different ID",
|
|
737
|
+
user: &User{ID: 2, Name: "Bob"},
|
|
738
|
+
expected: 2,
|
|
739
|
+
},
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
for _, tt := range tests {
|
|
743
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
744
|
+
repo := NewUserRepository()
|
|
745
|
+
repo.Save(tt.user)
|
|
746
|
+
|
|
747
|
+
found, err := repo.FindById(tt.expected)
|
|
748
|
+
|
|
749
|
+
assert.NoError(t, err)
|
|
750
|
+
assert.Equal(t, tt.user.Name, found.Name)
|
|
751
|
+
})
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
#### Mocking with testify/mock
|
|
757
|
+
|
|
758
|
+
```go
|
|
759
|
+
// email_service_test.go
|
|
760
|
+
package service
|
|
761
|
+
|
|
762
|
+
import (
|
|
763
|
+
"testing"
|
|
764
|
+
"github.com/stretchr/testify/assert"
|
|
765
|
+
"github.com/stretchr/testify/mock"
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
// Mock
|
|
769
|
+
type MockEmailSender struct {
|
|
770
|
+
mock.Mock
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
func (m *MockEmailSender) Send(to, subject, body string) error {
|
|
774
|
+
args := m.Called(to, subject, body)
|
|
775
|
+
return args.Error(0)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
func TestNotificationService_SendWelcome(t *testing.T) {
|
|
779
|
+
// Arrange
|
|
780
|
+
mockSender := new(MockEmailSender)
|
|
781
|
+
mockSender.On("Send", "user@example.com", "Welcome!", mock.Anything).Return(nil)
|
|
782
|
+
|
|
783
|
+
service := NewNotificationService(mockSender)
|
|
784
|
+
|
|
785
|
+
// Act
|
|
786
|
+
err := service.SendWelcome("user@example.com")
|
|
787
|
+
|
|
788
|
+
// Assert
|
|
789
|
+
assert.NoError(t, err)
|
|
790
|
+
mockSender.AssertExpectations(t)
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
## Java
|
|
797
|
+
|
|
798
|
+
### Test Framework: JUnit 5 + Mockito
|
|
799
|
+
|
|
800
|
+
#### Setup (Maven)
|
|
801
|
+
|
|
802
|
+
```xml
|
|
803
|
+
<dependencies>
|
|
804
|
+
<dependency>
|
|
805
|
+
<groupId>org.junit.jupiter</groupId>
|
|
806
|
+
<artifactId>junit-jupiter</artifactId>
|
|
807
|
+
<version>5.10.0</version>
|
|
808
|
+
<scope>test</scope>
|
|
809
|
+
</dependency>
|
|
810
|
+
<dependency>
|
|
811
|
+
<groupId>org.mockito</groupId>
|
|
812
|
+
<artifactId>mockito-core</artifactId>
|
|
813
|
+
<version>5.5.0</version>
|
|
814
|
+
<scope>test</scope>
|
|
815
|
+
</dependency>
|
|
816
|
+
<dependency>
|
|
817
|
+
<groupId>org.assertj</groupId>
|
|
818
|
+
<artifactId>assertj-core</artifactId>
|
|
819
|
+
<version>3.24.2</version>
|
|
820
|
+
<scope>test</scope>
|
|
821
|
+
</dependency>
|
|
822
|
+
</dependencies>
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
#### Complete TDD Example: Account Service
|
|
826
|
+
|
|
827
|
+
**Step 1: RED**
|
|
828
|
+
|
|
829
|
+
```java
|
|
830
|
+
// AccountServiceTest.java
|
|
831
|
+
import org.junit.jupiter.api.Test;
|
|
832
|
+
import static org.assertj.core.api.Assertions.*;
|
|
833
|
+
|
|
834
|
+
class AccountServiceTest {
|
|
835
|
+
|
|
836
|
+
@Test
|
|
837
|
+
void deposit_withPositiveAmount_increasesBalance() {
|
|
838
|
+
// Arrange
|
|
839
|
+
AccountService service = new AccountService();
|
|
840
|
+
Account account = new Account(100.0);
|
|
841
|
+
|
|
842
|
+
// Act
|
|
843
|
+
service.deposit(account, 50.0);
|
|
844
|
+
|
|
845
|
+
// Assert
|
|
846
|
+
assertThat(account.getBalance()).isEqualTo(150.0);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
**Step 2: GREEN**
|
|
852
|
+
|
|
853
|
+
```java
|
|
854
|
+
// AccountService.java
|
|
855
|
+
public class AccountService {
|
|
856
|
+
public void deposit(Account account, double amount) {
|
|
857
|
+
account.setBalance(account.getBalance() + amount);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Account.java
|
|
862
|
+
public class Account {
|
|
863
|
+
private double balance;
|
|
864
|
+
|
|
865
|
+
public Account(double initialBalance) {
|
|
866
|
+
this.balance = initialBalance;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
public double getBalance() { return balance; }
|
|
870
|
+
public void setBalance(double balance) { this.balance = balance; }
|
|
871
|
+
}
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
**Step 3: RED - Validation**
|
|
875
|
+
|
|
876
|
+
```java
|
|
877
|
+
@Test
|
|
878
|
+
void deposit_withNegativeAmount_throwsException() {
|
|
879
|
+
AccountService service = new AccountService();
|
|
880
|
+
Account account = new Account(100.0);
|
|
881
|
+
|
|
882
|
+
assertThatThrownBy(() -> service.deposit(account, -50.0))
|
|
883
|
+
.isInstanceOf(IllegalArgumentException.class)
|
|
884
|
+
.hasMessage("Deposit amount must be positive");
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
@Test
|
|
888
|
+
void withdraw_withInsufficientFunds_throwsException() {
|
|
889
|
+
AccountService service = new AccountService();
|
|
890
|
+
Account account = new Account(100.0);
|
|
891
|
+
|
|
892
|
+
assertThatThrownBy(() -> service.withdraw(account, 150.0))
|
|
893
|
+
.isInstanceOf(InsufficientFundsException.class)
|
|
894
|
+
.hasMessage("Insufficient funds");
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
**Step 4: GREEN & REFACTOR**
|
|
899
|
+
|
|
900
|
+
```java
|
|
901
|
+
public class AccountService {
|
|
902
|
+
|
|
903
|
+
public void deposit(Account account, double amount) {
|
|
904
|
+
validatePositiveAmount(amount, "Deposit");
|
|
905
|
+
account.setBalance(account.getBalance() + amount);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
public void withdraw(Account account, double amount) {
|
|
909
|
+
validatePositiveAmount(amount, "Withdrawal");
|
|
910
|
+
if (account.getBalance() < amount) {
|
|
911
|
+
throw new InsufficientFundsException("Insufficient funds");
|
|
912
|
+
}
|
|
913
|
+
account.setBalance(account.getBalance() - amount);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
private void validatePositiveAmount(double amount, String operation) {
|
|
917
|
+
if (amount <= 0) {
|
|
918
|
+
throw new IllegalArgumentException(operation + " amount must be positive");
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
#### Mocking with Mockito
|
|
925
|
+
|
|
926
|
+
```java
|
|
927
|
+
import org.junit.jupiter.api.Test;
|
|
928
|
+
import org.junit.jupiter.api.extension.ExtendWith;
|
|
929
|
+
import org.mockito.InjectMocks;
|
|
930
|
+
import org.mockito.Mock;
|
|
931
|
+
import org.mockito.junit.jupiter.MockitoExtension;
|
|
932
|
+
import static org.mockito.Mockito.*;
|
|
933
|
+
import static org.assertj.core.api.Assertions.*;
|
|
934
|
+
|
|
935
|
+
@ExtendWith(MockitoExtension.class)
|
|
936
|
+
class OrderServiceTest {
|
|
937
|
+
|
|
938
|
+
@Mock
|
|
939
|
+
private PaymentGateway paymentGateway;
|
|
940
|
+
|
|
941
|
+
@Mock
|
|
942
|
+
private InventoryService inventoryService;
|
|
943
|
+
|
|
944
|
+
@InjectMocks
|
|
945
|
+
private OrderService orderService;
|
|
946
|
+
|
|
947
|
+
@Test
|
|
948
|
+
void placeOrder_withValidOrder_processesPaymentAndUpdatesInventory() {
|
|
949
|
+
// Arrange
|
|
950
|
+
Order order = new Order("PROD-1", 2, 50.0);
|
|
951
|
+
when(inventoryService.checkStock("PROD-1")).thenReturn(10);
|
|
952
|
+
when(paymentGateway.charge(100.0)).thenReturn(new PaymentResult("TXN-123", true));
|
|
953
|
+
|
|
954
|
+
// Act
|
|
955
|
+
OrderResult result = orderService.placeOrder(order);
|
|
956
|
+
|
|
957
|
+
// Assert
|
|
958
|
+
assertThat(result.isSuccess()).isTrue();
|
|
959
|
+
assertThat(result.getTransactionId()).isEqualTo("TXN-123");
|
|
960
|
+
|
|
961
|
+
verify(inventoryService).checkStock("PROD-1");
|
|
962
|
+
verify(inventoryService).reserve("PROD-1", 2);
|
|
963
|
+
verify(paymentGateway).charge(100.0);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
#### BDD with Cucumber-JVM
|
|
969
|
+
|
|
970
|
+
```gherkin
|
|
971
|
+
# src/test/resources/features/account.feature
|
|
972
|
+
Feature: Account Management
|
|
973
|
+
As a bank customer
|
|
974
|
+
I want to manage my account balance
|
|
975
|
+
So that I can track my finances
|
|
976
|
+
|
|
977
|
+
Scenario: Deposit money
|
|
978
|
+
Given I have an account with balance $100
|
|
979
|
+
When I deposit $50
|
|
980
|
+
Then my balance should be $150
|
|
981
|
+
|
|
982
|
+
Scenario: Withdraw with insufficient funds
|
|
983
|
+
Given I have an account with balance $100
|
|
984
|
+
When I try to withdraw $150
|
|
985
|
+
Then I should see an error "Insufficient funds"
|
|
986
|
+
And my balance should be $100
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
```java
|
|
990
|
+
// StepDefinitions.java
|
|
991
|
+
import io.cucumber.java.en.*;
|
|
992
|
+
import static org.assertj.core.api.Assertions.*;
|
|
993
|
+
|
|
994
|
+
public class AccountStepDefinitions {
|
|
995
|
+
private Account account;
|
|
996
|
+
private AccountService service = new AccountService();
|
|
997
|
+
private Exception caughtException;
|
|
998
|
+
|
|
999
|
+
@Given("I have an account with balance ${double}")
|
|
1000
|
+
public void iHaveAccountWithBalance(double balance) {
|
|
1001
|
+
account = new Account(balance);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
@When("I deposit ${double}")
|
|
1005
|
+
public void iDeposit(double amount) {
|
|
1006
|
+
service.deposit(account, amount);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
@When("I try to withdraw ${double}")
|
|
1010
|
+
public void iTryToWithdraw(double amount) {
|
|
1011
|
+
try {
|
|
1012
|
+
service.withdraw(account, amount);
|
|
1013
|
+
} catch (Exception e) {
|
|
1014
|
+
caughtException = e;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
@Then("my balance should be ${double}")
|
|
1019
|
+
public void myBalanceShouldBe(double expected) {
|
|
1020
|
+
assertThat(account.getBalance()).isEqualTo(expected);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
@Then("I should see an error {string}")
|
|
1024
|
+
public void iShouldSeeAnError(String message) {
|
|
1025
|
+
assertThat(caughtException).hasMessage(message);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
## Ruby
|
|
1033
|
+
|
|
1034
|
+
### Test Framework: RSpec
|
|
1035
|
+
|
|
1036
|
+
#### Setup
|
|
1037
|
+
|
|
1038
|
+
```ruby
|
|
1039
|
+
# Gemfile
|
|
1040
|
+
group :test do
|
|
1041
|
+
gem 'rspec', '~> 3.12'
|
|
1042
|
+
gem 'rspec-mocks'
|
|
1043
|
+
end
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
```bash
|
|
1047
|
+
bundle install
|
|
1048
|
+
rspec --init
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
#### Complete TDD Example: Shopping Cart
|
|
1052
|
+
|
|
1053
|
+
**Step 1: RED**
|
|
1054
|
+
|
|
1055
|
+
```ruby
|
|
1056
|
+
# spec/shopping_cart_spec.rb
|
|
1057
|
+
require_relative '../lib/shopping_cart'
|
|
1058
|
+
|
|
1059
|
+
RSpec.describe ShoppingCart do
|
|
1060
|
+
describe '#total' do
|
|
1061
|
+
it 'returns 0 for empty cart' do
|
|
1062
|
+
cart = ShoppingCart.new
|
|
1063
|
+
|
|
1064
|
+
expect(cart.total).to eq(0)
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
end
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
**Step 2: GREEN**
|
|
1071
|
+
|
|
1072
|
+
```ruby
|
|
1073
|
+
# lib/shopping_cart.rb
|
|
1074
|
+
class ShoppingCart
|
|
1075
|
+
def total
|
|
1076
|
+
0 # Fake it!
|
|
1077
|
+
end
|
|
1078
|
+
end
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
**Step 3: RED - Add items**
|
|
1082
|
+
|
|
1083
|
+
```ruby
|
|
1084
|
+
describe '#total' do
|
|
1085
|
+
it 'returns sum of item prices' do
|
|
1086
|
+
cart = ShoppingCart.new
|
|
1087
|
+
cart.add_item(name: 'Widget', price: 10)
|
|
1088
|
+
cart.add_item(name: 'Gadget', price: 20)
|
|
1089
|
+
|
|
1090
|
+
expect(cart.total).to eq(30)
|
|
1091
|
+
end
|
|
1092
|
+
end
|
|
1093
|
+
```
|
|
1094
|
+
|
|
1095
|
+
**Step 4: GREEN & REFACTOR**
|
|
1096
|
+
|
|
1097
|
+
```ruby
|
|
1098
|
+
class ShoppingCart
|
|
1099
|
+
def initialize
|
|
1100
|
+
@items = []
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def add_item(item)
|
|
1104
|
+
@items << item
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
def total
|
|
1108
|
+
@items.sum { |item| item[:price] }
|
|
1109
|
+
end
|
|
1110
|
+
end
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
**Step 5: RED - Discount**
|
|
1114
|
+
|
|
1115
|
+
```ruby
|
|
1116
|
+
describe '#apply_discount' do
|
|
1117
|
+
it 'reduces total by percentage' do
|
|
1118
|
+
cart = ShoppingCart.new
|
|
1119
|
+
cart.add_item(name: 'Widget', price: 100)
|
|
1120
|
+
cart.apply_discount(20) # 20% off
|
|
1121
|
+
|
|
1122
|
+
expect(cart.total).to eq(80)
|
|
1123
|
+
end
|
|
1124
|
+
end
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
**Step 6: GREEN**
|
|
1128
|
+
|
|
1129
|
+
```ruby
|
|
1130
|
+
class ShoppingCart
|
|
1131
|
+
def initialize
|
|
1132
|
+
@items = []
|
|
1133
|
+
@discount_percent = 0
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def add_item(item)
|
|
1137
|
+
@items << item
|
|
1138
|
+
end
|
|
1139
|
+
|
|
1140
|
+
def apply_discount(percent)
|
|
1141
|
+
@discount_percent = percent
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1144
|
+
def total
|
|
1145
|
+
subtotal = @items.sum { |item| item[:price] }
|
|
1146
|
+
discount = subtotal * (@discount_percent / 100.0)
|
|
1147
|
+
subtotal - discount
|
|
1148
|
+
end
|
|
1149
|
+
end
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
#### RSpec Mocking
|
|
1153
|
+
|
|
1154
|
+
```ruby
|
|
1155
|
+
# spec/order_service_spec.rb
|
|
1156
|
+
RSpec.describe OrderService do
|
|
1157
|
+
describe '#process' do
|
|
1158
|
+
it 'charges payment and sends confirmation' do
|
|
1159
|
+
# Arrange
|
|
1160
|
+
payment_gateway = instance_double(PaymentGateway)
|
|
1161
|
+
email_service = instance_double(EmailService)
|
|
1162
|
+
|
|
1163
|
+
allow(payment_gateway).to receive(:charge).and_return(
|
|
1164
|
+
OpenStruct.new(success: true, transaction_id: 'TXN123')
|
|
1165
|
+
)
|
|
1166
|
+
allow(email_service).to receive(:send_confirmation)
|
|
1167
|
+
|
|
1168
|
+
service = OrderService.new(
|
|
1169
|
+
payment_gateway: payment_gateway,
|
|
1170
|
+
email_service: email_service
|
|
1171
|
+
)
|
|
1172
|
+
|
|
1173
|
+
order = Order.new(amount: 100, email: 'user@example.com')
|
|
1174
|
+
|
|
1175
|
+
# Act
|
|
1176
|
+
result = service.process(order)
|
|
1177
|
+
|
|
1178
|
+
# Assert
|
|
1179
|
+
expect(result.success?).to be true
|
|
1180
|
+
expect(payment_gateway).to have_received(:charge).with(100)
|
|
1181
|
+
expect(email_service).to have_received(:send_confirmation)
|
|
1182
|
+
.with('user@example.com', hash_including(transaction_id: 'TXN123'))
|
|
1183
|
+
end
|
|
1184
|
+
end
|
|
1185
|
+
end
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
#### BDD with RSpec (Native)
|
|
1189
|
+
|
|
1190
|
+
RSpec has built-in BDD-style syntax:
|
|
1191
|
+
|
|
1192
|
+
```ruby
|
|
1193
|
+
# spec/features/shopping_cart_spec.rb
|
|
1194
|
+
RSpec.feature 'Shopping Cart', type: :feature do
|
|
1195
|
+
scenario 'User adds items to cart' do
|
|
1196
|
+
# Given
|
|
1197
|
+
cart = ShoppingCart.new
|
|
1198
|
+
|
|
1199
|
+
# When
|
|
1200
|
+
cart.add_item(name: 'Widget', price: 10)
|
|
1201
|
+
cart.add_item(name: 'Gadget', price: 20)
|
|
1202
|
+
|
|
1203
|
+
# Then
|
|
1204
|
+
expect(cart.total).to eq(30)
|
|
1205
|
+
expect(cart.item_count).to eq(2)
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
scenario 'User applies discount code' do
|
|
1209
|
+
# Given
|
|
1210
|
+
cart = ShoppingCart.new
|
|
1211
|
+
cart.add_item(name: 'Widget', price: 100)
|
|
1212
|
+
|
|
1213
|
+
# When
|
|
1214
|
+
cart.apply_discount(20)
|
|
1215
|
+
|
|
1216
|
+
# Then
|
|
1217
|
+
expect(cart.total).to eq(80)
|
|
1218
|
+
end
|
|
1219
|
+
end
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
#### BDD with Cucumber
|
|
1223
|
+
|
|
1224
|
+
```gherkin
|
|
1225
|
+
# features/shopping_cart.feature
|
|
1226
|
+
Feature: Shopping Cart
|
|
1227
|
+
As a customer
|
|
1228
|
+
I want to manage my shopping cart
|
|
1229
|
+
So that I can purchase products
|
|
1230
|
+
|
|
1231
|
+
Scenario: Add item to cart
|
|
1232
|
+
Given I have an empty cart
|
|
1233
|
+
When I add a "Widget" priced at $10
|
|
1234
|
+
Then my cart total should be $10
|
|
1235
|
+
|
|
1236
|
+
Scenario: Apply discount
|
|
1237
|
+
Given I have a cart with total $100
|
|
1238
|
+
When I apply a 20% discount
|
|
1239
|
+
Then my cart total should be $80
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
```ruby
|
|
1243
|
+
# features/step_definitions/cart_steps.rb
|
|
1244
|
+
Given('I have an empty cart') do
|
|
1245
|
+
@cart = ShoppingCart.new
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1248
|
+
When('I add a {string} priced at ${int}') do |name, price|
|
|
1249
|
+
@cart.add_item(name: name, price: price)
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
Then('my cart total should be ${int}') do |expected|
|
|
1253
|
+
expect(@cart.total).to eq(expected)
|
|
1254
|
+
end
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
---
|
|
1258
|
+
|
|
1259
|
+
## Framework Comparison Summary
|
|
1260
|
+
|
|
1261
|
+
| Language | Unit Test | Mock | BDD | Watch Mode |
|
|
1262
|
+
|----------|-----------|------|-----|------------|
|
|
1263
|
+
| **JavaScript** | Jest/Vitest | jest.mock | Cucumber.js | `--watch` |
|
|
1264
|
+
| **Python** | pytest | unittest.mock | Behave | pytest-watch |
|
|
1265
|
+
| **C#** | xUnit/NUnit | Moq | SpecFlow | dotnet watch |
|
|
1266
|
+
| **Go** | testing | testify/mock | godog | go test -v |
|
|
1267
|
+
| **Java** | JUnit 5 | Mockito | Cucumber-JVM | Maven/Gradle |
|
|
1268
|
+
| **Ruby** | RSpec | rspec-mocks | Cucumber/RSpec | guard |
|
|
1269
|
+
|
|
1270
|
+
---
|
|
1271
|
+
|
|
1272
|
+
## Related Documents
|
|
1273
|
+
|
|
1274
|
+
- [SKILL.md](./SKILL.md) - TDD Assistant overview
|
|
1275
|
+
- [TDD Workflow](./tdd-workflow.md) - Detailed workflow guide
|
|
1276
|
+
- [TDD Core Standard](../../../core/test-driven-development.md) - Full TDD standard
|