-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
2786 lines (2276 loc) · 317 KB
/
feed.xml
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>2025-01-21T08:08:32+01: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">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.5.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>
<p>The configuration class uses the KafkaEndpoint builder and binds the components to the Citrus registry.
With that configuration you can inject the endpoint instances in your test with the <code class="highlighter-rouge">@CitrusEndpoint</code> annotation.</p>
<p>Now you can reference the endpoint in Citrus send/receive test actions.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Product</span> <span class="n">product</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Product</span><span class="o">(</span><span class="s">"Kiwi"</span><span class="o">);</span>
<span class="n">Booking</span> <span class="n">booking</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Booking</span><span class="o">(</span><span class="s">"citrus"</span><span class="o">,</span> <span class="n">product</span><span class="o">,</span> <span class="mi">10</span><span class="o">,</span> <span class="mf">0.99</span><span class="n">D</span><span class="o">,</span> <span class="n">TestHelper</span><span class="o">.</span><span class="na">createShippingAddress</span><span class="o">().</span><span class="na">getFullAddress</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="n">Booking</span> <span class="n">booking</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="n">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="n">marshal</span><span class="o">(</span><span class="n">booking</span><span class="o">))</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Another improvement for the test is to use the domain model object <code class="highlighter-rouge">Booking</code> as a message payload instead of using the inline Json String.</p>
<h2 id="stage-3-add-mail-verification">Stage #3: Add mail verification</h2>
<p>So far the test has been using Kafka endpoints exclusively.
Citrus provides a huge set of components to connect to different messaging transports and technologies.</p>
<p>As a next step the test verifies a mail message that is sent by the Quarkus application when a booking has been completed.</p>
<p>First of all the configuration adds a Citrus mail server.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">MailServer</span> <span class="nf">mailServer</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">mail</span><span class="o">().</span><span class="na">server</span><span class="o">()</span>
<span class="o">.</span><span class="na">port</span><span class="o">(</span><span class="mi">2222</span><span class="o">)</span>
<span class="o">.</span><span class="na">knownUsers</span><span class="o">(</span><span class="n">Collections</span><span class="o">.</span><span class="na">singletonList</span><span class="o">(</span><span class="s">"[email protected]:foodmarket:secr3t"</span><span class="o">))</span>
<span class="o">.</span><span class="na">autoAccept</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span>
<span class="o">.</span><span class="na">autoStart</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The mail server accepts incoming mail requests on port <code class="highlighter-rouge">2222</code> and adds some known users.
The Quarkus application then uses the connection credentials in the <code class="highlighter-rouge">application.properties</code>.</p>
<div class="language-properties highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">quarkus.mailer.mock</span><span class="p">=</span><span class="s">false</span>
<span class="py">quarkus.mailer.own-host-name</span><span class="p">=</span><span class="s">localhost</span>
<span class="py">quarkus.mailer.from</span><span class="p">=</span><span class="s">[email protected]</span>
<span class="py">quarkus.mailer.host</span><span class="p">=</span><span class="s">localhost</span>
<span class="py">quarkus.mailer.port</span><span class="p">=</span><span class="s">2222</span>
<span class="py">quarkus.mailer.username</span><span class="p">=</span><span class="s">foodmarket</span>
<span class="py">quarkus.mailer.password</span><span class="p">=</span><span class="s">secr3t</span>
<span class="py">quarkus.mailer.start-tls</span><span class="p">=</span><span class="s">OPTIONAL</span>
</code></pre></div></div>
<p>The test is able to reference this mail server to verify the mail sent by Quarkus.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">verifyBookingCompletedMail</span><span class="o">(</span><span class="n">Booking</span> <span class="n">booking</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="n">mailServer</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">(</span><span class="n">MailMessage</span><span class="o">.</span><span class="na">request</span><span class="o">()</span>
<span class="o">.</span><span class="na">from</span><span class="o">(</span><span class="s">"[email protected]"</span><span class="o">)</span>
<span class="o">.</span><span class="na">to</span><span class="o">(</span><span class="s">"%[email protected]"</span><span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="n">booking</span><span class="o">.</span><span class="na">getClient</span><span class="o">()))</span>
<span class="o">.</span><span class="na">subject</span><span class="o">(</span><span class="s">"Booking completed!"</span><span class="o">)</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"Hey %s, your booking %s has been completed."</span>
<span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="n">booking</span><span class="o">.</span><span class="na">getClient</span><span class="o">(),</span> <span class="n">booking</span><span class="o">.</span><span class="na">getProduct</span><span class="o">().</span><span class="na">getName</span><span class="o">()),</span> <span class="s">"text/plain"</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">send</span><span class="o">()</span>
<span class="o">.</span><span class="na">endpoint</span><span class="o">(</span><span class="n">mailServer</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">(</span><span class="n">MailMessage</span><span class="o">.</span><span class="na">response</span><span class="o">(</span><span class="mi">250</span><span class="o">)));</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The mail verification includes the validation of all mail related properties (e.g. from, to, subject, body) and the simulation of the mail server response (250 OK).
This is a good point to simulate a mail server error in order to verify the resilience and the error handling on the Quarkus application.</p>
<h2 id="stage-4-use-testbehaviors">Stage #4: Use TestBehaviors</h2>
<p>Citrus has the concept of TestBehaviors to reuse a set of test actions in multiple tests.
The mail verification steps may be added into a behavior so many tests can make use of it.</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">VerifyBookingCompletedMail</span> <span class="kd">implements</span> <span class="n">TestBehavior</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">Booking</span> <span class="n">booking</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">MailServer</span> <span class="n">mailServer</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">VerifyBookingCompletedMail</span><span class="o">(</span><span class="n">Booking</span> <span class="n">booking</span><span class="o">,</span> <span class="n">MailServer</span> <span class="n">mailServer</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">booking</span> <span class="o">=</span> <span class="n">booking</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">mailServer</span> <span class="o">=</span> <span class="n">mailServer</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">apply</span><span class="o">(</span><span class="n">TestActionRunner</span> <span class="n">t</span><span class="o">)</span> <span class="o">{</span>
<span class="n">t</span><span class="o">.</span><span class="na">run</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">mailServer</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">(</span><span class="n">MailMessage</span><span class="o">.</span><span class="na">request</span><span class="o">()</span>
<span class="o">.</span><span class="na">from</span><span class="o">(</span><span class="s">"[email protected]"</span><span class="o">)</span>
<span class="o">.</span><span class="na">to</span><span class="o">(</span><span class="s">"%[email protected]"</span><span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="n">booking</span><span class="o">.</span><span class="na">getClient</span><span class="o">()))</span>
<span class="o">.</span><span class="na">subject</span><span class="o">(</span><span class="s">"Booking completed!"</span><span class="o">)</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="s">"Hey %s, your booking %s has been completed."</span>
<span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="n">booking</span><span class="o">.</span><span class="na">getClient</span><span class="o">(),</span> <span class="n">booking</span><span class="o">.</span><span class="na">getProduct</span><span class="o">().</span><span class="na">getName</span><span class="o">()),</span> <span class="s">"text/plain"</span><span class="o">))</span>
<span class="o">);</span>
<span class="n">t</span><span class="o">.</span><span class="na">run</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">mailServer</span><span class="o">)</span>
<span class="o">.</span><span class="na">message</span><span class="o">(</span><span class="n">MailMessage</span><span class="o">.</span><span class="na">response</span><span class="o">())</span>
<span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The test is able to apply the behavior quite easily.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">verifyBookingCompletedMail</span><span class="o">(</span><span class="n">Booking</span> <span class="n">booking</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">t</span><span class="o">.</span><span class="na">applyBehavior</span><span class="o">(</span><span class="k">new</span> <span class="n">VerifyBookingCompletedMail</span><span class="o">(</span><span class="n">booking</span><span class="o">,</span> <span class="n">mailServer</span><span class="o">)));</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="stage-5-use-the-http-rest-api">Stage #5: Use the Http REST API</h2>
<p>The Quarkus application under test also provides a Http REST API to manage bookings and supplies.</p>
<p>The Citrus test is able to call the REST API with a Http client.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@BindToRegistry</span>
<span class="kd">public</span> <span class="n">HttpClient</span> <span class="nf">foodMarketApiClient</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">http</span><span class="o">().</span><span class="na">client</span><span class="o">()</span>
<span class="o">.</span><span class="na">requestUrl</span><span class="o">(</span><span class="s">"http://localhost:8081"</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">createBooking</span><span class="o">(</span><span class="n">Booking</span> <span class="n">booking</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">http</span><span class="o">()</span>
<span class="o">.</span><span class="na">client</span><span class="o">(</span><span class="n">foodMarketApiClient</span><span class="o">)</span>
<span class="o">.</span><span class="na">send</span><span class="o">()</span>
<span class="o">.</span><span class="na">post</span><span class="o">(</span><span class="s">"/api/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">contentType</span><span class="o">(</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON_VALUE</span><span class="o">)</span>
<span class="o">.</span><span class="na">body</span><span class="o">(</span><span class="n">marshal</span><span class="o">(</span><span class="n">booking</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">http</span><span class="o">()</span>
<span class="o">.</span><span class="na">client</span><span class="o">(</span><span class="n">foodMarketApiClient</span><span class="o">)</span>
<span class="o">.</span><span class="na">receive</span><span class="o">()</span>
<span class="o">.</span><span class="na">response</span><span class="o">(</span><span class="n">HttpStatus</span><span class="o">.</span><span class="na">CREATED</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">extract</span><span class="o">(</span><span class="n">json</span><span class="o">().</span><span class="na">expression</span><span class="o">(</span><span class="s">"$.id"</span><span class="o">,</span> <span class="s">"bookingId"</span><span class="o">))</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The test now sends a Http POST request to create the booking.
The client is able to verify the Http response <code class="highlighter-rouge">201 CREATED</code> and also save the generated booking id for later reference in the test.</p>
<h1 id="stage-6-verify-entities-in-the-database">Stage #6: Verify entities in the database</h1>
<p>The test may also verify the entities saved to the PostgreSQL database.
The <code class="highlighter-rouge">@QuarkusTest</code> dev services is able to inject the dataSource that connects to the PostgreSQL database Testcontainers that is startes as part of the test.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Inject</span>
<span class="n">DataSource</span> <span class="n">dataSource</span><span class="o">;</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">verifyBookingStatus</span><span class="o">(</span><span class="n">Booking</span><span class="o">.</span><span class="na">Status</span> <span class="n">status</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">sql</span><span class="o">()</span>
<span class="o">.</span><span class="na">dataSource</span><span class="o">(</span><span class="n">dataSource</span><span class="o">)</span>
<span class="o">.</span><span class="na">query</span><span class="o">()</span>
<span class="o">.</span><span class="na">statement</span><span class="o">(</span><span class="s">"select status from booking where booking.id=${bookingId}"</span><span class="o">)</span>
<span class="o">.</span><span class="na">validate</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span> <span class="n">status</span><span class="o">.</span><span class="na">name</span><span class="o">())</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The SQL verification uses the extracted <code class="highlighter-rouge">bookingId</code> test variable to identify the entity in the database.
The returned result set gets verified with the expected column values (e.g. <code class="highlighter-rouge">status=COMPLETED</code>)</p>
<h2 id="stage-7-use-openapi-specification">Stage #7: Use OpenAPI specification</h2>
<p>The Quarkus application also exposes its OpenAPI specification for the REST API.
The Citrus test is able to leverage this specification when sending/receiving Http messages.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">final</span> <span class="n">OpenApiSpecification</span> <span class="n">foodMarketSpec</span> <span class="o">=</span>
<span class="n">OpenApiSpecification</span><span class="o">.</span><span class="na">from</span><span class="o">(</span><span class="s">"http://localhost:8081/q/openapi"</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="n">Booking</span> <span class="n">booking</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">openapi</span><span class="o">()</span>
<span class="o">.</span><span class="na">specification</span><span class="o">(</span><span class="n">foodMarketSpec</span><span class="o">)</span>
<span class="o">.</span><span class="na">client</span><span class="o">(</span><span class="n">foodMarketApiClient</span><span class="o">)</span>
<span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="s">"addBooking"</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="n">marshal</span><span class="o">(</span><span class="n">booking</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">openapi</span><span class="o">()</span>
<span class="o">.</span><span class="na">specification</span><span class="o">(</span><span class="n">foodMarketSpec</span><span class="o">)</span>
<span class="o">.</span><span class="na">client</span><span class="o">(</span><span class="n">foodMarketApiClient</span><span class="o">)</span>
<span class="o">.</span><span class="na">receive</span><span class="o">(</span><span class="s">"addBooking"</span><span class="o">,</span> <span class="n">HttpStatus</span><span class="o">.</span><span class="na">CREATED</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">extract</span><span class="o">(</span><span class="n">json</span><span class="o">().</span><span class="na">expression</span><span class="o">(</span><span class="s">"$.id"</span><span class="o">,</span> <span class="s">"bookingId"</span><span class="o">))</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The test loads the OpenAPI specification from the Quarkus application with the URL <code class="highlighter-rouge">http://localhost:8081/q/openapi</code>.
The specification then is used with the Http send test action.</p>
<p>The action references an operation with the id <code class="highlighter-rouge">addBooking</code>.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">openapi</span><span class="pi">:</span> <span class="s">3.0.3</span>
<span class="na">info</span><span class="pi">:</span>
<span class="na">title</span><span class="pi">:</span> <span class="s">citrus-demo-quarkus API</span>
<span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">servers</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">url</span><span class="pi">:</span> <span class="s">http://localhost:8080</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Auto generated value</span>
<span class="pi">-</span> <span class="na">url</span><span class="pi">:</span> <span class="s">http://0.0.0.0:8080</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Auto generated value</span>
<span class="na">paths</span><span class="pi">:</span>
<span class="s">/api/bookings</span><span class="pi">:</span>
<span class="na">post</span><span class="pi">:</span>
<span class="na">operationId</span><span class="pi">:</span> <span class="s">addBooking</span>
<span class="na">requestBody</span><span class="pi">:</span>
<span class="na">content</span><span class="pi">:</span>
<span class="s">application/json</span><span class="pi">:</span>
<span class="na">schema</span><span class="pi">:</span>
<span class="s">$ref</span><span class="pi">:</span> <span class="s1">'</span><span class="s">#/components/schemas/Booking'</span>
<span class="na">responses</span><span class="pi">:</span>
<span class="s2">"</span><span class="s">200"</span><span class="pi">:</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">OK</span>
</code></pre></div></div>
<p>The Citrus test action now leverages information given in the specification such as resource path <code class="highlighter-rouge">/api/bookings</code> and the content type <code class="highlighter-rouge">application/json</code>.</p>
<h1 id="stage-8-ui-testing-with-selenium">Stage #8: UI testing with Selenium</h1>
<p>Citrus also integrates with Selenium UI testing.
This means that the test is able to open the browser and navigate to the Quarkus application home URL.
Then the test may simulate user interactions such as clicking on links and buttons on the page.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">approveBooking</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">selenium</span><span class="o">()</span>
<span class="o">.</span><span class="na">browser</span><span class="o">(</span><span class="n">browser</span><span class="o">)</span>
<span class="o">.</span><span class="na">start</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">doFinally</span><span class="o">().</span><span class="na">actions</span><span class="o">(</span>
<span class="n">selenium</span><span class="o">()</span>
<span class="o">.</span><span class="na">browser</span><span class="o">(</span><span class="n">browser</span><span class="o">)</span>
<span class="o">.</span><span class="na">stop</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">selenium</span><span class="o">()</span>
<span class="o">.</span><span class="na">browser</span><span class="o">(</span><span class="n">browser</span><span class="o">)</span>
<span class="o">.</span><span class="na">navigate</span><span class="o">(</span><span class="s">"http://localhost:8081"</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">delay</span><span class="o">().</span><span class="na">seconds</span><span class="o">(</span><span class="mi">3</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">selenium</span><span class="o">()</span>
<span class="o">.</span><span class="na">browser</span><span class="o">(</span><span class="n">browser</span><span class="o">)</span>
<span class="o">.</span><span class="na">click</span><span class="o">()</span>
<span class="o">.</span><span class="na">element</span><span class="o">(</span><span class="s">"id"</span><span class="o">,</span> <span class="s">"${bookingId}"</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="summary">Summary</h2>
<p>The sample application has shown how you can integrate Citrus within a Quarkus test.
The Quarkus test is able to use the Citrus capabilities because of the Citrus Quarkus extension that is provided since Citrus 4.0.</p></content><author><name></name></author></entry><entry><title type="html">Citrus 4.0.0</title><link href="http://0.0.0.0:4000/news/2023/10/25/release-4.0.0/" rel="alternate" type="text/html" title="Citrus 4.0.0" /><published>2023-10-25T00:00:00+02:00</published><updated>2023-10-25T00:00:00+02:00</updated><id>http://0.0.0.0:4000/news/2023/10/25/release-4.0.0</id><content type="html" xml:base="http://0.0.0.0:4000/news/2023/10/25/release-4.0.0/"><p>The community is proud to announce the availability of Citrus 4.0.0!</p>
<h2 id="objectives">Objectives</h2>
<p>Citrus 4.0 is a major release mainly driven by the industry migrating from <code class="highlighter-rouge">javax.*</code> to <code class="highlighter-rouge">jakarta.*</code> APIs. The new version uses Java 17 and aligns with popular Java enterprise libraries such as Spring Boot and Quarkus. Citrus 4 uses updated versions of many dependency libraries (e.g. Spring 6, Apache Camel 4, Cucumber 7, Selenium 4, Jakarta EE and many more). The proper alignment with those libraries and the evolving of Java’s ecosystem in general is a key driver for Citrus 4.</p>
<p>Of course the new major version also addresses many improvements and fixes. That being said we try to keep breaking changes on a low-level in order to provide a smooth migration from former versions. Due to the breaking changes in the many <code class="highlighter-rouge">javax</code> APIs this might not always be possible though. People coming from Citrus 3.x should have a look at the <a href="https://github.com/citrusframework/citrus/wiki/migration-guide:-citrus-3.x-to-4.x">3.x migration guide</a>.</p>
<p>Here are the main objectives we have with Citrus 4.0</p>
<ul>
<li>Java 17</li>
<li>Move to groupId <code class="highlighter-rouge">org.citrusframework</code></li>
<li>Update dependencies to latest major versions</li>
<li>Move from <code class="highlighter-rouge">javax.*</code> to <code class="highlighter-rouge">jakarta.*</code></li>
<li>Polyglot test runner support (XML, Groovy and YAML)</li>
<li>QuarkusTest runtime support</li>
<li>Remove deprecated modules and code</li>
</ul>
<h2 id="java-17">Java 17</h2>
<p>The Citrus 4.0 bits are compiled with Java 17 (was Java 11 for Citrus 3.x). This means you need to use Java 17 as a minimum to run Citrus.</p>
<p>We recommend to use the following setup:</p>
<ul>
<li>Java 17+</li>
<li>Maven 3.9+</li>
<li>As runtime one of
<ul>
<li>JUnit Jupiter 5.10+</li>
<li>TestNG 7.8+</li>
<li>Cucumber 7.14+</li>
<li>JUnit 4.13+</li>
<li>Quarkus Test 3.4+</li>
</ul>
</li>
</ul>
<p>Of course this also means that you are able to use the full greatness of evolving Java features such as records, multiline text blocks and so many more.</p>
<h2 id="maven-groupid-orgcitrusframework">Maven groupId “org.citrusframework”</h2>
<p>Citrus is an Open Source project and promotes the idea of open code and community contributions. For over a decade Citrus has been using the groupId <code class="highlighter-rouge">com.consol.citrus</code> because the company <a href="https://www.consol.com/">ConSol Software GmbH</a> and its employees have been a driving force in creating and maintaining the project since the very beginning.</p>
<p>Still ConSol plays a significant role in the success of Citrus by adapting and promoting the project in so many ways.
However, the list of new contributors emerges and Citrus benefits from many community contributions these days.</p>
<p>For the sake of strengthening the Open Source idea we decided to move the Citrus groupId from <code class="highlighter-rouge">com.consol.citrus</code> to a more generic <code class="highlighter-rouge">org.citrusframework</code> from Citrus 4.0 onwards.</p>
<p>With the move to <code class="highlighter-rouge">org.citrusframework</code> we decided to happily comply with the Open Source nature of the project because nowadays, the project receives valuable contributions from many companies.</p>
<p>Please update the groupId in your Maven <code class="highlighter-rouge">pom.xml</code> accordingly when updating to Citrus 4.0.</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>4.5.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>
</code></pre></div></div>
<p>Since the groupId also represents the basic Java package used in Citrus the packages of all Citrus Java classes have also changed from <code class="highlighter-rouge">com.consol.citrus</code> to <code class="highlighter-rouge">org.citrusframework</code> so please replace all occurrences and imports accordingly as you update.</p>
<p>Please do not worry as it should be nothing more than a search-and-replace operation in your project codebase in order to use the new base package wherever it may be referenced.</p>
<h2 id="dependency-update">Dependency update</h2>
<p>Citrus depends on many fantastic libraries and projects. With the move to the Jakarta API many of them hava had major version releases in 2023. Citrus now aligns with the latest and greatest versions of these libraries.</p>
<p>The new library versions used in Citrus 4.0 are:</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">JUnit Jupiter</td>
<td style="text-align: left">5.10.0</td>
<td style="text-align: left">TestNG</td>
<td style="text-align: left">7.8.0</td>
</tr>
<tr>
<td style="text-align: left">Cucumber</td>
<td style="text-align: left">7.14.0</td>
<td style="text-align: left">Quarkus Test</td>
<td style="text-align: left">3.4.2</td>
</tr>
<tr>
<td style="text-align: left">Apache Camel</td>
<td style="text-align: left">4.1.0</td>
<td style="text-align: left">ActiveMQ Artemis</td>
<td style="text-align: left">2.31.0</td>
</tr>
<tr>
<td style="text-align: left">ActiveMQ</td>
<td style="text-align: left">5.18.2</td>
<td style="text-align: left">Jetty</td>
<td style="text-align: left">11.0.17</td>
</tr>
<tr>
<td style="text-align: left">HttpClient</td>
<td style="text-align: left">5.2.1</td>
<td style="text-align: left">Cucumber</td>
<td style="text-align: left">7.14.0</td>
</tr>
<tr>
<td style="text-align: left">Knative Client</td>
<td style="text-align: left">6.9.0</td>
<td style="text-align: left">Kubernetes Client</td>
<td style="text-align: left">6.9.0</td>
</tr>
<tr>
<td style="text-align: left">Netty</td>
<td style="text-align: left">4.1.100.Final</td>
<td style="text-align: left">SLF4J</td>
<td style="text-align: left">2.0.9</td>
</tr>
<tr>
<td style="text-align: left">SnakeYAML</td>
<td style="text-align: left">2.2</td>
<td style="text-align: left">Spring Framework</td>
<td style="text-align: left">6.0.13</td>
</tr>
<tr>
<td style="text-align: left">Spring WS</td>
<td style="text-align: left">4.0.7</td>
<td style="text-align: left">Spring Integration</td>
<td style="text-align: left">6.1.4</td>
</tr>
<tr>
<td style="text-align: left">Testcontainers</td>
<td style="text-align: left">1.19.1</td>
<td style="text-align: left">PostrgeSQL</td>
<td style="text-align: left">42.6.0</td>
</tr>
<tr>
<td style="text-align: left">GreenMail</td>
<td style="text-align: left">2.0.0</td>
<td style="text-align: left">Jakarta JMS API</td>
<td style="text-align: left">3.1.0</td>
</tr>
<tr>
<td style="text-align: left">Jakarta Validation</td>
<td style="text-align: left">3.0.2</td>
<td style="text-align: left">Jakarta Websocket API</td>
<td style="text-align: left">2.1.1</td>
</tr>
<tr>
<td style="text-align: left">Jakarta Servlet API</td>
<td style="text-align: left">6.0.0</td>
<td style="text-align: left">Saaj</td>
<td style="text-align: left">3.0.3</td>
</tr>
<tr>
<td style="text-align: left">JaxB</td>
<td style="text-align: left">4.0.3</td>
<td style="text-align: left">Kafka</td>
<td style="text-align: left">3.6.0</td>
</tr>
<tr>
<td style="text-align: left">SLF4J</td>
<td style="text-align: left">2.0.9</td>
<td style="text-align: left">Snakeyaml</td>
<td style="text-align: left">2.2</td>
</tr>
</tbody>
</table>
<h2 id="move-to-jakarta-api">Move to Jakarta API</h2>
<p>Many libraries accomplish the switch from <code class="highlighter-rouge">javax.*</code> to <code class="highlighter-rouge">jakarta.*</code> APIs. The migration itself can be a tough task and the Citrus team is happy to have tackled this move with the new major version update.</p>
<p>For you as a user this move may not cause much trouble because the Citrus dependencies are all using the new Jakarta APIs already. In case you are using Citrus in combination with an older servlet container or with a library that still uses the <code class="highlighter-rouge">javax</code> APIs it may be time to also update these dependencies in your project.</p>
<h2 id="polyglot-test-runner-support">Polyglot test runner support</h2>
<p>Citrus provides many ways to write test cases. The most popular ones are using the Java DSL and the Spring bean XML syntax. In addition to that you can now choose to write Citrus tests in pure XML, YAML or Groovy. All of them share the same feature set, so you can adapt the test language to your individual requirements (not everybody is a Java expert and this is fine).</p>
<h2 id="quarkustest-runtime">QuarkusTest runtime</h2>
<p>Quarkus is an emerging enterprise Java stack that is keen to bring Java as a leading platform into the cloud-native application future. With Citrus 4 you can directly combine the test framework with the test capabilities of Quarkus. The Quarkus test framework provides awesome features such as dev services, auto configuration and first class integration with Testcontainers.</p>
<p>Citrus now provides a Quarkus extension that is able to hook into the QuarkusTest runtime so you can inject Citrus capabilities into your Quarkus test.</p>
<p>Read more about it in <a href="https://citrusframework.org/citrus/reference/html/index.html#runtime-quarkus">docs/reference/index.html#runtime-quarkus</a>.</p>
<h2 id="removed-modules">Removed modules</h2>
<p>Sometimes it is the time to say goodbye and a new major version is always a good point to get rid of obsolete and outdated things. Some Citrus modules are declared “end of life” and are being discontinued, some modules will just be moved to a separate code repository.</p>
<p>These modules have been removed from the main Citrus project.</p>
<ul>
<li><strong>citrus-java-dsl</strong> deprecated Java domain specific language that was based on the TestDesigner interface</li>
<li><strong>citrus-arquillian</strong> Arquillian integration to run Citrus as part of the Arquillian framework</li>
<li><strong>citrus-jdbc</strong> JDBC server implementation that is able to simulate any JDBC driver and database</li>
<li><strong>citrus-remote</strong> Run Citrus via remote Http tunnel on a foreign server</li>
</ul>
<p>Removing the modules does not mean that these bits are completely gone forever though. Some modules have just been moved out of the Citrus project into new separate code repositories. This is mainly because these modules do have experimental status and updating to Jakarta EE and Java 17 has not been possible at the moment.</p>
<p>The experimental modules moved to separate repositories are:</p>
<ul>
<li><em>citrus-remote</em> lives on in <a href="https://github.com/citrusframework/citrus-remote">https://github.com/citrusframework/citrus-remote</a></li>
<li><em>citrus-jdbc</em> endpoint module has been moved to <a href="https://github.com/citrusframework/citrus-db">https://github.com/citrusframework/citrus-db</a></li>
</ul>
<p>The code and functionality of those modules stays the same. Also, the code has already received updates on the major releases such as Spring 6.</p>
<p>It is up to the community though to decide about the future maintenance and release cadence as these modules will not be released automatically with Citrus main project anymore.</p>
<h2 id="provide-feedback-and-help">Provide feedback and help</h2>
<p>Please adapt to using Citrus 4 as soon as possible and give us feedback what is working and what may need more attention.
We would love to hear from you how you like Citrus 4! Also the documentation has not been fully updated yet. Any help is much appreciated! So in case you have something to improve of fix just open a PR we are more than happy to receive your contributions!</p>
<h2 id="thank-you">Thank you!</h2>
<p>Let’s use this opportunity to thank everybody involved in making this Citrus 4 release happen. The Citrus community did a tremendous job to brings this all together! The Citrus team is very proud to have a new version of Citrus available that after 16+ years of development still is up-to-date with the latest and greatest software libraries out there! THANK YOU!</p></content><author><name>Christoph Deppisch</name></author></entry><entry><title type="html">Testing Camel K with YAKS</title><link href="http://0.0.0.0:4000/news/2023/01/30/camel-k-yaks/" rel="alternate" type="text/html" title="Testing Camel K with YAKS" /><published>2023-01-30T00:00:00+01:00</published><updated>2023-01-30T00:00:00+01:00</updated><id>http://0.0.0.0:4000/news/2023/01/30/camel-k-yaks</id><content type="html" xml:base="http://0.0.0.0:4000/news/2023/01/30/camel-k-yaks/"><p>This post describes the steps to test a Camel K integration with YAKS both locally and on the Kubernetes platform.</p>
<p><img src="/img/assets/camel-k-yaks/featured.png" alt="featured.png" /></p>
<h1 id="what-is-yaks">What is YAKS?</h1>
<p><a href="https://github.com/citrusframework/yaks">YAKS</a> is an Open Source test automation platform that leverages Behavior Driven
Development concepts for running tests locally and on Cloud infrastructure (e.g. <a href="https://kubernetes.io/">Kubernetes</a> or
<a href="https://www.openshift.com/">OpenShift</a>).
This means that the testing tool is able to run your tests both as local tests and natively on Kubernetes.
The framework is specifically designed to verify Serverless and Microservice applications and aims for integration testing
with the application under test up and running in a production-like environment.
A typical YAKS test uses the very same infrastructure as the application under test and exchanges data/events over different
messaging transports (e.g. Http REST, Knative eventing, Kafka, JMS and many more).</p>
<p>As YAKS itself is written in Java the runtime uses a Java virtual machine with build tools such as Maven and integrates with
well known Java testing frameworks such as <a href="https://junit.org/junit5/">JUnit</a>, <a href="https://cucumber.io/">Cucumber</a> and
<a href="https://citrusframework.org">Citrus</a> to run the tests.</p>
<h1 id="understanding-the-camel-k-example">Understanding the Camel K example</h1>
<p>First of all here is a small sample Camel K integration that we would like to test in the following.
The integration exposes a Http service to the user.
The service accepts client Http POST requests that add fruit model objects.
The Camel K route applies content based routing to store the fruits in different AWS S3 buckets.</p>
<p><img src="/img/assets/camel-k-yaks/test-scenario.png" alt="test-scenario.png" /></p>
<p>In the test scenario YAKS is going to invoke the Camel K service and verify that the message content has been sent to the right AWS S3 bucket.</p>
<p>Here is a sample fruit model object that is subject to be stored in AWS S3:</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">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="p">,</span><span class="w">
</span><span class="s2">"name"</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">"category"</span><span class="p">:{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="s2">"tropical"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"nutrition"</span><span class="p">:{</span><span class="w">
</span><span class="s2">"calories"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w">
</span><span class="s2">"sugar"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="w">
</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">"AVAILABLE"</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">1.59</span><span class="p">,</span><span class="w">
</span><span class="s2">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"sweet"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Here is the Camel K integration route:</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">from</span><span class="o">(</span><span class="s1">'platform-http:/fruits'</span><span class="o">)</span>
<span class="o">.</span><span class="na">log</span><span class="o">(</span><span class="s1">'received fruit ${body}'</span><span class="o">)</span>
<span class="o">.</span><span class="na">unmarshal</span><span class="o">().</span><span class="na">json</span><span class="o">()</span>
<span class="o">.</span><span class="na">removeHeaders</span><span class="o">(</span><span class="s2">"*"</span><span class="o">)</span>
<span class="o">.</span><span class="na">setHeader</span><span class="o">(</span><span class="s2">"CamelAwsS3Key"</span><span class="o">,</span> <span class="n">constant</span><span class="o">(</span><span class="s2">"fruit.json"</span><span class="o">))</span>
<span class="o">.</span><span class="na">choice</span><span class="o">()</span>
<span class="o">.</span><span class="na">when</span><span class="o">().</span><span class="na">simple</span><span class="o">(</span><span class="s1">'${body[nutrition][sugar]} &lt;= 5'</span><span class="o">)</span>
<span class="o">.</span><span class="na">setHeader</span><span class="o">(</span><span class="s2">"CamelAwsS3BucketName"</span><span class="o">,</span> <span class="n">constant</span><span class="o">(</span><span class="s2">"low-sugar"</span><span class="o">))</span>
<span class="o">.</span><span class="na">when</span><span class="o">().</span><span class="na">simple</span><span class="o">(</span><span class="s1">'${body[nutrition][sugar]} &gt; 5 &amp;&amp; ${body[nutrition][sugar]} &lt;= 10'</span><span class="o">)</span>
<span class="o">.</span><span class="na">setHeader</span><span class="o">(</span><span class="s2">"CamelAwsS3BucketName"</span><span class="o">,</span> <span class="n">constant</span><span class="o">(</span><span class="s2">"medium-sugar"</span><span class="o">))</span>
<span class="o">.</span><span class="na">otherwise</span><span class="o">()</span>
<span class="o">.</span><span class="na">setHeader</span><span class="o">(</span><span class="s2">"CamelAwsS3BucketName"</span><span class="o">,</span> <span class="n">constant</span><span class="o">(</span><span class="s2">"high-sugar"</span><span class="o">))</span>
<span class="o">.</span><span class="na">end</span><span class="o">()</span>
<span class="o">.</span><span class="na">marshal</span><span class="o">().</span><span class="na">json</span><span class="o">()</span>
<span class="o">.</span><span class="na">log</span><span class="o">(</span><span class="s1">'sending ${body}'</span><span class="o">)</span>
<span class="o">.</span><span class="na">to</span><span class="o">(</span><span class="s2">"aws2-s3://noop?$parameters"</span><span class="o">)</span>
</code></pre></div></div>
<p>The route uses content based routing EAP based on the nutrition sugar rating of a given fruit in order to send the fruits
to different AWS S3 buckets (low-sugar, medium-sugar, high-sugar).</p>
<p>In the following the test case for this integration needs to invoke the exposed service with different fruits and verify its outcome on AWS S3.</p>
<h1 id="how-to-test-locally-with-yaks">How to test locally with YAKS</h1>
<p>In the beginning let’s just write the test and run it locally.
For now, we do not care how to deploy the application under test in the Cloud infrastructure as everything is running on
the local machine using <a href="https://www.jbang.dev/">JBang</a>.</p>
<p>JBang is a fantastic way to just start coding and running Java code and also Camel K integrations.</p>
<p>YAKS as a framework brings a set of ready-to-use domain specific languages (XML, YAML, Groovy, BDD Cucumber steps) for writing
tests in order to verify your deployed services.</p>
<p>This post uses the Behavior Driven Development integration via Cucumber.
So the YAKS test is a single feature file that uses BDD Gherkin syntax like this:</p>
<div class="language-gherkin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Feature</span><span class="p">:</span> Camel K Fruit Store
<span class="kn">Background</span><span class="p">:</span>
<span class="err">Given URL</span><span class="p">:</span> <span class="err">http</span><span class="p">:</span><span class="err">//localhost</span><span class="p">:</span><span class="err">8080</span>
<span class="kn">Scenario</span><span class="p">:</span> Create infrastructure
<span class="c"># Start AWS S3 container</span>
<span class="nf">Given </span>Enable service S3
<span class="nf">Given </span>start LocalStack container
<span class="c"># Create Camel K integration</span>
<span class="nf">Given </span>Camel K integration property file aws-s3-credentials.properties
<span class="nf">When </span>load Camel K integration fruit-service.groovy
<span class="err">Then Camel K integration fruit-service should print Started route1 (platform-http</span><span class="p">:</span><span class="err">///fruits)</span>
<span class="kn">Scenario</span><span class="p">:</span> Verify fruit service
<span class="c"># Invoke Camel K service</span>
<span class="err">Given HTTP request body</span><span class="p">:</span> <span class="err">yaks</span><span class="p">:</span><span class="err">readFile('pineapple.json')</span>
<span class="nf">And </span>HTTP request header Content-Type="application/json"
<span class="nf">When </span>send POST /fruits
<span class="nf">Then </span>receive HTTP 200 OK
<span class="c"># Verify uploaded S3 file</span>
<span class="nf">Given </span>New global Camel context
<span class="nf">Given </span>load to Camel registry amazonS3Client.groovy
<span class="nf">Given </span>Camel exchange message header CamelAwsS3Key="fruit.json"
<span class="err">Given receive Camel exchange from("aws2-s3</span><span class="p">:</span><span class="err">//medium-sugar?amazonS3Client=#amazonS3Client&amp;deleteAfterRead=true") with body</span><span class="p">:</span> <span class="err">yaks</span><span class="p">:</span><span class="err">readFile('pineapple.json')</span>
</code></pre></div></div>
<p>Let’s walk through the test step by step.
First of all the feature file uses the usual Given-When-Then BDD syntax to give context, describe the actions and verify the outcome.
Each step calls a specific YAKS action that is provided out of the box by the framework.
The user is able to choose from a <a href="https://github.com/citrusframework/yaks/tree/main/java/steps">huge set of steps</a> that
automatically perform actions like sending/receiving Http requests/responses, starting <a href="https://www.testcontainers.org/">Testcontainers</a>,
running Camel routes, connecting to a database, publishing events on Kafka or Knative brokers and many more.</p>
<p>In the first scenario the test automatically prepares some required infrastructure.
The YAKS test starts a Localstack <a href="https://www.testcontainers.org/">Testcontainer</a> to have an AWS S3 test instance running (<code class="highlighter-rouge">Given start LocalStack container</code>).
Then the test loads and starts the Camel K integration under test (<code class="highlighter-rouge">When load Camel K integration fruit-service.groovy</code>) and waits for it to properly start.
In local testing this step starts the Camel K integration using JBang.
Later the post will also run the test in a Kubernetes environment.</p>
<p>Now the infrastructure is up and running and the test is able to load the fruit model object as Http request body
(<code class="highlighter-rouge">Given HTTP request body: yaks:readFile('pineapple.json')</code>) and invoke the Camel K service (<code class="highlighter-rouge">When send POST /fruits</code>).
The test waits for the Http response and verifies its 200 OK status.</p>
<p>In the last step the test verifies that the fruit object has been added to the right AWS S3 bucket (medium-sugar).
As YAKS itself is not able to connect to AWS S3 the test uses Apache Camel for this step.
The test creates a Camel context, loads a AWS client and connects to AWS S3 with a temporary Camel route
(<code class="highlighter-rouge">Given receive Camel exchange from("aws2-s3://medium-sugar?amazonS3Client=#amazonS3Client&amp;deleteAfterRead=true")</code>).
With this Apache Camel integration YAKS is able to use the complete 300+ Camel components for sending and receiving messages
to various messaging transports. The Camel exchange body should be the same fruit model object (<code class="highlighter-rouge">yaks:readFile('pineapple.json'</code>)
as posted in the initial Http request.</p>
<p>YAKS uses the powerful message payload validation capabilities provided by Citrus for this message content verification.
The validation is able to compare message contents of type XML, Json, plaintext and many more.</p>
<p>This completes the test case. You can now run this test with Cucumber and JUnit for instance.
The easiest way though to directly run tests with YAKS is to use the <a href="https://github.com/citrusframework/yaks/releases">YAKS command line client</a>.
You do not need to set up a whole project with Maven dependencies and so on.
Just write the test file and run with:</p>
<p>```shell script
$ yaks run fruit-service.feature –local</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
You should see some log output like this:
</code></pre></div></div>
<p>INFO |
INFO | ————————————————————————
INFO | .__ __
INFO | <strong>__ |</strong>|/ |<strong>__</strong>__ __ __ <strong>__</strong>
INFO | _/ <strong><em>| \ ___ __ \ | \/ __</em>/
INFO | \ _</strong>| || | | | \/ | /___ \
INFO | ___ &gt;<strong>||</strong>| |<strong>| |</strong><strong>//</strong>__ &gt;
INFO | \/ \/
INFO |
INFO | C I T R U S T E S T S 3.4.0
INFO |
INFO | ————————————————————————
INFO |</p>
<p>Scenario: Create infrastructure # fruit-service.feature:6
Given URL: http://localhost:8080
Given Enable service S3
[…]</p>
<p>Scenario: Verify fruit service # fruit-service.feature:20
Given URL: http://localhost:8080
Given HTTP request body: yaks:readFile(‘pineapple.json’)
[…]</p>
<p>Scenario: Remove infrastructure # fruit-service.feature:31
Given URL: http://localhost:8080
Given delete Camel K integration fruit-service
Given stop LocalStack container</p>
<p>3 Scenarios (3 passed)
18 Steps (18 passed)
0m18,051s</p>
<table>
<tbody>
<tr>
<td>INFO</td>
<td>————————————————————————</td>
</tr>
<tr>
<td>INFO</td>
<td> </td>
</tr>
<tr>
<td>INFO</td>
<td>CITRUS TEST RESULTS</td>
</tr>
<tr>
<td>INFO</td>
<td> </td>
</tr>
<tr>
<td>INFO</td>
<td>Create infrastructure …………………………………… SUCCESS</td>
</tr>
<tr>
<td>INFO</td>
<td>Verify fruit service ……………………………………. SUCCESS</td>