[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7ACKPRPKNASERXZ82H18VCG":3,"skill-same:01M7ACKPRPKNASERXZ82H18VCG":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":14,"repositoryHtmlUrl":50,"repositoryHomepageUrl":6,"ownerHtmlUrl":51,"ownerBlogUrl":52,"ownerDescription":53,"updatedAt":54,"install":55,"resources":67},"01M7ACKPRPKNASERXZ82H18VCG","Test-Driven Development",null,"Provides expert knowledge for applying the Red-Green-Refactor cycle, designing tests at the right pyramid layer, and implementing TDD across Spring Boot + Kotlin, Python (pytest), and Next.js \u002F Astro (Vitest + Playwright) stacks.","lgzarturo\u002Ffeedback_widget","lgzarturo","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F128883?v=4","01M720TSS2BWM10Y93J765VZ8E","algforge","01M7ACJRYQY4Z9SAB0Z7HQA0M0",0,"MIT","https:\u002F\u002Fgithub.com\u002Flgzarturo\u002Ffeedback_widget\u002Fblob\u002Fmain\u002F.cursor\u002Fskills\u002Ftesting-tdd\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nid: testing-tdd\nversion: 1.0.0\nname: Test-Driven Development\ndescription: >\n  Provides expert knowledge for applying the Red-Green-Refactor cycle, designing tests at the right pyramid layer, and implementing TDD across Spring Boot + Kotlin, Python (pytest), and Next.js \u002F Astro (Vitest + Playwright) stacks.\n\nuser-invokable: true\nlicense: MIT\nmetadata:\n  author: lgzarturo\n  category: testing\n\ncompatibility:\n  tools: [claude, codex, gemini, agy, opencode]\n  stacks:\n    languages: []\n    frameworks: []\n\nrisk:\n  level: low\n  can_execute_shell: false\n  can_modify_files: true\n  requires_network: false\n\ninputs: []\n\noutputs: []\n\nquality:\n  reviewed_by: codeconductor-core\n  version: 0.1.0\n---\n\n\n\n# Test-Driven Development\n\n## The Red-Green-Refactor Cycle\n\n```text\n  ┌─────────────────────────────────────────────┐\n  │                                             │\n  │   RED → write a failing test                │\n  │    ↓                                        │\n  │   GREEN → write the minimum code to pass    │\n  │    ↓                                        │\n  │   REFACTOR → clean up without breaking      │\n  │    ↓                                        │\n  │   repeat ───────────────────────────────────┘\n```\n\n**Red**: Write a test that describes one behavior you want. Run it. It must\nfail — if it passes without implementation, the test is not testing anything.\n\n**Green**: Write the simplest code that makes the test pass. Do not optimize.\nDo not add features. Just pass the test.\n\n**Refactor**: Clean up duplication, naming, and structure. Run the tests after\nevery change. If any test breaks, the refactor changed behavior — that is a bug.\n\nThe cycle is short. Each iteration should take minutes, not hours. If a cycle\ntakes longer than 30 minutes, the behavior being tested is too large — split it.\n\n## When to Apply TDD\n\n**Apply TDD for:**\n\n- New business logic with clear rules (validation, calculations, state machines)\n- Bug fixes — write a regression test that reproduces the bug first\n- Public service layer methods\n- API endpoints with defined acceptance criteria\n\n**Do not apply TDD for:**\n\n- Exploratory code where the design is not yet known — spike first, then write\n  tests for the final design\n- Trivial scaffolding (data class constructors, getters)\n- Database migrations — test the resulting schema state, not the migration steps\n- Third-party SDK wrappers where behavior is owned by the library\n\n## Test Pyramid\n\n```text\n       \u002F\\\n      \u002F  \\\n     \u002F E2E\\       10% — full browser\u002FAPI flows, happy path + critical errors\n    \u002F------\\\n   \u002F  Integ \\     20% — components with real dependencies (DB, HTTP clients)\n  \u002F----------\\\n \u002F    Unit    \\   70% — isolated logic, mocked dependencies, sub-millisecond\n\u002F______________\\\n```\n\nUnit tests are the TDD workhorse. Write them first. Integration tests verify\nthat components connect correctly. E2E tests verify that the system works for\nthe user — keep them minimal and focused on the critical paths.\n\n## TDD Rules\n\n**One failing test at a time.** Write one test, make it pass, then write the\nnext. Do not write multiple failing tests before implementing.\n\n**The test must fail for the right reason.** A `NullPointerException` on setup\nis not a meaningful failure — that is a broken test. The failure must be the\nassertion, not an error in the test itself.\n\n**Minimum implementation.** In the Green phase, return a hardcoded value if\nthat makes the test pass. The next test will force you to generalize.\n\n**Refactor only on green.** Never refactor when tests are failing. You lose the\nsafety net that tells you whether the refactor changed behavior.\n\n**Tests are not optional after the fact.** Writing tests after implementation\nis not TDD. It is documentation. It catches far fewer design problems.\n\n---\n\n## Spring Boot + Kotlin\n\n### Naming Convention\n\n```kotlin\n@Test\nfun `should [expected behavior] when [condition]`()\n```\n\n```kotlin\n@Test\nfun `should return user when found by id`() { ... }\n\n@Test\nfun `should throw NotFoundException when user does not exist`() { ... }\n\n@Test\nfun `should not create user when email already exists`() { ... }\n```\n\n### TDD Cycle — Unit Test Example\n\n**Requirement**: `UserService.create()` should reject duplicate emails.\n\n**Red** — write the failing test first:\n\n```kotlin\n@ExtendWith(MockKExtension::class)\nclass UserServiceTest {\n\n    @MockK\n    private lateinit var userRepository: UserRepository\n\n    private lateinit var userService: UserService\n\n    @BeforeEach\n    fun setUp() {\n        userService = UserService(userRepository)\n    }\n\n    @Test\n    fun `should throw ConflictException when email already exists`() {\n        \u002F\u002F Arrange\n        val email = \"existing@example.com\"\n        every { userRepository.existsByEmail(email) } returns true\n\n        \u002F\u002F Act & Assert\n        assertThrows\u003CConflictException> {\n            userService.create(email = email, name = \"Test\")\n        }\n        verify(exactly = 0) { userRepository.save(any()) }\n    }\n}\n```\n\nRun → fails (method does not exist yet).\n\n**Green** — minimum implementation:\n\n```kotlin\nclass UserService(private val userRepository: UserRepository) {\n\n    fun create(email: String, name: String): User {\n        if (userRepository.existsByEmail(email)) {\n            throw ConflictException(\"Email $email is already registered\")\n        }\n        return userRepository.save(User(email = email, name = name))\n    }\n}\n```\n\nRun → passes.\n\n**Refactor** — extract the check into a private guard, add KDoc only if the\ndomain rule is non-obvious. Run tests → still green.\n\n**Next test** — happy path:\n\n```kotlin\n@Test\nfun `should create and return user when email is unique`() {\n    val email = \"new@example.com\"\n    val saved = User(id = UUID.randomUUID(), email = email, name = \"New User\")\n\n    every { userRepository.existsByEmail(email) } returns false\n    every { userRepository.save(any()) } returns saved\n\n    val result = userService.create(email = email, name = \"New User\")\n\n    assertThat(result.email).isEqualTo(email)\n    verify(exactly = 1) { userRepository.save(any()) }\n}\n```\n\n### TDD Cycle — Integration Test Example\n\n**Requirement**: `GET \u002Fapi\u002Fusers\u002F{id}` returns 404 when the user does not exist.\n\n**Red**:\n\n```kotlin\n@SpringBootTest\n@AutoConfigureMockMvc\nclass UserControllerTest {\n\n    @Autowired\n    private lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `should return 404 when user does not exist`() {\n        mockMvc.perform(get(\"\u002Fapi\u002Fusers\u002F${UUID.randomUUID()}\"))\n            .andExpect(status().isNotFound)\n            .andExpect(jsonPath(\"$.code\").value(\"NOT_FOUND\"))\n    }\n}\n```\n\nRun → fails (endpoint may not exist yet, or returns wrong status).\n\n**Green** — add\u002Ffix the endpoint and error handler. Run → passes.\n\n**Refactor** — extract error response builder if duplicated across handlers.\n\n### MockK Quick Reference\n\n```kotlin\n\u002F\u002F Stub return value\nevery { repo.findById(id) } returns Optional.of(user)\n\n\u002F\u002F Stub exception\nevery { repo.save(any()) } throws DataIntegrityViolationException(\"duplicate\")\n\n\u002F\u002F Verify call count\nverify(exactly = 1) { repo.save(any()) }\nverify(exactly = 0) { emailService.send(any()) }\n\n\u002F\u002F Capture argument\nval slot = slot\u003CUser>()\nevery { repo.save(capture(slot)) } returns savedUser\nassertThat(slot.captured.email).isEqualTo(\"expected@example.com\")\n\n\u002F\u002F Coroutines\ncoEvery { repo.findById(id) } returns user\ncoVerify(exactly = 1) { repo.findById(id) }\n```\n\n---\n\n## Python (pytest)\n\n### Naming Convention\n\n```python\ndef test_[behavior]_when_[condition]():\n```\n\n```python\ndef test_returns_user_when_found_by_id(): ...\ndef test_raises_not_found_when_user_does_not_exist(): ...\ndef test_does_not_create_user_when_email_already_exists(): ...\n```\n\n### TDD Cycle — Unit Test Example\n\n**Requirement**: `UserService.create()` should reject duplicate emails.\n\n**Red**:\n\n```python\n# tests\u002Fusers\u002Ftest_services.py\nimport pytest\nfrom unittest.mock import MagicMock\nfrom apps.users.services import UserService\n\n\ndef test_raises_when_email_already_exists():\n    repo = MagicMock()\n    repo.exists_by_email.return_value = True\n    service = UserService(repo)\n\n    with pytest.raises(ValueError, match=\"already registered\"):\n        service.create(email=\"taken@example.com\", name=\"Test\")\n\n    repo.save.assert_not_called()\n```\n\nRun → fails (`UserService` does not exist).\n\n**Green**:\n\n```python\n# apps\u002Fusers\u002Fservices.py\nclass UserService:\n\n    def __init__(self, repository):\n        self._repo = repository\n\n    def create(self, *, email: str, name: str):\n        if self._repo.exists_by_email(email):\n            raise ValueError(f\"Email {email!r} is already registered\")\n        return self._repo.save({\"email\": email, \"name\": name})\n```\n\nRun → passes.\n\n### pytest with Django and factory-boy\n\n```python\n# tests\u002Fusers\u002Ffactories.py\nimport factory\nfrom factory.django import DjangoModelFactory\nfrom apps.users.models import User\n\n\nclass UserFactory(DjangoModelFactory):\n    class Meta:\n        model = User\n\n    email = factory.Sequence(lambda n: f\"user{n}@example.com\")\n    name = factory.Faker(\"name\")\n    is_active = True\n```\n\n```python\n# conftest.py\nimport pytest\nfrom pytest_factoryboy import register\nfrom tests.users.factories import UserFactory\n\nregister(UserFactory)\n```\n\n```python\n# tests\u002Fusers\u002Ftest_services.py\nimport pytest\nfrom apps.users.services import UserService\n\n\n@pytest.mark.django_db\ndef test_create_user_succeeds_with_unique_email():\n    service = UserService()\n\n    user = service.create(email=\"new@example.com\", name=\"Alice\")\n\n    assert user.pk is not None\n    assert user.email == \"new@example.com\"\n\n\n@pytest.mark.django_db\ndef test_create_user_raises_when_email_taken(user_factory):\n    user_factory(email=\"taken@example.com\")\n\n    with pytest.raises(ValueError, match=\"already registered\"):\n        UserService().create(email=\"taken@example.com\", name=\"Bob\")\n```\n\n### pytest Fixtures\n\n```python\n# conftest.py\nimport pytest\n\n\n@pytest.fixture\ndef authenticated_client(client, user_factory):\n    user = user_factory()\n    client.force_login(user)\n    return client, user\n\n\n# In test\ndef test_profile_requires_auth(client):\n    response = client.get(\"\u002Fapi\u002Fprofile\u002F\")\n    assert response.status_code == 401\n\n\ndef test_profile_returns_user_data(authenticated_client):\n    client, user = authenticated_client\n    response = client.get(\"\u002Fapi\u002Fprofile\u002F\")\n    assert response.status_code == 200\n    assert response.json()[\"email\"] == user.email\n```\n\n---\n\n## Next.js \u002F Astro (Vitest + RTL + Playwright)\n\n### Naming Convention\n\n```typescript\nit('should [behavior] when [condition]', () => { ... })\ndescribe('ComponentName', () => {\n  describe('when [state]', () => {\n    it('should [behavior]', () => { ... })\n  })\n})\n```\n\n### TDD Cycle — Component Test Example (Vitest + RTL)\n\n**Requirement**: `\u003CCounter>` increments when the button is clicked.\n\n**Red**:\n\n```typescript\n\u002F\u002F components\u002Fcounter.test.tsx\nimport { render, screen, fireEvent } from '@testing-library\u002Freact';\nimport { Counter } from '.\u002Fcounter';\n\ndescribe('Counter', () => {\n  it('should display initial count of 0', () => {\n    render(\u003CCounter \u002F>);\n    expect(screen.getByText('Count: 0')).toBeInTheDocument();\n  });\n\n  it('should increment count when button is clicked', () => {\n    render(\u003CCounter \u002F>);\n    fireEvent.click(screen.getByRole('button', { name: \u002Fincrement\u002Fi }));\n    expect(screen.getByText('Count: 1')).toBeInTheDocument();\n  });\n});\n```\n\nRun → fails (component does not exist).\n\n**Green**:\n\n```tsx\n\u002F\u002F components\u002Fcounter.tsx\n'use client';\nimport { useState } from 'react';\n\nexport function Counter() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003Cp>Count: {count}\u003C\u002Fp>\n      \u003Cbutton onClick={() => setCount(c => c + 1)} aria-label=\"increment\">\n        +\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\nRun → passes.\n\n**Refactor** — extract `useCounter` hook if logic grows. Run tests → green.\n\n### Vitest Setup\n\n```typescript\n\u002F\u002F vitest.config.ts\nimport { defineConfig } from 'vitest\u002Fconfig';\nimport react from '@vitejs\u002Fplugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  test: {\n    environment: 'jsdom',\n    globals: true,\n    setupFiles: ['.\u002Fvitest.setup.ts'],\n  },\n});\n```\n\n```typescript\n\u002F\u002F vitest.setup.ts\nimport '@testing-library\u002Fjest-dom';\n```\n\n### Server Action Testing\n\nTest Server Actions by calling them directly in unit tests — no HTTP overhead.\n\n```typescript\n\u002F\u002F app\u002Fposts\u002Fcreate\u002Factions.test.ts\nimport { createPost } from '.\u002Factions';\n\nvi.mock('@\u002Flib\u002Fdb', () => ({\n  db: {\n    post: {\n      create: vi.fn(),\n    },\n  },\n}));\n\ndescribe('createPost', () => {\n  it('should return validation error when title is empty', async () => {\n    const formData = new FormData();\n    formData.set('title', '');\n    formData.set('content', 'Some content');\n\n    const result = await createPost(formData);\n\n    expect(result?.error?.title).toBeDefined();\n  });\n});\n```\n\n### E2E Testing with Playwright\n\nReserve Playwright for critical user flows only. Do not replicate unit test\nscenarios in E2E.\n\n```typescript\n\u002F\u002F e2e\u002Fauth.spec.ts\nimport { test, expect } from '@playwright\u002Ftest';\n\ntest.describe('Authentication', () => {\n  test('should redirect to login when accessing protected route unauthenticated', async ({ page }) => {\n    await page.goto('\u002Fdashboard');\n    await expect(page).toHaveURL('\u002Flogin');\n  });\n\n  test('should show dashboard after successful login', async ({ page }) => {\n    await page.goto('\u002Flogin');\n    await page.getByLabel('Email').fill('user@example.com');\n    await page.getByLabel('Password').fill('password123');\n    await page.getByRole('button', { name: 'Sign in' }).click();\n    await expect(page).toHaveURL('\u002Fdashboard');\n  });\n});\n```\n\n```typescript\n\u002F\u002F playwright.config.ts\nimport { defineConfig } from '@playwright\u002Ftest';\n\nexport default defineConfig({\n  testDir: '.\u002Fe2e',\n  use: {\n    baseURL: 'http:\u002F\u002Flocalhost:3000',\n  },\n  webServer: {\n    command: 'npm run build && npm run start',\n    port: 3000,\n    reuseExistingServer: !process.env.CI,\n  },\n});\n```\n\n---\n\n## CI Integration\n\nRun tests in this order — fail fast on the cheapest failures first:\n\n```text\n1. Unit tests          — sub-second feedback; block all subsequent steps on failure\n2. Integration tests   — real DB, real HTTP; slower but necessary\n3. E2E tests           — slowest; run only on main branch or release branches\n```\n\n```yaml\n# Example GitHub Actions\njobs:\n  test:\n    steps:\n      - name: Unit tests\n        run: npx vitest run --reporter=verbose\n      - name: Integration tests\n        run: npx vitest run --project=integration\n      - name: E2E tests\n        if: github.ref == 'refs\u002Fheads\u002Fmain'\n        run: npx playwright test\n```\n\n**Pipeline rules:**\n\n- A failing unit test blocks the entire pipeline — do not merge broken tests\n- E2E tests are expensive; run them on CI but not in pre-commit hooks\n- Flaky tests must be fixed or deleted — a test that sometimes passes is worse\n  than no test, because it erodes trust in the suite\n","# Test-Driven Development\n\n## The Red-Green-Refactor Cycle\n\n```text\n  ┌─────────────────────────────────────────────┐\n  │                                             │\n  │   RED → write a failing test                │\n  │    ↓                                        │\n  │   GREEN → write the minimum code to pass    │\n  │    ↓                                        │\n  │   REFACTOR → clean up without breaking      │\n  │    ↓                                        │\n  │   repeat ───────────────────────────────────┘\n```\n\n**Red**: Write a test that describes one behavior you want. Run it. It must\nfail — if it passes without implementation, the test is not testing anything.\n\n**Green**: Write the simplest code that makes the test pass. Do not optimize.\nDo not add features. Just pass the test.\n\n**Refactor**: Clean up duplication, naming, and structure. Run the tests after\nevery change. If any test breaks, the refactor changed behavior — that is a bug.\n\nThe cycle is short. Each iteration should take minutes, not hours. If a cycle\ntakes longer than 30 minutes, the behavior being tested is too large — split it.\n\n## When to Apply TDD\n\n**Apply TDD for:**\n\n- New business logic with clear rules (validation, calculations, state machines)\n- Bug fixes — write a regression test that reproduces the bug first\n- Public service layer methods\n- API endpoints with defined acceptance criteria\n\n**Do not apply TDD for:**\n\n- Exploratory code where the design is not yet known — spike first, then write\n  tests for the final design\n- Trivial scaffolding (data class constructors, getters)\n- Database migrations — test the resulting schema state, not the migration steps\n- Third-party SDK wrappers where behavior is owned by the library\n\n## Test Pyramid\n\n```text\n       \u002F\\\n      \u002F  \\\n     \u002F E2E\\       10% — full browser\u002FAPI flows, happy path + critical errors\n    \u002F------\\\n   \u002F  Integ \\     20% — components with real dependencies (DB, HTTP clients)\n  \u002F----------\\\n \u002F    Unit    \\   70% — isolated logic, mocked dependencies, sub-millisecond\n\u002F______________\\\n```\n\nUnit tests are the TDD workhorse. Write them first. Integration tests verify\nthat components connect correctly. E2E tests verify that the system works for\nthe user — keep them minimal and focused on the critical paths.\n\n## TDD Rules\n\n**One failing test at a time.** Write one test, make it pass, then write the\nnext. Do not write multiple failing tests before implementing.\n\n**The test must fail for the right reason.** A `NullPointerException` on setup\nis not a meaningful failure — that is a broken test. The failure must be the\nassertion, not an error in the test itself.\n\n**Minimum implementation.** In the Green phase, return a hardcoded value if\nthat makes the test pass. The next test will force you to generalize.\n\n**Refactor only on green.** Never refactor when tests are failing. You lose the\nsafety net that tells you whether the refactor changed behavior.\n\n**Tests are not optional after the fact.** Writing tests after implementation\nis not TDD. It is documentation. It catches far fewer design problems.\n\n---\n\n## Spring Boot + Kotlin\n\n### Naming Convention\n\n```kotlin\n@Test\nfun `should [expected behavior] when [condition]`()\n```\n\n```kotlin\n@Test\nfun `should return user when found by id`() { ... }\n\n@Test\nfun `should throw NotFoundException when user does not exist`() { ... }\n\n@Test\nfun `should not create user when email already exists`() { ... }\n```\n\n### TDD Cycle — Unit Test Example\n\n**Requirement**: `UserService.create()` should reject duplicate emails.\n\n**Red** — write the failing test first:\n\n```kotlin\n@ExtendWith(MockKExtension::class)\nclass UserServiceTest {\n\n    @MockK\n    private lateinit var userRepository: UserRepository\n\n    private lateinit var userService: UserService\n\n    @BeforeEach\n    fun setUp() {\n        userService = UserService(userRepository)\n    }\n\n    @Test\n    fun `should throw ConflictException when email already exists`() {\n        \u002F\u002F Arrange\n        val email = \"existing@example.com\"\n        every { userRepository.existsByEmail(email) } returns true\n\n        \u002F\u002F Act & Assert\n        assertThrows\u003CConflictException> {\n            userService.create(email = email, name = \"Test\")\n        }\n        verify(exactly = 0) { userRepository.save(any()) }\n    }\n}\n```\n\nRun → fails (method does not exist yet).\n\n**Green** — minimum implementation:\n\n```kotlin\nclass UserService(private val userRepository: UserRepository) {\n\n    fun create(email: String, name: String): User {\n        if (userRepository.existsByEmail(email)) {\n            throw ConflictException(\"Email $email is already registered\")\n        }\n        return userRepository.save(User(email = email, name = name))\n    }\n}\n```\n\nRun → passes.\n\n**Refactor** — extract the check into a private guard, add KDoc only if the\ndomain rule is non-obvious. Run tests → still green.\n\n**Next test** — happy path:\n\n```kotlin\n@Test\nfun `should create and return user when email is unique`() {\n    val email = \"new@example.com\"\n    val saved = User(id = UUID.randomUUID(), email = email, name = \"New User\")\n\n    every { userRepository.existsByEmail(email) } returns false\n    every { userRepository.save(any()) } returns saved\n\n    val result = userService.create(email = email, name = \"New User\")\n\n    assertThat(result.email).isEqualTo(email)\n    verify(exactly = 1) { userRepository.save(any()) }\n}\n```\n\n### TDD Cycle — Integration Test Example\n\n**Requirement**: `GET \u002Fapi\u002Fusers\u002F{id}` returns 404 when the user does not exist.\n\n**Red**:\n\n```kotlin\n@SpringBootTest\n@AutoConfigureMockMvc\nclass UserControllerTest {\n\n    @Autowired\n    private lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `should return 404 when user does not exist`() {\n        mockMvc.perform(get(\"\u002Fapi\u002Fusers\u002F${UUID.randomUUID()}\"))\n            .andExpect(status().isNotFound)\n            .andExpect(jsonPath(\"$.code\").value(\"NOT_FOUND\"))\n    }\n}\n```\n\nRun → fails (endpoint may not exist yet, or returns wrong status).\n\n**Green** — add\u002Ffix the endpoint and error handler. Run → passes.\n\n**Refactor** — extract error response builder if duplicated across handlers.\n\n### MockK Quick Reference\n\n```kotlin\n\u002F\u002F Stub return value\nevery { repo.findById(id) } returns Optional.of(user)\n\n\u002F\u002F Stub exception\nevery { repo.save(any()) } throws DataIntegrityViolationException(\"duplicate\")\n\n\u002F\u002F Verify call count\nverify(exactly = 1) { repo.save(any()) }\nverify(exactly = 0) { emailService.send(any()) }\n\n\u002F\u002F Capture argument\nval slot = slot\u003CUser>()\nevery { repo.save(capture(slot)) } returns savedUser\nassertThat(slot.captured.email).isEqualTo(\"expected@example.com\")\n\n\u002F\u002F Coroutines\ncoEvery { repo.findById(id) } returns user\ncoVerify(exactly = 1) { repo.findById(id) }\n```\n\n---\n\n## Python (pytest)\n\n### Naming Convention\n\n```python\ndef test_[behavior]_when_[condition]():\n```\n\n```python\ndef test_returns_user_when_found_by_id(): ...\ndef test_raises_not_found_when_user_does_not_exist(): ...\ndef test_does_not_create_user_when_email_already_exists(): ...\n```\n\n### TDD Cycle — Unit Test Example\n\n**Requirement**: `UserService.create()` should reject duplicate emails.\n\n**Red**:\n\n```python\n# tests\u002Fusers\u002Ftest_services.py\nimport pytest\nfrom unittest.mock import MagicMock\nfrom apps.users.services import UserService\n\n\ndef test_raises_when_email_already_exists():\n    repo = MagicMock()\n    repo.exists_by_email.return_value = True\n    service = UserService(repo)\n\n    with pytest.raises(ValueError, match=\"already registered\"):\n        service.create(email=\"taken@example.com\", name=\"Test\")\n\n    repo.save.assert_not_called()\n```\n\nRun → fails (`UserService` does not exist).\n\n**Green**:\n\n```python\n# apps\u002Fusers\u002Fservices.py\nclass UserService:\n\n    def __init__(self, repository):\n        self._repo = repository\n\n    def create(self, *, email: str, name: str):\n        if self._repo.exists_by_email(email):\n            raise ValueError(f\"Email {email!r} is already registered\")\n        return self._repo.save({\"email\": email, \"name\": name})\n```\n\nRun → passes.\n\n### pytest with Django and factory-boy\n\n```python\n# tests\u002Fusers\u002Ffactories.py\nimport factory\nfrom factory.django import DjangoModelFactory\nfrom apps.users.models import User\n\n\nclass UserFactory(DjangoModelFactory):\n    class Meta:\n        model = User\n\n    email = factory.Sequence(lambda n: f\"user{n}@example.com\")\n    name = factory.Faker(\"name\")\n    is_active = True\n```\n\n```python\n# conftest.py\nimport pytest\nfrom pytest_factoryboy import register\nfrom tests.users.factories import UserFactory\n\nregister(UserFactory)\n```\n\n```python\n# tests\u002Fusers\u002Ftest_services.py\nimport pytest\nfrom apps.users.services import UserService\n\n\n@pytest.mark.django_db\ndef test_create_user_succeeds_with_unique_email():\n    service = UserService()\n\n    user = service.create(email=\"new@example.com\", name=\"Alice\")\n\n    assert user.pk is not None\n    assert user.email == \"new@example.com\"\n\n\n@pytest.mark.django_db\ndef test_create_user_raises_when_email_taken(user_factory):\n    user_factory(email=\"taken@example.com\")\n\n    with pytest.raises(ValueError, match=\"already registered\"):\n        UserService().create(email=\"taken@example.com\", name=\"Bob\")\n```\n\n### pytest Fixtures\n\n```python\n# conftest.py\nimport pytest\n\n\n@pytest.fixture\ndef authenticated_client(client, user_factory):\n    user = user_factory()\n    client.force_login(user)\n    return client, user\n\n\n# In test\ndef test_profile_requires_auth(client):\n    response = client.get(\"\u002Fapi\u002Fprofile\u002F\")\n    assert response.status_code == 401\n\n\ndef test_profile_returns_user_data(authenticated_client):\n    client, user = authenticated_client\n    response = client.get(\"\u002Fapi\u002Fprofile\u002F\")\n    assert response.status_code == 200\n    assert response.json()[\"email\"] == user.email\n```\n\n---\n\n## Next.js \u002F Astro (Vitest + RTL + Playwright)\n\n### Naming Convention\n\n```typescript\nit('should [behavior] when [condition]', () => { ... })\ndescribe('ComponentName', () => {\n  describe('when [state]', () => {\n    it('should [behavior]', () => { ... })\n  })\n})\n```\n\n### TDD Cycle — Component Test Example (Vitest + RTL)\n\n**Requirement**: `\u003CCounter>` increments when the button is clicked.\n\n**Red**:\n\n```typescript\n\u002F\u002F components\u002Fcounter.test.tsx\nimport { render, screen, fireEvent } from '@testing-library\u002Freact';\nimport { Counter } from '.\u002Fcounter';\n\ndescribe('Counter', () => {\n  it('should display initial count of 0', () => {\n    render(\u003CCounter \u002F>);\n    expect(screen.getByText('Count: 0')).toBeInTheDocument();\n  });\n\n  it('should increment count when button is clicked', () => {\n    render(\u003CCounter \u002F>);\n    fireEvent.click(screen.getByRole('button', { name: \u002Fincrement\u002Fi }));\n    expect(screen.getByText('Count: 1')).toBeInTheDocument();\n  });\n});\n```\n\nRun → fails (component does not exist).\n\n**Green**:\n\n```tsx\n\u002F\u002F components\u002Fcounter.tsx\n'use client';\nimport { useState } from 'react';\n\nexport function Counter() {\n  const [count, setCount] = useState(0);\n  return (\n    \u003Cdiv>\n      \u003Cp>Count: {count}\u003C\u002Fp>\n      \u003Cbutton onClick={() => setCount(c => c + 1)} aria-label=\"increment\">\n        +\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\nRun → passes.\n\n**Refactor** — extract `useCounter` hook if logic grows. Run tests → green.\n\n### Vitest Setup\n\n```typescript\n\u002F\u002F vitest.config.ts\nimport { defineConfig } from 'vitest\u002Fconfig';\nimport react from '@vitejs\u002Fplugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  test: {\n    environment: 'jsdom',\n    globals: true,\n    setupFiles: ['.\u002Fvitest.setup.ts'],\n  },\n});\n```\n\n```typescript\n\u002F\u002F vitest.setup.ts\nimport '@testing-library\u002Fjest-dom';\n```\n\n### Server Action Testing\n\nTest Server Actions by calling them directly in unit tests — no HTTP overhead.\n\n```typescript\n\u002F\u002F app\u002Fposts\u002Fcreate\u002Factions.test.ts\nimport { createPost } from '.\u002Factions';\n\nvi.mock('@\u002Flib\u002Fdb', () => ({\n  db: {\n    post: {\n      create: vi.fn(),\n    },\n  },\n}));\n\ndescribe('createPost', () => {\n  it('should return validation error when title is empty', async () => {\n    const formData = new FormData();\n    formData.set('title', '');\n    formData.set('content', 'Some content');\n\n    const result = await createPost(formData);\n\n    expect(result?.error?.title).toBeDefined();\n  });\n});\n```\n\n### E2E Testing with Playwright\n\nReserve Playwright for critical user flows only. Do not replicate unit test\nscenarios in E2E.\n\n```typescript\n\u002F\u002F e2e\u002Fauth.spec.ts\nimport { test, expect } from '@playwright\u002Ftest';\n\ntest.describe('Authentication', () => {\n  test('should redirect to login when accessing protected route unauthenticated', async ({ page }) => {\n    await page.goto('\u002Fdashboard');\n    await expect(page).toHaveURL('\u002Flogin');\n  });\n\n  test('should show dashboard after successful login', async ({ page }) => {\n    await page.goto('\u002Flogin');\n    await page.getByLabel('Email').fill('user@example.com');\n    await page.getByLabel('Password').fill('password123');\n    await page.getByRole('button', { name: 'Sign in' }).click();\n    await expect(page).toHaveURL('\u002Fdashboard');\n  });\n});\n```\n\n```typescript\n\u002F\u002F playwright.config.ts\nimport { defineConfig } from '@playwright\u002Ftest';\n\nexport default defineConfig({\n  testDir: '.\u002Fe2e',\n  use: {\n    baseURL: 'http:\u002F\u002Flocalhost:3000',\n  },\n  webServer: {\n    command: 'npm run build && npm run start',\n    port: 3000,\n    reuseExistingServer: !process.env.CI,\n  },\n});\n```\n\n---\n\n## CI Integration\n\nRun tests in this order — fail fast on the cheapest failures first:\n\n```text\n1. Unit tests          — sub-second feedback; block all subsequent steps on failure\n2. Integration tests   — real DB, real HTTP; slower but necessary\n3. E2E tests           — slowest; run only on main branch or release branches\n```\n\n```yaml\n# Example GitHub Actions\njobs:\n  test:\n    steps:\n      - name: Unit tests\n        run: npx vitest run --reporter=verbose\n      - name: Integration tests\n        run: npx vitest run --project=integration\n      - name: E2E tests\n        if: github.ref == 'refs\u002Fheads\u002Fmain'\n        run: npx playwright test\n```\n\n**Pipeline rules:**\n\n- A failing unit test blocks the entire pipeline — do not merge broken tests\n- E2E tests are expensive; run them on CI but not in pre-commit hooks\n- Flaky tests must be fixed or deleted — a test that sometimes passes is worse\n  than no test, because it erodes trust in the suite\n",{"id":24,"name":5,"risk":25,"inputs":28,"license":15,"outputs":29,"quality":30,"version":33,"metadata":34,"description":36,"compatibility":37,"user-invokable":27},"testing-tdd",{"level":26,"can_modify_files":27,"requires_network":20,"can_execute_shell":20},"low",true,[],[],{"version":31,"reviewed_by":32},"0.1.0","codeconductor-core","1.0.0",{"author":9,"category":35},"testing","Provides expert knowledge for applying the Red-Green-Refactor cycle, designing tests at the right pyramid layer, and implementing TDD across Spring Boot + Kotlin, Python (pytest), and Next.js \u002F Astro (Vitest + Playwright) stacks.\n",{"tools":38,"stacks":44},[39,40,41,42,43],"claude","codex","gemini","agy","opencode",{"languages":45,"frameworks":46},[],[],"main","ef000917622cb4063da0d721445f2caf2cfb21a4",".cursor\u002Fskills\u002Ftesting-tdd","https:\u002F\u002Fgithub.com\u002Flgzarturo\u002Ffeedback_widget","https:\u002F\u002Fgithub.com\u002Flgzarturo","https:\u002F\u002Fwww.lgzarturo.com","Building products, not just software.\r\n\r\nTechnical Leadership • Software Architecture • AI","2026-10-24 07:43:49",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":27},[57,60,63],{"tool":58,"command":59},"npx","npx skills add lgzarturo\u002Ffeedback_widget --skill \"Test-Driven Development\"",{"tool":61,"command":62},"bunx","bunx skills add lgzarturo\u002Ffeedback_widget --skill \"Test-Driven Development\"",{"tool":64,"command":65},"pnpm","pnpm dlx skills add lgzarturo\u002Ffeedback_widget --skill \"Test-Driven Development\"","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Flgzarturo\u002Ffeedback_widget\u002Fblob\u002Fmain\u002F.cursor\u002Fskills\u002Ftesting-tdd\u002FSKILL.md",[],[69],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":19},"01M7ACKKAS1WFP2BN5Z7JDZXPS","Security","Provides expert knowledge for implementing and reviewing application security following OWASP Top 10 (2021), with stack-specific patterns for Spring Boot, Django, Next.js, and Astro.","https:\u002F\u002Fgithub.com\u002Flgzarturo\u002Ffeedback_widget\u002Fblob\u002Fmain\u002F.cursor\u002Fskills\u002Fsecurity\u002FSKILL.md",[],[]]