-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeed.xml
More file actions
1494 lines (1129 loc) · 178 KB
/
Copy pathfeed.xml
File metadata and controls
1494 lines (1129 loc) · 178 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.3">Jekyll</generator><link href="http://0.0.0.0:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://0.0.0.0:4000/" rel="alternate" type="text/html" /><updated>2026-06-19T15:12:38+02:00</updated><id>http://0.0.0.0:4000/feed.xml</id><title type="html">Citrus Integration Tests</title><subtitle>Automated integration tests for message protocols and data formats that you require</subtitle><entry><title type="html">Design Citrus Tests Visually — Kaoto Meets Citrus</title><link href="http://0.0.0.0:4000/news/2026/06/19/kaoto-meets-citrus/" rel="alternate" type="text/html" title="Design Citrus Tests Visually — Kaoto Meets Citrus" /><published>2026-06-19T00:00:00+02:00</published><updated>2026-06-19T00:00:00+02:00</updated><id>http://0.0.0.0:4000/news/2026/06/19/kaoto-meets-citrus</id><content type="html" xml:base="http://0.0.0.0:4000/news/2026/06/19/kaoto-meets-citrus/"><p>What if you could design Citrus integration tests by dragging and dropping components on a visual canvas — no boilerplate, no guesswork, no steep learning curve?
With <a href="https://kaoto.io/blog/2026/kaoto-2.11-release/">Kaoto 2.11</a> this is now a reality for Citrus.</p>
<p><img src="/img/assets/kaoto-citrus-integration/kaoto-logo.png" alt="Kaoto Logo" /></p>
<p><img src="/img/assets/kaoto-citrus-integration/kaoto-teaser.png" alt="Kaoto meets Citrus" /></p>
<p>Kaoto is an open source visual designer for <a href="https://camel.apache.org/">Apache Camel</a> integrations, and starting with version 2.11 it ships with comprehensive Citrus framework support.
This means you can write and execute Citrus tests in a fully visual, low-code environment — right inside VS Code.</p>
<h2 id="why-this-matters">Why this matters</h2>
<p>Citrus is powerful, but getting started has always required some familiarity with the framework’s Java or YAML DSL, its endpoint model, and its rich library of test actions.
That learning curve is exactly what Kaoto eliminates.</p>
<p>Kaoto is aware of the complete Citrus component catalog: every test action, every endpoint type, every function and validation matcher.
It uses the individual property schemas of each component to provide guided forms, auto-completion, and contextual help.
You pick what you need, fill in the blanks, and Kaoto takes care of the rest.</p>
<h2 id="visual-test-design-in-action">Visual test design in action</h2>
<p>Once you open a Citrus test file in Kaoto, each test action appears as a visual step on the canvas with dedicated icons that make it easy to distinguish between different actions at a glance.</p>
<p><img src="/img/assets/kaoto-citrus-integration/test-icons.png" alt="Visual test flow with dedicated Citrus icons" /></p>
<p>You build your test scenario step by step — start infrastructure (e.g. <code class="highlighter-rouge">kafka</code> container), add a <code class="highlighter-rouge">send</code> action, configure a <code class="highlighter-rouge">receive</code> with expected message content, throw in an <code class="highlighter-rouge">echo</code> or <code class="highlighter-rouge">sleep</code> — all by selecting from the component catalog and filling in property forms.</p>
<h3 id="full-endpoint-catalog">Full endpoint catalog</h3>
<p>Kaoto knows about all 35+ Citrus test endpoints out of the box: HTTP clients and servers, Kafka, JMS, FTP, SOAP, Mail, SSH, Kubernetes and many more.
Each endpoint type comes with its own structured configuration form, so you always know exactly which properties are available.</p>
<p><img src="/img/assets/kaoto-citrus-integration/test-endpoints.png" alt="Citrus test endpoints in the Kaoto catalog" /></p>
<h3 id="guided-property-forms">Guided property forms</h3>
<p>No more scanning through documentation to find the right property name or value format.
Kaoto renders a tailored form for each component, with proper input types, dropdowns, and contextual help.</p>
<p><img src="/img/assets/kaoto-citrus-integration/test-forms.png" alt="Guided property forms for Citrus endpoints" /></p>
<h2 id="works-in-vs-code">Works in VS Code</h2>
<p>Kaoto is available as a <a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-kaoto">VS Code extension</a>, which means you can design and execute Citrus tests without ever leaving your IDE.
Create a new Citrus test file, open it with Kaoto, and start building your test scenario visually.</p>
<p><img src="/img/assets/kaoto-citrus-integration/kaoto-extension.png" alt="Kaoto VS Code extension with Citrus testing support" /></p>
<p>You can run your tests directly from the Kaoto interface to validate your integration behavior during development — keeping the feedback loop as short as possible.</p>
<h2 id="yaml-dsl--and-more-to-come">YAML DSL — and more to come</h2>
<p>The Citrus tests created through Kaoto use the YAML DSL exclusively at the moment.
Kaoto serializes everything to the standard Citrus YAML format, so the tests you build visually are fully portable and compatible with the broader Citrus ecosystem.
Support for additional DSLs such as XML is planned for future releases.</p>
<h2 id="give-it-a-try">Give it a try</h2>
<p>Want to see it in action right now? You can try Citrus and Kaoto directly in your browser — no installation required — at the <a href="https://kaotoio.github.io/kaoto/">Kaoto online playground</a>.</p>
<p>For a full local setup:</p>
<ol>
<li>Install the <a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-kaoto">Kaoto VS Code extension</a></li>
<li>Create a new Citrus test file (<code class="highlighter-rouge">.citrus.yaml</code>)</li>
<li>Open it with the Kaoto editor and start designing your test</li>
</ol>
<p>Whether you are new to Citrus and looking for a gentle introduction, or an experienced user who wants to prototype tests faster — Kaoto’s visual approach is a great addition to your toolbox.</p>
<p>We are excited about the collaboration between the Citrus and Kaoto communities and look forward to expanding this integration further.
Give it a spin and let us know what you think!</p></content><author><name>Christoph Deppisch</name></author></entry><entry><title type="html">Quarkus - Event-Driven Kafka</title><link href="http://0.0.0.0:4000/samples/quarkus-event-driven-kafka/" rel="alternate" type="text/html" title="Quarkus - Event-Driven Kafka" /><published>2026-06-18T00:00:00+02:00</published><updated>2026-06-18T00:00:00+02:00</updated><id>http://0.0.0.0:4000/samples/sample-quarkus-event-driven-kafka</id><content type="html" xml:base="http://0.0.0.0:4000/samples/quarkus-event-driven-kafka/"><p>Event-driven architectures powered by Apache Kafka are everywhere. Microservices publish and consume events asynchronously, and the reactive nature of these systems makes them fast and scalable. But it also makes them harder to test. How do you verify that a message entering one Kafka topic produces the correct output on another? How do you write tests that are deterministic, readable, and run against a real broker instead of fragile mocks?</p>
<p>This post walks you through testing an event-driven Quarkus application with the <a href="https://citrusframework.org">Citrus</a> integration testing framework. You will see how Citrus connects to Kafka topics, sends and receives messages, and validates the results with just a few lines of code. By the end you will have a clear recipe for adding Citrus to your own Quarkus project.</p>
<h1 id="the-application-under-test">The application under test</h1>
<p>The example application is intentionally simple so we can focus on the testing side. It is a Quarkus service that listens for incoming text messages on a Kafka topic, transforms them to uppercase, and publishes the result to an output topic.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Kafka Topic (words-in) --&gt; Transform to Uppercase --&gt; Kafka Topic (words-out)
</code></pre></div></div>
<p>The implementation uses SmallRye Reactive Messaging, the MicroProfile standard that Quarkus adopts for event-driven communication. Two methods handle the entire flow:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@ApplicationScoped</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">EventDrivenApplication</span> <span class="o">{</span>
<span class="nd">@Channel</span><span class="o">(</span><span class="s">"words-out"</span><span class="o">)</span>
<span class="n">Emitter</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">emitter</span><span class="o">;</span>
<span class="nd">@Incoming</span><span class="o">(</span><span class="s">"words-in"</span><span class="o">)</span>
<span class="nd">@Outgoing</span><span class="o">(</span><span class="s">"uppercase"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">toUpperCase</span><span class="o">(</span><span class="n">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">message</span><span class="o">.</span><span class="na">toUpperCase</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Incoming</span><span class="o">(</span><span class="s">"uppercase"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">sink</span><span class="o">(</span><span class="n">String</span> <span class="n">word</span><span class="o">)</span> <span class="o">{</span>
<span class="n">emitter</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="s">"&gt;&gt; "</span> <span class="o">+</span> <span class="n">word</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The <code class="highlighter-rouge">toUpperCase</code> method receives each message from the <code class="highlighter-rouge">words-in</code> channel and pushes the uppercased result to an internal channel called <code class="highlighter-rouge">uppercase</code>. The <code class="highlighter-rouge">sink</code> method picks it up, prepends <code class="highlighter-rouge">"&gt;&gt; "</code>, and emits the final string to the <code class="highlighter-rouge">words-out</code> channel. The channel-to-topic mapping lives in <code class="highlighter-rouge">application.properties</code>:</p>
<div class="language-properties highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">mp.messaging.incoming.words-in.auto.offset.reset</span><span class="p">=</span><span class="s">earliest</span>
<span class="py">mp.messaging.incoming.words-in.topic</span><span class="p">=</span><span class="s">words-in</span>
<span class="py">mp.messaging.outgoing.words-out.topic</span><span class="p">=</span><span class="s">words-out</span>
</code></pre></div></div>
<p>That is the entire production code. Now let’s verify it.</p>
<h1 id="adding-citrus-to-the-project">Adding Citrus to the project</h1>
<p>Citrus is highly modular, so you only pull in the capabilities you actually need. For this Kafka scenario you add three test-scoped dependencies to the Maven POM:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-quarkus<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>4.10.1<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
<span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-kafka<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>4.10.1<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
<span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-junit-jupiter<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>4.10.1<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>
<p>Here is what each module brings to the table:</p>
<ul>
<li><strong>citrus-quarkus</strong> integrates Citrus with the Quarkus test lifecycle. It hooks into <code class="highlighter-rouge">@QuarkusTest</code> so that Citrus endpoints and test runners are available without any extra configuration.</li>
<li><strong>citrus-kafka</strong> provides Kafka endpoint implementations with built-in producer and consumer capabilities, message serialization, and topic configuration.</li>
<li><strong>citrus-junit-jupiter</strong> connects Citrus to JUnit 5, the test engine that Quarkus uses under the hood.</li>
</ul>
<p>No additional infrastructure setup is required. Quarkus dev services will automatically start a Kafka broker via Testcontainers when the tests run. Citrus connects to that same broker, which means your tests hit a real Kafka instance, not a mock.</p>
<h1 id="enabling-citrus-in-a-quarkus-test">Enabling Citrus in a Quarkus test</h1>
<p>The bridge between Quarkus and Citrus is a single annotation. Add <code class="highlighter-rouge">@CitrusSupport</code> next to <code class="highlighter-rouge">@QuarkusTest</code> and the framework takes care of the rest:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@QuarkusTest</span>
<span class="nd">@CitrusSupport</span>
<span class="kd">class</span> <span class="nc">EventDrivenApplicationTest</span> <span class="kd">implements</span> <span class="n">TestActionSupport</span> <span class="o">{</span>
<span class="nd">@CitrusResource</span>
<span class="n">GherkinTestActionRunner</span> <span class="n">runner</span><span class="o">;</span>
<span class="c1">// endpoint configurations and test methods</span>
<span class="o">}</span>
</code></pre></div></div>
<p>A few things are worth noting here.</p>
<p><code class="highlighter-rouge">@QuarkusTest</code> starts the application in test mode and activates dev services. This is where the Kafka Testcontainer gets launched. <code class="highlighter-rouge">@CitrusSupport</code> layers Citrus on top of that lifecycle so you can inject Citrus-specific resources into the test class.</p>
<p>The <code class="highlighter-rouge">GherkinTestActionRunner</code> is the entry point for all Citrus test actions. It provides a fluent API with Given-When-Then semantics that makes tests read almost like specifications. You inject it with the <code class="highlighter-rouge">@CitrusResource</code> annotation.</p>
<p>The <code class="highlighter-rouge">TestActionSupport</code> interface is a convenience that gives you static access to test actions like <code class="highlighter-rouge">send()</code> and <code class="highlighter-rouge">receive()</code> without extra imports. It keeps the test code clean and focused on the intent.</p>
<h1 id="configuring-kafka-endpoints">Configuring Kafka endpoints</h1>
<p>Before you can send and receive messages in a test, you need to tell Citrus which Kafka topics to use and how to connect. Citrus offers a declarative annotation-based approach that fits naturally into the test class:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@CitrusEndpoint</span>
<span class="nd">@KafkaEndpointConfig</span><span class="o">(</span><span class="n">topic</span> <span class="o">=</span> <span class="s">"words-in"</span><span class="o">,</span>
<span class="n">server</span> <span class="o">=</span> <span class="s">"${kafka.bootstrap.servers}"</span><span class="o">)</span>
<span class="n">KafkaEndpoint</span> <span class="n">wordsIn</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="nd">@KafkaEndpointConfig</span><span class="o">(</span><span class="n">topic</span> <span class="o">=</span> <span class="s">"words-out"</span><span class="o">,</span>
<span class="n">server</span> <span class="o">=</span> <span class="s">"${kafka.bootstrap.servers}"</span><span class="o">)</span>
<span class="n">KafkaEndpoint</span> <span class="n">wordsOut</span><span class="o">;</span>
</code></pre></div></div>
<p>Each field annotated with <code class="highlighter-rouge">@CitrusEndpoint</code> becomes a fully configured Kafka endpoint. The <code class="highlighter-rouge">@KafkaEndpointConfig</code> annotation specifies the topic name and the broker address.</p>
<p>The <code class="highlighter-rouge">${kafka.bootstrap.servers}</code> property is the key to the seamless integration with Quarkus dev services. When Quarkus starts the Kafka Testcontainer, it publishes the broker address under this property. Citrus picks it up automatically, so both the application and the test connect to the exact same Kafka instance. No hardcoded ports, no environment-specific configuration files, no manual container management.</p>
<h1 id="writing-the-test">Writing the test</h1>
<p>With the endpoints in place, the actual test is remarkably concise:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">shouldHandleEvents</span><span class="o">()</span> <span class="o">{</span>
<span class="n">runner</span><span class="o">.</span><span class="na">when</span><span class="o">(</span>
<span class="n">send</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="n">wordsIn</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"Hi"</span><span class="o">)</span>
<span class="o">);</span>
<span class="n">runner</span><span class="o">.</span><span class="na">then</span><span class="o">(</span>
<span class="n">receive</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="n">wordsOut</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"&gt;&gt; HI"</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The test sends the string <code class="highlighter-rouge">"Hi"</code> to the <code class="highlighter-rouge">words-in</code> topic and then verifies that the <code class="highlighter-rouge">words-out</code> topic receives <code class="highlighter-rouge">"&gt;&gt; HI"</code>. That single assertion covers the entire processing pipeline: Kafka consumption, the uppercase transformation, the prefix formatting, and the Kafka production on the output side.</p>
<p>The Gherkin-style <code class="highlighter-rouge">when</code> and <code class="highlighter-rouge">then</code> methods are not just syntactic sugar. They clearly communicate intent. The <strong>when</strong> block describes the stimulus, the action that triggers the application behavior. The <strong>then</strong> block describes the expected outcome. If the received message does not match the expected body, Citrus fails the test with a clear diff showing exactly what went wrong.</p>
<h1 id="how-it-all-fits-together">How it all fits together</h1>
<p>When you run the test with <code class="highlighter-rouge">./mvnw clean test</code>, here is the sequence of events that unfolds behind the scenes:</p>
<ol>
<li><strong>Quarkus starts</strong> the application in test mode and activates dev services.</li>
<li><strong>Dev services launches</strong> a Kafka container via Testcontainers and publishes the broker address as <code class="highlighter-rouge">kafka.bootstrap.servers</code>.</li>
<li><strong>Citrus initializes</strong> the <code class="highlighter-rouge">wordsIn</code> and <code class="highlighter-rouge">wordsOut</code> endpoints using the broker address from the property.</li>
<li><strong>The test sends</strong> <code class="highlighter-rouge">"Hi"</code> to the <code class="highlighter-rouge">words-in</code> topic through the <code class="highlighter-rouge">wordsIn</code> endpoint.</li>
<li><strong>The application consumes</strong> the message, transforms it to <code class="highlighter-rouge">"HI"</code>, prefixes it with <code class="highlighter-rouge">"&gt;&gt; "</code>, and publishes <code class="highlighter-rouge">"&gt;&gt; HI"</code> to the <code class="highlighter-rouge">words-out</code> topic.</li>
<li><strong>Citrus receives</strong> the message from the <code class="highlighter-rouge">words-out</code> topic through the <code class="highlighter-rouge">wordsOut</code> endpoint and validates it against the expected body.</li>
<li><strong>The test completes</strong> and Quarkus tears down the Kafka container automatically.</li>
</ol>
<p>No manual Kafka setup, no Docker Compose files to maintain, no cleanup scripts. The entire infrastructure lifecycle is managed for you.</p>
<p>There is one small piece of test configuration worth mentioning. Because Citrus and Quarkus share the same classpath, you need to tell Quarkus CDI to allow split packages from the Citrus framework. This is done in <code class="highlighter-rouge">src/test/resources/application.properties</code>:</p>
<div class="language-properties highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">quarkus.arc.ignored-split-packages</span><span class="p">=</span><span class="s">org.citrusframework.*</span>
</code></pre></div></div>
<p>This avoids CDI bean discovery conflicts and is a one-line addition that you set once and forget.</p>
<h1 id="the-complete-test-class">The complete test class</h1>
<p>Here is the full test in one place for easy reference:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@QuarkusTest</span>
<span class="nd">@CitrusSupport</span>
<span class="kd">class</span> <span class="nc">EventDrivenApplicationTest</span> <span class="kd">implements</span> <span class="n">TestActionSupport</span> <span class="o">{</span>
<span class="nd">@CitrusEndpoint</span>
<span class="nd">@KafkaEndpointConfig</span><span class="o">(</span><span class="n">topic</span> <span class="o">=</span> <span class="s">"words-in"</span><span class="o">,</span>
<span class="n">server</span> <span class="o">=</span> <span class="s">"${kafka.bootstrap.servers}"</span><span class="o">)</span>
<span class="n">KafkaEndpoint</span> <span class="n">wordsIn</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="nd">@KafkaEndpointConfig</span><span class="o">(</span><span class="n">topic</span> <span class="o">=</span> <span class="s">"words-out"</span><span class="o">,</span>
<span class="n">server</span> <span class="o">=</span> <span class="s">"${kafka.bootstrap.servers}"</span><span class="o">)</span>
<span class="n">KafkaEndpoint</span> <span class="n">wordsOut</span><span class="o">;</span>
<span class="nd">@CitrusResource</span>
<span class="n">GherkinTestActionRunner</span> <span class="n">runner</span><span class="o">;</span>
<span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">shouldHandleEvents</span><span class="o">()</span> <span class="o">{</span>
<span class="n">runner</span><span class="o">.</span><span class="na">when</span><span class="o">(</span>
<span class="n">send</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="n">wordsIn</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"Hi"</span><span class="o">)</span>
<span class="o">);</span>
<span class="n">runner</span><span class="o">.</span><span class="na">then</span><span class="o">(</span>
<span class="n">receive</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="n">wordsOut</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"&gt;&gt; HI"</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Under 50 lines of code for a complete integration test that sends a message to Kafka, waits for the application to process it, receives the result from a different topic, and validates the output. That is the power of Citrus combined with Quarkus dev services.</p>
<h1 id="where-to-go-from-here">Where to go from here</h1>
<p>This example covers the fundamentals, but Citrus has much more to offer for Kafka testing scenarios:</p>
<ul>
<li><strong>Message headers</strong>: Kafka records carry headers and message keys alongside the payload. Citrus lets you set and verify both in your send and receive actions.</li>
<li><strong>JSON validation</strong>: Add the <code class="highlighter-rouge">citrus-validation-json</code> module and Citrus will compare received JSON payloads field by field, supporting ignore patterns, validation matchers, and flexible element ordering.</li>
<li><strong>Multiple topics and complex flows</strong>: Real applications often consume from several topics and produce to several others. You can define as many endpoints as you need and orchestrate multi-step test scenarios.</li>
<li><strong>Error scenarios</strong>: Test what happens when the application receives malformed input or when downstream services are unavailable. Citrus gives you full control over the messages you send, making negative testing straightforward.</li>
<li><strong>Endpoint configuration classes</strong>: When your test suite grows, you can extract endpoint definitions into shared <code class="highlighter-rouge">@CitrusConfiguration</code> classes and reuse them across multiple tests. The <a href="/samples/quarkus/">Quarkus sample</a> on this site demonstrates this pattern in detail.</li>
</ul>
<p>To explore the full example project including the source code, head over to the <a href="https://github.com/citrusframework/citrus-quarkus-examples">citrus-quarkus-examples</a> repository on GitHub.</p>
<p>For deeper dives into Citrus capabilities, the <a href="https://citrusframework.org/citrus/reference/html/">reference guide</a> covers Kafka endpoints, Quarkus integration, and the many other messaging transports that Citrus supports.</p></content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://0.0.0.0:4000/img/icons/quarkus.png" /></entry><entry><title type="html">Get ready for Citrus 5.0.0</title><link href="http://0.0.0.0:4000/news/2026/06/18/approaching-release-5.0/" rel="alternate" type="text/html" title="Get ready for Citrus 5.0.0" /><published>2026-06-18T00:00:00+02:00</published><updated>2026-06-18T00:00:00+02:00</updated><id>http://0.0.0.0:4000/news/2026/06/18/approaching-release-5.0</id><content type="html" xml:base="http://0.0.0.0:4000/news/2026/06/18/approaching-release-5.0/"><p>Citrus 5.0.0 is on the horizon! The next major release of the Citrus integration testing framework is planned for <strong>GA in Q3 2026</strong> and brings a sweeping update of the core technology stack that Citrus builds on. Two milestone releases (M1 and M2) are already available for early adopters who want to get a head start on migration.</p>
<h2 id="why-a-new-major-version">Why a new major version?</h2>
<p>The Java ecosystem has taken significant leaps forward in recent time. Spring Framework 7, Spring Boot 4, Kafka 4, JUnit 6 and Jackson 3 all represent new major versions that come with their own set of breaking changes and modern API improvements. Citrus 5 aligns with these releases so you can use the latest and greatest enterprise Java libraries alongside your integration tests.</p>
<p>Here are the main objectives for Citrus 5.0:</p>
<ul>
<li>While still supporting Java 17 Citrus aligns with <strong>Java 21</strong> (LTS baseline) and <strong>Java 25</strong> support</li>
<li>Update to <strong>Spring Framework 7</strong> and <strong>Spring Boot 4</strong></li>
<li>Update to <strong>JUnit 6</strong></li>
<li>Update to <strong>Jackson 3</strong>, <strong>Apache Camel 4.21</strong>, <strong>Kafka 4.2</strong>, <strong>Jetty 12</strong></li>
<li>Introduce a <strong>Citrus MCP server</strong> for AI-assisted test development</li>
<li>Expand <strong>Quarkus</strong> support with easy to use integration into the Quarkus test lifecycle</li>
<li>Streamline YAML DSL schema and improve the UX with visual designers such as Kaoto</li>
</ul>
<h2 id="java-17-and-java-21">Java 17 and Java 21</h2>
<p>Citrus 5.0 still supports Java 17 as a minimum Java version but all cursors are pointing towards <strong>Java 21</strong>. This lets the framework and its users take full advantage of modern Java features such as virtual threads, pattern matching, sequenced collections and record patterns.</p>
<h2 id="dependency-updates">Dependency updates</h2>
<p>Citrus depends on many outstanding open source libraries and the 5.0 release aligns with their latest major versions. Here is an overview of the most important updates:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Library</th>
<th style="text-align: left">Version</th>
<th style="text-align: left">Library</th>
<th style="text-align: left">Version</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">Spring Framework</td>
<td style="text-align: left">7.0.7</td>
<td style="text-align: left">Spring Boot</td>
<td style="text-align: left">4.0.6</td>
</tr>
<tr>
<td style="text-align: left">Spring Integration</td>
<td style="text-align: left">7.0.4</td>
<td style="text-align: left">Spring WS</td>
<td style="text-align: left">5.0.1</td>
</tr>
<tr>
<td style="text-align: left">Apache Camel</td>
<td style="text-align: left">4.21.0</td>
<td style="text-align: left">Apache Kafka</td>
<td style="text-align: left">4.2.0</td>
</tr>
<tr>
<td style="text-align: left">Jackson</td>
<td style="text-align: left">3.1.0</td>
<td style="text-align: left">Jetty</td>
<td style="text-align: left">12.1.8</td>
</tr>
<tr>
<td style="text-align: left">JUnit</td>
<td style="text-align: left">6</td>
<td style="text-align: left">Quarkus</td>
<td style="text-align: left">3.36.2</td>
</tr>
</tbody>
</table>
<h3 id="spring-framework-7-and-spring-boot-4">Spring Framework 7 and Spring Boot 4</h3>
<p>Spring 7 brings a refined programming model, improved observability and continued evolution of the Spring module system. Spring Boot 4 builds on top of this foundation with updated auto-configuration and new starter patterns. Citrus 5 is fully aligned with these releases, so you can use Spring Boot 4 in your test projects without dependency conflicts.</p>
<h3 id="junit-6">JUnit 6</h3>
<p>JUnit 6 is the next generation of the most popular Java testing framework. Citrus 5 ships with a dedicated runtime module for JUnit 6, so you can write your Citrus test cases using the new JUnit 6 APIs and extensions right away.</p>
<h3 id="jackson-3">Jackson 3</h3>
<p>Jackson 3.1.0 represents a major step in the Jackson JSON library with a reworked module system and updated API. Citrus uses Jackson extensively for JSON message validation and data binding, and version 5.0 ensures full compatibility with the new Jackson 3 APIs.</p>
<h3 id="apache-kafka-42">Apache Kafka 4.2</h3>
<p>Kafka 4.2 introduces the new KRaft-based architecture as the default, removing the ZooKeeper dependency entirely. Citrus 5 aligns with this release and also brings new Testcontainers support for Kafka into Citrus.</p>
<h3 id="quarkus-336">Quarkus 3.36</h3>
<p>Citrus is fully integrated into the Quarkus test ecosystem with special Quarkus test resource implementations that are able to take part of the Quarkus test lifecycle.</p>
<p>Citrus 5 brings continued enhancements to streamline the Citrus integration into Quarkus for instance by populating <strong>Quarkus dev services properties as test variables</strong> for seamless integration with Quarkus dev services. As an example your Citrus tests might share the Kafka broker configuration from dev services out of the box.</p>
<h2 id="citrus-mcp-server">Citrus MCP server</h2>
<p>One of the most exciting additions in Citrus 5 is the new <strong>Model Context Protocol (MCP) server</strong>. MCP is an emerging standard that allows AI coding assistants to interact with development tools in a structured way.</p>
<p>The Citrus MCP server exposes the framework’s test vocabulary, endpoint configurations and test action catalog to AI assistants. This enables intelligent code completion, test generation and guided test authoring directly from your IDE or command line.</p>
<p>With M2 the MCP server is also published as a <strong>Claude Code plugin</strong> via JBang, making it easy to integrate Citrus-aware AI assistance into your development workflow.</p>
<h2 id="expanded-testcontainers-support">Expanded Testcontainers support</h2>
<p>Citrus has long embraced Testcontainers for managing external service dependencies in integration tests. Version 5.0 significantly expands this support with new container implementations:</p>
<ul>
<li><strong>Native Apache Kafka container</strong> - a dedicated container implementation as an alternative to the existing Testcontainers Kafka module</li>
<li><strong>Strimzi Kafka container</strong> - run Kafka via the Strimzi container image, ideal for teams already using Strimzi in their Kubernetes environments</li>
<li><strong>Floci container support</strong> - adds Floci containers to the Citrus Testcontainers module as an alternative AWS test service provider.</li>
</ul>
<h2 id="try-it-now">Try it now</h2>
<p>The two milestone releases M1 and M2 are available on Maven Central. Add Citrus 5.0.0-M2 to your project to try it out:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-bom<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>5.0.0-M2<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span>
<span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>
<p>Milestone releases are intended for early adopters to validate their existing test suites against the new dependency stack and to provide feedback before the GA release. We encourage you to try upgrading now so you have plenty of time to adapt your tests before the final release in Q3 2026.</p>
<h2 id="provide-feedback">Provide feedback</h2>
<p>We would love to hear from you! If you run into issues during migration or have ideas for improvements, please open an issue on <a href="https://github.com/citrusframework/citrus/issues">GitHub</a> or start a discussion. Your feedback is invaluable in shaping the GA release.</p>
<p>The Citrus community has once again shown its strength in bringing this major update together. After 18+ years of development Citrus continues to evolve with the Java ecosystem, and we are excited to deliver a framework that stays current with the technologies you use every day. Thank you to everyone who contributed!</p></content><author><name>Christoph Deppisch</name></author></entry><entry><title type="html">XML samples</title><link href="http://0.0.0.0:4000/samples/overview/xml/" rel="alternate" type="text/html" title="XML samples" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/overview/samples-xml</id><content type="html" xml:base="http://0.0.0.0:4000/samples/overview/xml/"><p>Read about the XML validation support in the <a href="https://citrusframework.org/citrus/reference/html/#xml-message-validation">reference guide</a></p></content><author><name></name></author></entry><entry><title type="html">Soap WS samples</title><link href="http://0.0.0.0:4000/samples/overview/soap/" rel="alternate" type="text/html" title="Soap WS samples" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/overview/samples-soap</id><content type="html" xml:base="http://0.0.0.0:4000/samples/overview/soap/"><p>Read about the SOAP WebServices support in the <a href="https://citrusframework.org/citrus/reference/html/#soap-webservices">reference guide</a></p></content><author><name></name></author></entry><entry><title type="html">Http samples</title><link href="http://0.0.0.0:4000/samples/overview/http/" rel="alternate" type="text/html" title="Http samples" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/overview/samples-http</id><content type="html" xml:base="http://0.0.0.0:4000/samples/overview/http/"><p>Read about the Http support in the <a href="https://citrusframework.org/citrus/reference/html#http-rest">reference guide</a></p></content><author><name></name></author></entry><entry><title type="html">Ftp samples</title><link href="http://0.0.0.0:4000/samples/overview/ftp/" rel="alternate" type="text/html" title="Ftp samples" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/overview/samples-ftp</id><content type="html" xml:base="http://0.0.0.0:4000/samples/overview/ftp/"><p>Read about the Ftp/Sftp support in the <a href="https://citrusframework.org/citrus/reference/html/#ftp">reference guide</a></p></content><author><name></name></author></entry><entry><title type="html">JBang sample</title><link href="http://0.0.0.0:4000/samples/jbang/" rel="alternate" type="text/html" title="JBang sample" /><published>2024-01-23T00:00:00+01:00</published><updated>2024-01-23T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/sample-jbang</id><content type="html" xml:base="http://0.0.0.0:4000/samples/jbang/"><p>You can easily create and run Citrus tests with <a href="https://www.jbang.dev/">JBang</a>.
The JBang tool support in Citrus is described in more detail in <a href="https://citrusframework.org/citrus/reference/html#runtime-jbang">reference guide</a></p>
<p>Running Citrus via JBang does not require any project setup which is a fantastic match for fast prototyping of integration tests.
The JBang command will automatically set up everything you need to run the Citrus test.
This means you can run your test case sources directly from your command line.</p>
<p>To initialize a test you can run the script <code class="highlighter-rouge">citrus@citrusframework/citrus</code> with the <code class="highlighter-rouge">init</code> command as follows:</p>
<p><em>Initialize my-test.yaml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jbang citrus@citrusframework/citrus init my-test.yaml
</code></pre></div></div>
<p>The command above uses the JBang catalog <code class="highlighter-rouge">citrus@citrusframework/citrus</code> located on the <a href="https://github.com/citrusframework/citrus">Citrus GitHub repository</a>.
JBang will automatically resolve all dependencies and execute the command line script tool.
This initializes the Citrus test file.
You will find the created test source file in the current directory.</p>
<p><em>my-test.yaml</em></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">my-test</span>
<span class="na">author</span><span class="pi">:</span> <span class="s">Citrus</span>
<span class="na">status</span><span class="pi">:</span> <span class="s">FINAL</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Sample test in YAML</span>
<span class="na">variables</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">message</span>
<span class="na">value</span><span class="pi">:</span> <span class="s">Citrus rocks!</span>
<span class="na">actions</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">echo</span><span class="pi">:</span>
<span class="na">message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${message}"</span>
</code></pre></div></div>
<p>The JBang script is able to initialize any supported Citrus test domain specific language <code class="highlighter-rouge">.java</code>, <code class="highlighter-rouge">.xml</code>, <code class="highlighter-rouge">.yaml</code>, <code class="highlighter-rouge">.groovy</code> or <code class="highlighter-rouge">.feature</code>.</p>
<p>You can now run this test source file without any prior project setup using Citrus JBang:</p>
<p><em>Run my-test.yaml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jbang citrus@citrusframework/citrus run my-test.yaml
</code></pre></div></div>
<p>The command output will be like this:</p>
<p><em>Output</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>main] citrusframework.testng.TestNGEngine : Running <span class="nb">test source </span>my-test.yaml
<span class="o">[</span>main] org.testng.internal.Utils : <span class="o">[</span>TestNG] Running:
<span class="o">[</span>main] rusframework.report.LoggingReporter : .__ __
<span class="o">[</span>main] rusframework.report.LoggingReporter : ____ |__|/ |________ __ __ ______
<span class="o">[</span>main] rusframework.report.LoggingReporter : _/ ___<span class="se">\|</span> <span class="se">\ </span> __<span class="se">\_</span> __ <span class="se">\ </span> | <span class="se">\/</span> ___/
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="se">\ </span> <span class="se">\_</span>__| <span class="o">||</span> | | | <span class="se">\/</span> | /<span class="se">\_</span>__ <span class="se">\ </span>
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="se">\_</span>__ <span class="o">&gt;</span>__||__| |__| |____//____ <span class="o">&gt;</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="se">\/</span> <span class="se">\/</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : C I T R U S T E S T S 4.10.1
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>main] .citrusframework.actions.EchoAction : Citrus rocks!
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : TEST SUCCESS my-test <span class="o">(</span>org.citrusframework<span class="o">)</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : CITRUS TEST RESULTS
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : SUCCESS <span class="o">(</span> 3 ms<span class="o">)</span> my-test
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : TOTAL: 1
<span class="o">[</span>main] rusframework.report.LoggingReporter : SUCCESS: 1 <span class="o">(</span>100.0%<span class="o">)</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter : FAILED: 0 <span class="o">(</span>0.0%<span class="o">)</span>
<span class="o">[</span>main] rusframework.report.LoggingReporter : PERFORMANCE: 0 ms
<span class="o">[</span>main] rusframework.report.LoggingReporter :
<span class="o">[</span>main] rusframework.report.LoggingReporter : <span class="nt">------------------------------------------------------------------------</span>
<span class="o">===============================================</span>
Default Suite
Total tests run: 1, Passes: 1, Failures: 0, Skips: 0
<span class="o">===============================================</span>
</code></pre></div></div>
<h2 id="install-citrus-jbang-app">Install Citrus JBang app</h2>
<p>For a more convenient command line usage you can install Citrus as a JBang app.</p>
<p><em>Install Citrus as JBang app</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jbang trust add https://github.com/citrusframework/citrus/
jbang app <span class="nb">install </span>citrus@citrusframework/citrus
</code></pre></div></div>
<p>Now you can just call <code class="highlighter-rouge">citrus</code> and create and run tests with Citrus JBang.</p>
<p><em>Run my-test.yaml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run my-test.yaml
</code></pre></div></div>
<h2 id="run-tests">Run tests</h2>
<p>You can directly run test sources with Citrus JBang.
This includes test sources written in Java (<code class="highlighter-rouge">.java</code>), XML (<code class="highlighter-rouge">.xml</code>), YAML (<code class="highlighter-rouge">.yaml</code>), Groovy (<code class="highlighter-rouge">.groovy</code>) or as a Cucumber Gherkin feature file (<code class="highlighter-rouge">.feature</code>).</p>
<h3 id="java-test-sources">Java test sources</h3>
<p><em>Initialize MyTest.java</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus init MyTest.java
</code></pre></div></div>
<p><em>MyTest.java</em></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.citrusframework.TestCaseRunner</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.citrusframework.annotations.CitrusResource</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">citrusframework</span><span class="o">.</span><span class="na">actions</span><span class="o">.</span><span class="na">CreateVariablesAction</span><span class="o">.</span><span class="na">Builder</span><span class="o">.</span><span class="na">createVariables</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">citrusframework</span><span class="o">.</span><span class="na">actions</span><span class="o">.</span><span class="na">EchoAction</span><span class="o">.</span><span class="na">Builder</span><span class="o">.</span><span class="na">echo</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyTest</span> <span class="kd">implements</span> <span class="n">Runnable</span> <span class="o">{</span>
<span class="nd">@CitrusResource</span>
<span class="n">TestCaseRunner</span> <span class="n">t</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
<span class="n">t</span><span class="o">.</span><span class="na">given</span><span class="o">(</span>
<span class="n">createVariables</span><span class="o">().</span><span class="na">variable</span><span class="o">(</span><span class="s">"message"</span><span class="o">,</span> <span class="s">"Citrus rocks!"</span><span class="o">)</span>
<span class="o">);</span>
<span class="n">t</span><span class="o">.</span><span class="na">then</span><span class="o">(</span>
<span class="n">echo</span><span class="o">().</span><span class="na">message</span><span class="o">(</span><span class="s">"${message}"</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p><em>Run MyTest.java</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run MyTest.java
</code></pre></div></div>
<h3 id="xml-test-sources">XML test sources</h3>
<p><em>Initialize my-test.xml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus init my-test.xml
</code></pre></div></div>
<p><em>my-test.xml</em></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;test</span> <span class="na">name=</span><span class="s">"EchoTest"</span> <span class="na">author=</span><span class="s">"Christoph"</span> <span class="na">status=</span><span class="s">"FINAL"</span> <span class="na">xmlns=</span><span class="s">"http://citrusframework.org/schema/xml/testcase"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://citrusframework.org/schema/xml/testcase http://citrusframework.org/schema/xml/testcase/citrus-testcase.xsd"</span><span class="nt">&gt;</span>
<span class="nt">&lt;description&gt;</span>Sample test in XML<span class="nt">&lt;/description&gt;</span>
<span class="nt">&lt;variables&gt;</span>
<span class="nt">&lt;variable</span> <span class="na">name=</span><span class="s">"message"</span> <span class="na">value=</span><span class="s">"Citrus rocks!"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/variables&gt;</span>
<span class="nt">&lt;actions&gt;</span>
<span class="nt">&lt;echo</span> <span class="na">message=</span><span class="s">"${message}"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/actions&gt;</span>
<span class="nt">&lt;/test&gt;</span>
</code></pre></div></div>
<p><em>Run my-test.xml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run my-test.xml
</code></pre></div></div>
<h3 id="yaml-test-sources">YAML test sources</h3>
<p><em>Initialize my-test.yaml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus init my-test.yaml
</code></pre></div></div>
<p><em>my-test.yaml</em></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">EchoTest</span>
<span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Sample</span><span class="nv"> </span><span class="s">test</span><span class="nv"> </span><span class="s">in</span><span class="nv"> </span><span class="s">YAML"</span>
<span class="na">variables</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">message"</span>
<span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Citrus</span><span class="nv"> </span><span class="s">rocks!"</span>
<span class="na">actions</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">echo</span><span class="pi">:</span>
<span class="na">message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${message}"</span>
</code></pre></div></div>
<p><em>Run my-test.yaml</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run my-test.yaml
</code></pre></div></div>
<h3 id="groovy-test-sources">Groovy test sources</h3>
<p><em>Initialize my-test.groovy</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus init my-test.groovy
</code></pre></div></div>
<p><em>my-test.groovy</em></p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">citrusframework</span><span class="o">.</span><span class="na">actions</span><span class="o">.</span><span class="na">EchoAction</span><span class="o">.</span><span class="na">Builder</span><span class="o">.</span><span class="na">echo</span>
<span class="n">name</span> <span class="s2">"EchoTest"</span>
<span class="n">description</span> <span class="s2">"Sample test in Groovy"</span>
<span class="n">variables</span> <span class="o">{</span>
<span class="n">message</span><span class="o">=</span><span class="s2">"Citrus rocks!"</span>
<span class="o">}</span>
<span class="n">actions</span> <span class="o">{</span>
<span class="n">$</span><span class="o">(</span><span class="n">echo</span><span class="o">().</span><span class="na">message</span><span class="o">(</span><span class="s1">'${message}'</span><span class="o">))</span>
<span class="o">}</span>
</code></pre></div></div>
<p><em>Run my-test.groovy</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run my-test.groovy
</code></pre></div></div>
<h3 id="cucumber-feature-sources">Cucumber feature sources</h3>
<p><em>Initialize my-test.feature</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus init my-test.feature
</code></pre></div></div>
<p><em>my-test.feature</em></p>
<div class="language-gherkin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Feature</span><span class="p">:</span> EchoTest
<span class="kn">Background</span><span class="p">:</span>
<span class="nf">Given </span>variables
<span class="p">|</span> <span class="nv">message</span> <span class="p">|</span> <span class="nv">Citrus</span> <span class="nv">rocks!</span> <span class="p">|</span>
<span class="kn">Scenario</span><span class="p">:</span> Print message
<span class="nf">Then </span>print '${message}'
</code></pre></div></div>
<p><em>Run my-test.feature</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jbang <span class="nt">--deps</span> org.citrusframework:citrus-cucumber-all:4.9.0-SNAPSHOT citrus run my-test.feature
</code></pre></div></div>
<p><em>NOTE:</em> Many of the predefined Cucumber steps (e.g. <code class="highlighter-rouge">Then print '&lt;message&gt;'</code>) in Citrus are provided in a separate Citrus child project called <a href="https://github.com/citrusframework/yaks">YAKS</a>.
You need to add additional project dependencies for that steps to be loaded as part of the JBang script.
The <code class="highlighter-rouge">--deps</code> option adds dependencies using Maven artifact coordinates.
You may add the additional modules to the <code class="highlighter-rouge">jbang.properties</code> as described in the next section.</p>
<h2 id="additional-jbang-dependencies">Additional JBang dependencies</h2>
<p>Citrus JBang comes with a set of default dependencies that makes the scripts run as tests.</p>
<p>The default modules that you can use in Citrus JBang are:</p>
<ul>
<li>org.citrusframework:citrus-base</li>
<li>org.citrusframework:citrus-jbang-connector</li>
<li>org.citrusframework:citrus-groovy</li>
<li>org.citrusframework:citrus-xml</li>
<li>org.citrusframework:citrus-yaml</li>
<li>org.citrusframework:citrus-http</li>
<li>org.citrusframework:citrus-validation-json</li>
<li>org.citrusframework:citrus-validation-xml</li>
</ul>
<p>This enables you to run Java, YAML, XML, Groovy tests out of the box.
In case your tests uses an additional feature from the Citrus project you may need to add the module so JBang can load the dependency at startup.</p>
<p>The easiest way to do this is to create a <code class="highlighter-rouge">jbang.properties</code> file that defines the additional dependencies:</p>
<p><em>jbang.properties</em></p>
<div class="language-properties highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Declare required additional dependencies
</span><span class="py">run.deps</span><span class="p">=</span><span class="s">org.citrusframework:citrus-camel:4.10.1,</span><span class="se">\
</span><span class="s">org.citrusframework:citrus-testcontainers:4.10.1,</span><span class="se">\
</span><span class="s">org.citrusframework:citrus-kafka:4.10.1</span>
</code></pre></div></div>
<p>The file above adds the modules <code class="highlighter-rouge">citrus-camel</code>, <code class="highlighter-rouge">citrus-testcontainers</code> and <code class="highlighter-rouge">citrus-kafka</code> so you can use them in your JBang Citrus test source.</p>
<p>The <code class="highlighter-rouge">jbang.properties</code> file may be located right next to the test source file or in your user home directory for global settings.</p>
<p><em>IMPORTANT:</em> In case you want to run Cucumber BDD Gherkin feature files and use the predefined steps included in the <a href="https://github.com/citrusframework/yaks">YAKS</a> project,
you need to add this YAKS runtime dependency accordingly: <code class="highlighter-rouge">org.citrusframework.yaks:yaks-standard:0.20.0</code></p>
<h2 id="run-from-clipboard">Run from clipboard</h2>
<p>You can run tests from your current clipboard.
Just use the file name <code class="highlighter-rouge">clipboard.xxx</code> where the file extension defines the type of the test source (<code class="highlighter-rouge">.java</code>, <code class="highlighter-rouge">.yaml</code>, <code class="highlighter-rouge">.xml</code>, <code class="highlighter-rouge">.groovy</code>, <code class="highlighter-rouge">.feature</code>).</p>
<p><em>Run YAML test from Clipboard</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus run clipboard.yaml
</code></pre></div></div>
<h2 id="list-tests">List tests</h2>
<p>The <code class="highlighter-rouge">ls</code> command lists all running Citrus tests.
These tests may be started</p>
<p><em>List running tests</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>citrus <span class="nb">ls</span>
</code></pre></div></div>
<p><em>Command output</em></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PID NAME STATUS AGE
19201 my-test.yaml Running 20s
</code></pre></div></div></content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://0.0.0.0:4000/img/icons/jbang.png" /></entry><entry><title type="html">Quarkus sample</title><link href="http://0.0.0.0:4000/samples/quarkus/" rel="alternate" type="text/html" title="Quarkus sample" /><published>2023-11-03T00:00:00+01:00</published><updated>2023-11-03T00:00:00+01:00</updated><id>http://0.0.0.0:4000/samples/sample-quarkus</id><content type="html" xml:base="http://0.0.0.0:4000/samples/quarkus/"><p>This project uses Quarkus to implement a sample event-driven application and shows how to verify the event processing with an automated integration test written in <a href="https://citrusframework.org">Citrus</a>.
The Quarkus support in Citrus is described in more detail in <a href="https://citrusframework.org/citrus/reference/html#runtime-quarkus">reference guide</a></p>
<h2 id="objectives">Objectives</h2>
<p>The project uses the Quarkus test framework to set up a dev services environment with JUnit Jupiter where the application is running on the local machine.
The Quarkus dev services capabilities will automatically start Testcontainers during the test in order to simulate the surrounding infrastructure
(e.g. PostgreSQL database and the Kafka message broker).</p>
<p>If you want to learn more about Quarkus, please visit its website: <a href="https://quarkus.io/">https://quarkus.io/</a>.</p>
<h2 id="quarkus-sample-application">Quarkus sample application</h2>
<p>The Quarkus sample demo application is a food market event-driven application that listens for incoming events of type <code class="highlighter-rouge">booking</code> and <code class="highlighter-rouge">supply</code>.</p>
<p><img src="/img/assets/sample-quarkus/food-market-app-demo.png" alt="Food Market App" /></p>
<p>Users are able to add booking events. Each of them references a product and gives an amount as well as an accepted price in a simple Json object structure.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="s2">"client"</span><span class="p">:</span><span class="w"> </span><span class="s2">"citrus-test"</span><span class="p">,</span><span class="w"> </span><span class="s2">"product"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Pineapple"</span><span class="p">,</span><span class="w"> </span><span class="s2">"amount"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="s2">"price"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.99</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>At the same time suppliers may add their individual supply events that again reference a product with an amount and a selling price.</p>
<p>The Quarkus application consumes both event types and as soon as bookings and supplies do match in all criteria the food market application will produce booking-completed and shipping events as a result.</p>
<p>All events are produced and consumed with Kafka event streams.
The domain model objects with their individual status are stored in a PostgreSQL database.</p>
<h2 id="adding-citrus-to-the-project">Adding Citrus to the project</h2>
<p>Looking at the Maven <code class="highlighter-rouge">pom.xml</code> you will see that Citrus is added as a test scoped dependency.
The most convenient way to add Citrus to your project is to import the <code class="highlighter-rouge">citrus-bom</code>.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependencyManagement&gt;</span>
<span class="nt">&lt;dependencies&gt;</span>
<span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-bom<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>4.10.1<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span>
<span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
<span class="nt">&lt;/dependencies&gt;</span>
<span class="nt">&lt;/dependencyManagement&gt;</span>
</code></pre></div></div>
<p>Citrus is very modular. This means you can choose from a wide range of modules that add specific testing capabilities to the project (e.g. citrus-kafka, citrus-http, citrus-mail, …).
In this sample project we include the following modules as test scoped dependencies:</p>
<ul>
<li>citrus-quarkus</li>
<li>citrus-kafka</li>
<li>citrus-http</li>
<li>citrus-sql</li>
<li>citrus-selenium</li>
<li>citrus-validation-json</li>
<li>citrus-validation-text</li>
</ul>
<p>The <code class="highlighter-rouge">citrus-quarkus</code> module provides the QuarkusTest resource implementation that enables Citrus on a Quarkus test.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.citrusframework<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>citrus-quarkus<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>
<p>The other modules add very specific Citrus capabilities such as validation of a Json message payload.</p>
<p>This completes the dependency setup.
Now we can move on to writing an automated integration test that verifies the Quarkus application.</p>
<h2 id="enable-citrus-with-quarkustest">Enable Citrus with QuarkusTest</h2>
<p>The test uses an arbitrary <code class="highlighter-rouge">@QuarkusTest</code> annotation with JUnit Jupiter.
This means that Quarkus takes care of starting the application under test.
It also starts some Testcontainers for the PostgreSQL database and the Kafka message broker.</p>
<p>You can enable the Citrus capabilities on the test by adding the <code class="highlighter-rouge">@CitrusSupport</code> annotation to the test class.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@QuarkusTest</span>
<span class="nd">@CitrusSupport</span>
<span class="kd">class</span> <span class="nc">FoodMarketApplicationTest</span> <span class="o">{</span>
<span class="nd">@CitrusResource</span>
<span class="n">TestCaseRunner</span> <span class="n">t</span><span class="o">;</span>
<span class="nd">@Inject</span>
<span class="n">ObjectMapper</span> <span class="n">mapper</span><span class="o">;</span>
<span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">shouldProcessEvents</span><span class="o">()</span> <span class="o">{</span>
<span class="n">createBooking</span><span class="o">();</span>
<span class="n">createSupply</span><span class="o">();</span>
<span class="n">verifyBookingCompletedEvent</span><span class="o">();</span>
<span class="n">verifyShippingEvent</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The Citrus enabled test is able to inject additional resources such as the <code class="highlighter-rouge">TestCaseRunner</code>.
This runner is the entrance to all Citrus related test actions like send/receive messages or querying and verifying entities in the database.</p>
<p>The test will perform four main actions:</p>
<ul>
<li>Create a booking event</li>
<li>Create a matching supply event</li>
<li>Verify the booking completed event</li>
<li>Verify the shipping event</li>
</ul>
<p>In first version of the test all events will be sent/received via the Kafka message broker.</p>
<h2 id="stage-1-prototyping-the-test">Stage #1: Prototyping the test</h2>
<p>Citrus is able to send and receive messages via Kafka quite easily.
You can use a dynamic endpoint URL (e.g. <code class="highlighter-rouge">kafka:my-topic-name</code>) to exchange data.
The message content (header and body) is given with simple inline Json Strings in this first prototype.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@QuarkusTest</span>
<span class="nd">@CitrusSupport</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">FoodMarketDemoTest</span> <span class="o">{</span>
<span class="nd">@CitrusResource</span>
<span class="n">TestCaseRunner</span> <span class="n">t</span><span class="o">;</span>
<span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">shouldMatchBookingAndSupply</span><span class="o">()</span> <span class="o">{</span>
<span class="n">createBooking</span><span class="o">();</span>
<span class="n">createSupply</span><span class="o">();</span>
<span class="n">verifyBookingCompletedEvent</span><span class="o">();</span>
<span class="n">verifyShippingEvent</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">createBooking</span><span class="o">()</span> <span class="o">{</span>
<span class="n">t</span><span class="o">.</span><span class="na">when</span><span class="o">(</span><span class="n">send</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="s">"kafka:bookings"</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"""
{
"</span><span class="n">client</span><span class="s">": "</span><span class="n">citrus</span><span class="s">",
"</span><span class="n">product</span><span class="s">": {
"</span><span class="n">name</span><span class="s">": "</span><span class="n">Kiwi</span><span class="s">"
},
"</span><span class="n">amount</span><span class="s">": 10,
"</span><span class="n">price</span><span class="s">": 0.99,
"</span><span class="n">shippingAddress</span><span class="s">": "</span><span class="mo">001</span><span class="o">,</span> <span class="n">Foo</span> <span class="n">Blvd</span><span class="o">.</span><span class="s">"
}
"""</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="c1">//...</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The injected Citrus <code class="highlighter-rouge">TestCaseRunner</code> <code class="highlighter-rouge">t</code> is able to use Gherkin Given-When-Then syntax and references the KafkaEndpoint <code class="highlighter-rouge">kafka:bookings</code> in the send operation.
The message body is a simple Json String that represents the booking.</p>
<p>The rest of the story is quite easy.
In the same way we can also send a <code class="highlighter-rouge">supply</code> event and then receive <code class="highlighter-rouge">completed</code> and <code class="highlighter-rouge">shipping</code> events in the test.</p>
<p>When receiving the <code class="highlighter-rouge">completed</code> and <code class="highlighter-rouge">shipping</code> events the test is able to use the Citrus Json validation power coming with the <code class="highlighter-rouge">citrus-validation-json</code> module.
Citrus will compare the received Json object with an expected template and make sure that all fields and properties do match as expected.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">FoodMarketApplicationTest</span> <span class="o">{</span>
<span class="c1">// ...</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">verifyBookingCompletedEvent</span><span class="o">()</span> <span class="o">{</span>
<span class="n">t</span><span class="o">.</span><span class="na">then</span><span class="o">(</span><span class="n">receive</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="s">"kafka:completed?timeout=10000&amp;consumerGroup=citrus-booking"</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">()</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"""
{
"</span><span class="n">client</span><span class="s">": "</span><span class="n">citrus</span><span class="s">",
"</span><span class="n">product</span><span class="s">": "</span><span class="n">Kiwi</span><span class="s">",
"</span><span class="n">amount</span><span class="s">": 10,
"</span><span class="n">status</span><span class="s">": "</span><span class="n">COMPLETED</span><span class="s">"
}
"""</span><span class="o">)</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The Citrus Json validation will now compare the received event with the expected Json object and fail the test when there is a mismatch.</p>
<p><em>Citrus Json validation</em></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="s2">"client"</span><span class="p">:</span><span class="w"> </span><span class="s2">"citrus"</span><span class="p">,</span><span class="w"> </span><span class="s2">"product"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kiwi"</span><span class="p">,</span><span class="w"> </span><span class="s2">"amount"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"COMPLETED"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">compared</span><span class="w"> </span><span class="err">to</span><span class="w">
</span><span class="p">{</span><span class="w"> </span><span class="s2">"client"</span><span class="p">:</span><span class="w"> </span><span class="s2">"citrus"</span><span class="p">,</span><span class="w"> </span><span class="s2">"product"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kiwi"</span><span class="p">,</span><span class="w"> </span><span class="s2">"amount"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"COMPLETED"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>The Json validation is very powerful.
You can ignore properties (expected value set to <code class="highlighter-rouge">@ignore@</code>), use validation matchers, functions and test variables.
A mismatch in the order of elements or some difference in the formatting of the Json document is not failing the test.</p>
<p>In case there is a mismatch you will be provided with an error and the test fails accordingly.</p>
<h2 id="running-the-citrus-tests">Running the Citrus tests</h2>
<p>The Quarkus test framework uses JUnit Jupiter as a test driver.
This means you can run the tests just like any other JUnit test (e.g. from your Java IDE, with Maven).</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./mvnw <span class="nb">test</span>
</code></pre></div></div>
<p>The Citrus test capabilities are added on top of <code class="highlighter-rouge">@QuarkusTest</code> with the <code class="highlighter-rouge">@CitrusSupport</code> annotation.
So you will not need any other configuration to empower the tests with Citrus.</p>
<h2 id="stage-2-use-endpoint-builders-and-domain-model-objects">Stage #2: Use endpoint builders and domain model objects</h2>
<p>Using the dynamic endpoint <code class="highlighter-rouge">kafka:my-topic-name</code> may be a good and easy start for prototyping.
When it comes to writing more tests in your project you may want to leverage a central Kafka endpoint configuration and reuse it in multiple tests.</p>
<p>You can add a <code class="highlighter-rouge">@CitrusConfiguration</code> annotation that loads endpoints from one to many configuration classes.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@QuarkusTest</span>
<span class="nd">@CitrusSupport</span>
<span class="nd">@CitrusConfiguration</span><span class="o">(</span><span class="n">classes</span> <span class="o">=</span> <span class="o">{</span> <span class="n">CitrusEndpointConfig</span><span class="o">.</span><span class="na">class</span> <span class="o">})</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">FoodMarketDemoTest</span> <span class="o">{</span>
<span class="nd">@CitrusResource</span>
<span class="n">TestCaseRunner</span> <span class="n">t</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="n">KafkaEndpoint</span> <span class="n">supplies</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="n">KafkaEndpoint</span> <span class="n">bookings</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="n">KafkaEndpoint</span> <span class="n">completed</span><span class="o">;</span>
<span class="nd">@CitrusEndpoint</span>
<span class="n">KafkaEndpoint</span> <span class="n">shipping</span><span class="o">;</span>
<span class="c1">// code the tests</span>
<span class="o">}</span>
</code></pre></div></div>
<p>In the loaded <code class="highlighter-rouge">CitrusEndpointConfig</code> class the Kafka endpoint instances get configured for all tests that load the configuration.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CitrusEndpointConfig</span> <span class="o">{</span>
<span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">KafkaEndpoint</span> <span class="nf">bookings</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">kafka</span><span class="o">()</span>
<span class="o">.</span><span class="na">asynchronous</span><span class="o">()</span>
<span class="o">.</span><span class="na">topic</span><span class="o">(</span><span class="s">"bookings"</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">KafkaEndpoint</span> <span class="nf">supplies</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">kafka</span><span class="o">()</span>
<span class="o">.</span><span class="na">asynchronous</span><span class="o">()</span>
<span class="o">.</span><span class="na">topic</span><span class="o">(</span><span class="s">"supplies"</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">KafkaEndpoint</span> <span class="nf">shipping</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">kafka</span><span class="o">()</span>
<span class="o">.</span><span class="na">asynchronous</span><span class="o">()</span>
<span class="o">.</span><span class="na">topic</span><span class="o">(</span><span class="s">"shipping"</span><span class="o">)</span>
<span class="o">.</span><span class="na">consumerGroup</span><span class="o">(</span><span class="s">"citrus-shipping"</span><span class="o">)</span>
<span class="o">.</span><span class="na">timeout</span><span class="o">(</span><span class="mi">10000L</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">KafkaEndpoint</span> <span class="nf">completed</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">kafka</span><span class="o">()</span>
<span class="o">.</span><span class="na">asynchronous</span><span class="o">()</span>
<span class="o">.</span><span class="na">topic</span><span class="o">(</span><span class="s">"completed"</span><span class="o">)</span>
<span class="o">.</span><span class="na">consumerGroup</span><span class="o">(</span><span class="s">"citrus-completed"</span><span class="o">)</span>
<span class="o">.</span><span class="na">timeout</span><span class="o">(</span><span class="mi">10000L</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// more endpoints</span>
<span class="o">}</span>
</code></pre></div></div>