Skills Plugins MCP Prompt Model 博客 我的中心

quarkus-tdd

JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。

DeepseekModel Curated skill Quality Excellent · 90 v1.0.0

Get

https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-ja-jp-skills-quarkus-tdd-skill-md&format=skill
Download .skill Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name quarkus-tdd description JUnit 5、Mockito、REST Assured、Camelテスト、JaCoCoを使用したQuarkus 3.xのテスト駆動開発。機能追加、バグ修正、またはイベント駆動サービスのリファクタリング時に使用。 origin ECC Quarkus TDD Workflow 80%以上のカバレッジ(ユニット+統合)を備えたQuarkus 3.xサービスのTDD指導。Apache Camelを使用したイベント駆動アーキテクチャに最適化。 When to Use 新機能またはRESTエンドポイント バグ修正またはリファクタリング データアクセスロジック、セキュリティルール、またはリアクティブストリーム追加 Apache Camelルートとイベントハンドラーテスト RabbitMQを使用したイベント駆動サービステスト 条件フローロジック検証 CompletableFuture非同期操作検証 LogContextプロパゲーション テスト Workflow テストを先に書く(失敗するはず) 最小限のコードで合格実装 テストが緑の状態でリファクタリング JaCoCoでカバレッジ実装(80%以上を目標) Unit Tests with @Nested Organization 包括的で読みやすいテストのため、以下の構造化されたアプローチに従います: @ExtendWith(MockitoExtension.class) @DisplayName("OrderService Unit Tests") class OrderServiceTest { @Mock private OrderRepository orderRepository; @Mock private EventService eventService; @Mock private FulfillmentPublisher fulfillmentPublisher; @InjectMocks private OrderService orderService; private CreateOrderCommand validCommand; @BeforeEach void setUp () { validCommand = new CreateOrderCommand ( "customer-123" , List.of( new OrderLine ( "sku-123" , 2 )) ); } @Nested @DisplayName("createOrder のテスト") class CreateOrder { @Test @DisplayName("有効なコマンドが与えられた場合、注文を永続化してフルフィルメントイベントを発行する") void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes () { // ARRANGE doNothing(). when (orderRepository).persist(any(Order.class)); // ACT OrderReceipt receipt = orderService.createOrder(validCommand); // ASSERT assertThat(receipt).isNotNull(); assertThat(receipt.customerId()).isEqualTo( "customer-123" ); verify(orderRepository).persist(any(Order.class)); verify(fulfillmentPublisher).publishAsync(receipt); verify(eventService).createSuccessEvent(receipt, "ORDER_CREATED" ); } @Test @DisplayName("顧客IDが無い場合、BadRequestをスロー") void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest () { // ARRANGE CreateOrderCommand invalid = new CreateOrderCommand ( "" , validCommand.lines()); // ACT & ASSERT WebApplicationException exception = assertThrows( WebApplicationException.class, () -> orderService.createOrder(invalid) ); assertThat(exception.getResponse().getStatus()).isEqualTo( 400 ); verify(orderRepository, never()).persist(any(Order.class)); verify(fulfillmentPublisher, never()).publishAsync(any()); } @Test @DisplayName("永続化失敗時、エラーイベントを記録") void givenPersistenceFailure_whenCreateOrder_thenRecordsErrorEvent () { // ARRANGE doThrow( new PersistenceException ( "database unavailable" )) . when (orderRepository).persist(any(Order.class)); // ACT & ASSERT PersistenceException exception = assertThrows( PersistenceException.class, () -> orderService.createOrder(validCommand) ); assertThat(exception.getMessage()).contains( "database unavailable" ); verify(eventService).createErrorEvent( eq(validCommand), eq( "ORDER_CREATE_FAILED" ), contains( "database unavailable" ) ); verify(fulfillmentPublisher, never()).publishAsync(any()); } @Test @DisplayName("nullコマンドが与えられた場合、NullPointerExceptionをスロー") void givenNullCommand_whenCreateOrder_thenThrowsNullPointerException () { // ACT & ASSERT assertThrows( NullPointerException.class, () -> orderService.createOrder( null ) ); verify(orderRepository, never()).persist(any(Order.class)); } } } Key Testing Patterns @Nested クラス : テストするメソッド別にテストをグループ化 @DisplayName : テストレポート用の読みやすい説明提供 命名規則 : 明確性のため givenX_whenY_thenZ AAA パターン : 明示的な // ARRANGE , // ACT , // ASSERT コメント @BeforeEach : 重複削減のためテストデータを共通設定 assertDoesNotThrow : 例外をキャッチせずに成功シナリオをテスト assertThrows : AssertJを使用したメッセージ検証で例外シナリオをテスト 包括的カバレッジ : 正常系、null入力、エッジケース、例外をテスト 相互作用検証 : Mockito verify() でメソッド呼び出しが正しく行われたか確認 Never検証 : never() でエラーシナリオでメソッドが呼ばれていないことを確認 Testing Camel Routes @QuarkusTest @DisplayName("Business Rules Camel Route Tests") class BusinessRulesRouteTest { @Inject CamelContext camelContext; @Inject ProducerTemplate producerTemplate; @InjectMock EventService eventService; @InjectMock DocumentValidator documentValidator; private BusinessRulesPayload testPayload; @BeforeEach void setUp () { // ARRANGE - テストデータ testPayload = new BusinessRulesPayload (); testPayload.setDocumentId( 1L ); testPayload.setFlowProfile(FlowProfile.BASIC); } @Nested @DisplayName("business-rules-publisher ルートのテスト") class BusinessRulesPublisher { @Test @DisplayName("有効なペイロードが与えられた場合、メッセージをRabbitMQに送信") void givenValidPayload_whenPublish_thenMessageSentToQueue () throws Exception { // ARRANGE MockEndpoint mockRabbitMQ = camelContext.getEndpoint( "mock:rabbitmq" , MockEndpoint.class); mockRabbitMQ.expectedMessageCount( 1 ); // テスト用の実エンドポイントをモックに置き換え camelContext.getRouteController().stopRoute( "business-rules-publisher" ); AdviceWith.adviceWith(camelContext, "business-rules-publisher" , advice -> { advice.replaceFromWith( "direct:business-rules-publisher" ); advice.weaveByToString( ".*spring-rabbitmq.*" ).replace().to( "mock:rabbitmq" ); }); camelContext.getRouteController().startRoute( "business-rules-publisher" ); // ACT producerTemplate.sendBody( "direct:business-rules-publisher" , testPayload); // ASSERT — .marshal().json(JsonLibrary.Jackson)の後、bodyはJSON文字列 mockRabbitMQ.assertIsSatisfied( 5000 ); assertThat(mockRabbitMQ.getExchanges()).hasSize( 1 ); String body = mockRabbitMQ.getExchanges().get( 0 ).getIn().getBody(String.class); assertThat(body).contains( "\"documentId\":1" ); } @Test @DisplayName("ペイロード与えられた場合、JSONに整形") void givenPayload_whenPublish_thenMarshalledToJson () throws Exception { // ARRANGE MockEndpoint mockMarshal = new MockEndpoint ( "mock:marshal" ); camelContext.addEndpoint( "mock:marshal" , mockMarshal); mockMarshal.expectedMessageCount( 1 ); camelContext.getRouteController().stopRoute( "business-rules-publisher" ); AdviceWith.adviceWith(camelContext, "business-rules-publisher" , advice -> { advice.weaveAddLast().to( "mock:marshal" ); }); camelContext.getRouteController().startRoute( "business-rules-publisher" ); // ACT producerTemplate.sendBody( "direct:business-rules-publisher" , testPayload); // ASSERT mockMarshal.assertIsSatisfied( 5000 ); String body = mockMarshal.getExchanges().get( 0 ).getIn().getBody(String.class); assertThat(body).contains( "\"documentId\":1" ); assertThat(body).contains( "\"flowProfile\":\"BASIC\"" ); } } @Nested @DisplayName("document-processing ルートのテスト") class DocumentProcessing { @Test @DisplayName("請求書タイプが与えられた場合、正しいプロセッサーにルーティング") void givenInvoiceType_whenProcess_thenRoutesToInvoiceProcessor () throws Exception { // ARRANGE MockEndpoint mockInvoice = camelContext.getEndpoint( "mock:invoice" , MockEndpoint.class); mockInvoice.expectedMessageCount( 1 ); camelContext.getRouteController().stopRoute( "document-processing" ); AdviceWith.adviceWith(camelContext, "document-processing" , advice -> { advice.weaveByToString( ".*direct:process-invoice.*" ).replace().to( "mock:invoice" ); }); camelContext.getRouteController().startRoute( "document-processing" ); // ACT producerTemplate.sendBodyAndHeader( "direct:process-document" , testPayload, "documentType" , "INVOICE" ); // ASSERT mockInvoice.assertIsSatisfied( 5000 ); } @Test @DisplayName("検証エラーが与えられた場合、エラーハンドラーにルーティング") void givenValidationError_whenProcess_thenRoutesToErrorHandler () throws Exception { // ARRANGE MockEndpoint mockError = camelContext.getEndpoint( "mock:error" , MockEndpoint.class); mockError.expectedMessageCount( 1 ); camelContext.getRouteController().stopRoute( "document-processing" ); AdviceWith.adviceWith(camelContext, "document-processing" , advice -> { advice.weaveByToString( ".*direct:validation-error-handler.*" ) .replace().to( "mock:error" ); }); camelContext.getRouteController().startRoute( "document-processing" ); // バリデータビーンをモック化して例外をスロー when (documentValidator.validate(any())).thenThrow( new ValidationException ( "Invalid document" )); // ACT producerTemplate.sendBody( "direct:process-document" , testPayload); // ASSERT mockError.assertIsSatisfied( 5000 ); Exception exception = mockError.getExchanges().get( 0 ).getException(); assertThat(exception).isInstanceOf(ValidationException.class); assertThat(exception.getMessage()).contains( "Invalid document" ); } } } Testing Event Services @ExtendWith(MockitoExtension.class) @DisplayName("EventService Unit Tests") class EventServiceTest { @Mock private EventRepository eventRepository; @Mock private ObjectMapper objectMapper; @InjectMocks private EventService eventService; private BusinessRulesPayload testPayload; @BeforeEach void setUp () { // ARRANGE testPayload = new BusinessRulesPayload (); testPayload.setDocumentId( 1L ); } @Nested @DisplayName("createSuccessEvent のテスト") class CreateSuccessEvent { @Test @DisplayName("有効なペイロードが与えられた場合、正しい属性でサクセスイベント作成") void givenValidPayload_whenCreateSuccessEvent_thenEventPersisted () throws Exception { // ARRANGE when (objectMapper.writeValueAsString(testPayload)).thenReturn( "{\"documentId\":1}" ); // ACT assertDoesNotThrow(() -> eventService.createSuccessEvent(testPayload, "DOCUMENT_PROCESSED" )); // ASSERT verify(eventRepository).persist(argThat(event -> event.getType().equals( "DOCUMENT_PROCESSED" ) && event.getStatus() == EventStatus.SUCCESS && event.getPayload().equals( "{\"documentId\":1}" ) && event.getTimestamp() != null )); } @Test @DisplayName("nullペイロードが与えられた場合、例外をスロー") void givenNullPayload_whenCreateSuccessEvent_thenThrowsException () { // ARRANGE Object nullPayload = null ; // ACT & ASSERT NullPointerException exception = assertThrows( NullPointerException.class, () -> eventService.createSuccessEvent(nullPayload, "EVENT_TYPE" ) ); assertThat(exception.getMessage()).isEqualTo( "Payload cannot be null" ); verify(eventRepository, never()).persist(any()); } } @Nested @DisplayName("createErrorEvent のテスト") class CreateErrorEvent { @Test @DisplayName("エラーが与えられた場合、エラーメッセージ付きエラーイベント作成") void givenError_whenCreateErrorEvent_thenEventPersistedWithMessage () throws Exception { // ARRANGE String errorMessage = "Processing failed" ; when (objectMapper.writeValueAsString(testPayload)).thenReturn( "{\"documentId\":1}" ); // ACT assertDoesNotThrow(() -> eventService.createErrorEvent(testPayload, "PROCESSING_ERROR" , errorMessage)); // ASSERT verify(eventRepository).persist(argThat(event -> event.getType().equals( "PROCESSING_ERROR" ) && event.getStatus() == EventStatus.ERROR && event.getErrorMessage().equals(errorMessage) && event.getPayload().equals( "{\"documentId\":1}" ) )); } @ParameterizedTest @DisplayName("不正なエラーメッセージが与えられた場合、例外をスロー") @ValueSource(strings = {"", " "}) void givenBlankErrorMessage_whenCreateErrorEvent_thenThrowsException (String blankMessage) { // ACT & ASSERT IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, () -> eventService.createErrorEvent(testPayload, "ERROR" , blankMessage) ); assertThat(exception.getMessage()).contains( "Error message cannot be blank" ); } } } Testing CompletableFuture @ExtendWith(MockitoExtension.class) @DisplayName("FileStorageService Unit Tests") class FileStorageServiceTest { @Mock private S3Client s3Client; @Mock private ExecutorService executorService; @InjectMocks private FileStorageService fileStorageService; private InputStream testInputStream; private LogContext testLogContext; @BeforeEach void setUp () { // ARRANGE testInputStream = new ByteArrayInputStream ( "test content" .getBytes()); testLogContext = new LogContext (); testLogContext.put( "traceId" , "trace-123" ); } @Nested @DisplayName("uploadOriginalFile のテスト") class UploadOriginalFile { @Test @DisplayName("有効なファイルが与えられた場合、ファイルアップロード成功とドキュメント情報を返す") void givenValidFile_whenUpload_thenReturnsDocumentInfo () throws Exception { // ARRANGE doAnswer(invocation -> { ((Runnable) invocation.getArgument( 0 )).run(); return null ; }). when (executorService).execute(any(Runnable.class)); when (s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class))) .thenReturn(PutObjectResponse.builder().build()); // ACT
Keywords that activate this skill. Click one to copy it.

This skill does not provide trigger words.

The downloaded .skill package contains the following fields.
Field Description
formatFormat tag (skill/v1)
skill_idUnique skill ID
nameSkill name
versionVersion
descriptionDescription
categoryCategories (array)
trigger_wordsTrigger words
tagsTags
sourceSource
source_urlSource URL (this page)
exported_atExported at (set per download)
system_promptSystem prompt body
model_configModel config: provider / model / temperature / max_tokens / top_p
examplesExamples
install_guideImport guide for Coze / Dify / Claude / custom frameworks
The same skill can be exported in different platform formats.
.skill Standard format with system_prompt and model_config, ready for any agent framework Download
.skillpro Enhanced format with scripts, tools, dependencies and hooks Download
.json Plain JSON export with system_prompt and model parameters only Download
Coze Markdown with frontmatter, for Coze platform import Download
Dify Dify DSL, import directly after creating an app Download

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。