[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E3CZKYE94Q57PCPCEN844F":3,"skill-same:01M6E3CZKYE94Q57PCPCEN844F":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":38,"commitSha":39,"skillDirectory":40,"forksCount":13,"repositoryHtmlUrl":41,"repositoryHomepageUrl":42,"ownerHtmlUrl":43,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":44,"install":45,"resources":58},"01M6E3CZKYE94Q57PCPCEN844F","springboot-tdd",null,"Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.","7dvq6yrkgs-stack\u002Fplaywright","7dvq6yrkgs-stack","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F251638143?v=4","01M6E061J2AMGWYZ6TK7Y8NM74","01M6E061PQCQJGQMPBERMCFN5G",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fspringboot-tdd\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32],"tdd","springboot tdd","test","boot","development","spring boot","refactoring","spring","use","driven","2026-11-10 10:12:30",false,"---\nname: springboot-tdd\ndescription: Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.\n---\n\n# Spring Boot TDD ワークフロー\n\n80%以上のカバレッジ（ユニット+統合）を持つSpring Bootサービスのためのテスト駆動開発ガイダンス。\n\n## いつ使用するか\n\n- 新機能やエンドポイント\n- バグ修正やリファクタリング\n- データアクセスロジックやセキュリティルールの追加\n\n## ワークフロー\n\n1) テストを最初に書く（失敗すべき）\n2) テストを通すための最小限のコードを実装\n3) テストをグリーンに保ちながらリファクタリング\n4) カバレッジを強制（JaCoCo）\n\n## ユニットテスト（JUnit 5 + Mockito）\n\n```java\n@ExtendWith(MockitoExtension.class)\nclass MarketServiceTest {\n  @Mock MarketRepository repo;\n  @InjectMocks MarketService service;\n\n  @Test\n  void createsMarket() {\n    CreateMarketRequest req = new CreateMarketRequest(\"name\", \"desc\", Instant.now(), List.of(\"cat\"));\n    when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));\n\n    Market result = service.create(req);\n\n    assertThat(result.name()).isEqualTo(\"name\");\n    verify(repo).save(any());\n  }\n}\n```\n\nパターン:\n- Arrange-Act-Assert\n- 部分モックを避ける。明示的なスタビングを優先\n- バリエーションに`@ParameterizedTest`を使用\n\n## Webレイヤーテスト（MockMvc）\n\n```java\n@WebMvcTest(MarketController.class)\nclass MarketControllerTest {\n  @Autowired MockMvc mockMvc;\n  @MockBean MarketService marketService;\n\n  @Test\n  void returnsMarkets() throws Exception {\n    when(marketService.list(any())).thenReturn(Page.empty());\n\n    mockMvc.perform(get(\"\u002Fapi\u002Fmarkets\"))\n        .andExpect(status().isOk())\n        .andExpect(jsonPath(\"$.content\").isArray());\n  }\n}\n```\n\n## 統合テスト（SpringBootTest）\n\n```java\n@SpringBootTest\n@AutoConfigureMockMvc\n@ActiveProfiles(\"test\")\nclass MarketIntegrationTest {\n  @Autowired MockMvc mockMvc;\n\n  @Test\n  void createsMarket() throws Exception {\n    mockMvc.perform(post(\"\u002Fapi\u002Fmarkets\")\n        .contentType(MediaType.APPLICATION_JSON)\n        .content(\"\"\"\n          {\"name\":\"Test\",\"description\":\"Desc\",\"endDate\":\"2030-01-01T00:00:00Z\",\"categories\":[\"general\"]}\n        \"\"\"))\n      .andExpect(status().isCreated());\n  }\n}\n```\n\n## 永続化テスト（DataJpaTest）\n\n```java\n@DataJpaTest\n@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)\n@Import(TestContainersConfig.class)\nclass MarketRepositoryTest {\n  @Autowired MarketRepository repo;\n\n  @Test\n  void savesAndFinds() {\n    MarketEntity entity = new MarketEntity();\n    entity.setName(\"Test\");\n    repo.save(entity);\n\n    Optional\u003CMarketEntity> found = repo.findByName(\"Test\");\n    assertThat(found).isPresent();\n  }\n}\n```\n\n## Testcontainers\n\n- 本番環境を反映するためにPostgres\u002FRedis用の再利用可能なコンテナを使用\n- `@DynamicPropertySource`経由でJDBC URLをSpringコンテキストに注入\n\n## カバレッジ（JaCoCo）\n\nMavenスニペット:\n```xml\n\u003Cplugin>\n  \u003CgroupId>org.jacoco\u003C\u002FgroupId>\n  \u003CartifactId>jacoco-maven-plugin\u003C\u002FartifactId>\n  \u003Cversion>0.8.14\u003C\u002Fversion>\n  \u003Cexecutions>\n    \u003Cexecution>\n      \u003Cgoals>\u003Cgoal>prepare-agent\u003C\u002Fgoal>\u003C\u002Fgoals>\n    \u003C\u002Fexecution>\n    \u003Cexecution>\n      \u003Cid>report\u003C\u002Fid>\n      \u003Cphase>verify\u003C\u002Fphase>\n      \u003Cgoals>\u003Cgoal>report\u003C\u002Fgoal>\u003C\u002Fgoals>\n    \u003C\u002Fexecution>\n  \u003C\u002Fexecutions>\n\u003C\u002Fplugin>\n```\n\n## アサーション\n\n- 可読性のためにAssertJ（`assertThat`）を優先\n- JSONレスポンスには`jsonPath`を使用\n- 例外には: `assertThatThrownBy(...)`\n\n## テストデータビルダー\n\n```java\nclass MarketBuilder {\n  private String name = \"Test\";\n  MarketBuilder withName(String name) { this.name = name; return this; }\n  Market build() { return new Market(null, name, MarketStatus.ACTIVE); }\n}\n```\n\n## CIコマンド\n\n- Maven: `mvn -T 4 test` または `mvn verify`\n- Gradle: `.\u002Fgradlew test jacocoTestReport`\n\n**覚えておいてください**: テストは高速で、分離され、決定論的に保ちます。実装の詳細ではなく、動作をテストします。\n","# Spring Boot TDD ワークフロー\n\n80%以上のカバレッジ（ユニット+統合）を持つSpring Bootサービスのためのテスト駆動開発ガイダンス。\n\n## いつ使用するか\n\n- 新機能やエンドポイント\n- バグ修正やリファクタリング\n- データアクセスロジックやセキュリティルールの追加\n\n## ワークフロー\n\n1) テストを最初に書く（失敗すべき）\n2) テストを通すための最小限のコードを実装\n3) テストをグリーンに保ちながらリファクタリング\n4) カバレッジを強制（JaCoCo）\n\n## ユニットテスト（JUnit 5 + Mockito）\n\n```java\n@ExtendWith(MockitoExtension.class)\nclass MarketServiceTest {\n  @Mock MarketRepository repo;\n  @InjectMocks MarketService service;\n\n  @Test\n  void createsMarket() {\n    CreateMarketRequest req = new CreateMarketRequest(\"name\", \"desc\", Instant.now(), List.of(\"cat\"));\n    when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));\n\n    Market result = service.create(req);\n\n    assertThat(result.name()).isEqualTo(\"name\");\n    verify(repo).save(any());\n  }\n}\n```\n\nパターン:\n- Arrange-Act-Assert\n- 部分モックを避ける。明示的なスタビングを優先\n- バリエーションに`@ParameterizedTest`を使用\n\n## Webレイヤーテスト（MockMvc）\n\n```java\n@WebMvcTest(MarketController.class)\nclass MarketControllerTest {\n  @Autowired MockMvc mockMvc;\n  @MockBean MarketService marketService;\n\n  @Test\n  void returnsMarkets() throws Exception {\n    when(marketService.list(any())).thenReturn(Page.empty());\n\n    mockMvc.perform(get(\"\u002Fapi\u002Fmarkets\"))\n        .andExpect(status().isOk())\n        .andExpect(jsonPath(\"$.content\").isArray());\n  }\n}\n```\n\n## 統合テスト（SpringBootTest）\n\n```java\n@SpringBootTest\n@AutoConfigureMockMvc\n@ActiveProfiles(\"test\")\nclass MarketIntegrationTest {\n  @Autowired MockMvc mockMvc;\n\n  @Test\n  void createsMarket() throws Exception {\n    mockMvc.perform(post(\"\u002Fapi\u002Fmarkets\")\n        .contentType(MediaType.APPLICATION_JSON)\n        .content(\"\"\"\n          {\"name\":\"Test\",\"description\":\"Desc\",\"endDate\":\"2030-01-01T00:00:00Z\",\"categories\":[\"general\"]}\n        \"\"\"))\n      .andExpect(status().isCreated());\n  }\n}\n```\n\n## 永続化テスト（DataJpaTest）\n\n```java\n@DataJpaTest\n@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)\n@Import(TestContainersConfig.class)\nclass MarketRepositoryTest {\n  @Autowired MarketRepository repo;\n\n  @Test\n  void savesAndFinds() {\n    MarketEntity entity = new MarketEntity();\n    entity.setName(\"Test\");\n    repo.save(entity);\n\n    Optional\u003CMarketEntity> found = repo.findByName(\"Test\");\n    assertThat(found).isPresent();\n  }\n}\n```\n\n## Testcontainers\n\n- 本番環境を反映するためにPostgres\u002FRedis用の再利用可能なコンテナを使用\n- `@DynamicPropertySource`経由でJDBC URLをSpringコンテキストに注入\n\n## カバレッジ（JaCoCo）\n\nMavenスニペット:\n```xml\n\u003Cplugin>\n  \u003CgroupId>org.jacoco\u003C\u002FgroupId>\n  \u003CartifactId>jacoco-maven-plugin\u003C\u002FartifactId>\n  \u003Cversion>0.8.14\u003C\u002Fversion>\n  \u003Cexecutions>\n    \u003Cexecution>\n      \u003Cgoals>\u003Cgoal>prepare-agent\u003C\u002Fgoal>\u003C\u002Fgoals>\n    \u003C\u002Fexecution>\n    \u003Cexecution>\n      \u003Cid>report\u003C\u002Fid>\n      \u003Cphase>verify\u003C\u002Fphase>\n      \u003Cgoals>\u003Cgoal>report\u003C\u002Fgoal>\u003C\u002Fgoals>\n    \u003C\u002Fexecution>\n  \u003C\u002Fexecutions>\n\u003C\u002Fplugin>\n```\n\n## アサーション\n\n- 可読性のためにAssertJ（`assertThat`）を優先\n- JSONレスポンスには`jsonPath`を使用\n- 例外には: `assertThatThrownBy(...)`\n\n## テストデータビルダー\n\n```java\nclass MarketBuilder {\n  private String name = \"Test\";\n  MarketBuilder withName(String name) { this.name = name; return this; }\n  Market build() { return new Market(null, name, MarketStatus.ACTIVE); }\n}\n```\n\n## CIコマンド\n\n- Maven: `mvn -T 4 test` または `mvn verify`\n- Gradle: `.\u002Fgradlew test jacocoTestReport`\n\n**覚えておいてください**: テストは高速で、分離され、決定論的に保ちます。実装の詳細ではなく、動作をテストします。\n",{"name":5,"description":7},"main","a2e3284ed4488780397ee2f78217366e11470dfb","skills\u002Fspringboot-tdd","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright","https:\u002F\u002Fplaywright.dev","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack","2026-04-25 06:04:45",{"source":8,"skillName":5,"commands":46,"prompt":56,"zipAvailable":57},[47,50,53],{"tool":48,"command":49},"npx","npx skills add 7dvq6yrkgs-stack\u002Fplaywright --skill springboot-tdd",{"tool":51,"command":52},"bunx","bunx skills add 7dvq6yrkgs-stack\u002Fplaywright --skill springboot-tdd",{"tool":54,"command":55},"pnpm","pnpm dlx skills add 7dvq6yrkgs-stack\u002Fplaywright --skill springboot-tdd","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fspringboot-tdd\u002FSKILL.md",true,[59],{"path":60,"resourceType":61,"resourceTypeDesc":62,"sizeBytes":63,"sourceHtmlUrl":64},".skillfish.json","REFERENCE","参考文档",246,"https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fspringboot-tdd\u002F.skillfish.json",[66,96,106,116,131,146],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":84,"publishedAt":33},"01M6E3XK4PRPMQHM7Y98FQPTY1","zyte-api-automation","Automate Zyte API tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzyte-api-automation\u002FSKILL.md",[72,73,76,79],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":74,"categoryName":75,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[85,86,87,88,89,90,91,92,93,94,95],"zyte api automation","api","automation","MCP","tasks","search","Tools","first","automate","composio","via",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":104,"publishedAt":33},"01M6E3XFX6QSTR1QR59NFXJTMA","zylvie-automation","Automate Zylvie tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzylvie-automation\u002FSKILL.md",[102,103],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},[105,87,93,88,89,90,91,92,94,95],"zylvie automation",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":114,"publishedAt":33},"01M6E3XCX2GDYYCHN58ZJHT37M","zoominfo-automation","Automate Zoominfo tasks via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzoominfo-automation\u002FSKILL.md",[112,113],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},[115,87,90,91,92,93,94,95,89,88],"zoominfo automation",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":124,"publishedAt":33},"01M6E3X9WJ2T73216WQHHTB9BD","zoho_mail-automation","Automate Zoho Mail tasks via Rube MCP (Composio): email sending, folders, labels, and mailbox management. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzoho_mail-automation\u002FSKILL.md",[122,123],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},[125,126,127,87,88,89,90,128,91,92,93,129,94,130,95],"mail","zoho mail automation","zoho","email","management","mailbox",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":142,"publishedAt":33},"01M6E3X719W648XZBW1EC6JQ64","zoho_invoice-automation","Automate Zoho Invoice tasks via Rube MCP (Composio): invoices, estimates, expenses, clients, and payment tracking. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzoho_invoice-automation\u002FSKILL.md",[137,138,139],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":140,"categoryName":141,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000040","财务",[143,127,87,89,90,91,92,93,144,145,94,95,88],"zoho invoice automation","tracking","payment",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":154,"publishedAt":33},"01M6E3X28JYTC3QTK32AQF7BYB","zoho_inventory-automation","Automate Zoho Inventory tasks via Rube MCP (Composio): items, orders, warehouses, shipments, and stock management. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002F7dvq6yrkgs-stack\u002Fplaywright\u002Fblob\u002Fmain\u002Fskills\u002Fzoho_inventory-automation\u002FSKILL.md",[152,153],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},[87,155,127,156,92,93,91,90,89,88,94,95,157,158,129],"inventory","zoho inventory automation","stock","orders"]