table of contents - citrus...

563

Upload: others

Post on 21-May-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move
Page 2: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

0

1

2

3

4

5

6

7

8

9

10

10.1

10.2

10.3

10.4

10.5

10.6

10.7

11

12

13

13.1

13.2

13.3

13.4

13.5

13.6

TableofContentsIntroduction

Preface

Changesnew

Introduction

Setup

Testcase

Testvariables

Run

Configuration

Endpoints

Validation

Xml

Schema

Json

Xhtml

Plaintext

Binary

Gzip

Xpath

JsonPath

Actions

Send

Receive

Database

Sleep

Java

Timeout

CitrusReferenceGuide

2

Page 3: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

13.7

13.8

13.9

13.10

13.11

13.12

13.13

13.14

13.15

13.16

13.17

13.18

13.19

13.20

13.21

13.22

13.23

13.24

13.25

14

15

16

16.1

16.2

16.3

16.4

16.5

16.6

16.7

16.8

Echo

Stoptime

Createvariables

Trace

Transform

Groovy

Fail

Input

Load

Wait

PurgeJMSqueues

Purgechannels

Purgeendpoints

Assert

Catch

Antrun

Manageserver

Stoptimer

Genericaction

Templates

Testbehaviors

Containers

Sequential

Conditional

Parallel

Iterate

Repeat

RepeatOnError

Timer

Custom

CitrusReferenceGuide

3

Page 4: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

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

Finally

Jms

Http

HttpWebsockets

Soap

Ftp

Messagechannel

File

Camel

Vertx

Mail

Arquillian

Docker

Kubernetes

Ssh

Rmi

Jmx

Cucumber

Zookeeper

Restdocs

Selenium

Endpointcomponent

Endpointadapter

Functions

ValidationMatchers

Datadictionary

Testactors

Testsuite

Metainfo

Messagetracing

CitrusReferenceGuide

4

Page 5: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

47

48

48.1

49

49.1

49.2

49.3

49.4

49.5

49.6

49.7

49.8

49.9

49.10

Reporting

Samples

FlightBookingSample

Appendix

Changes2.6

Changes2.5

Changes2.4

Changes2.3

Changes2.2

Changes2.1

Changes2.0

Changes1.4

Changes1.3

Changes1.2

CitrusReferenceGuide

5

Page 6: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusFramework-ReferenceDocumentation

AuthorsChristophDeppisch,MartinMaher

Version2.7

Copyright©2017ConSolSoftwareGmbH

www.citrusframework.org

CitrusReferenceGuide

6Introduction

Page 7: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Preface

Integrationtestingcanbeveryhard,especiallywhenthereisnosufficienttoolsupport.UnittestingisflavoredwithfantastictoolsandAPIslikeJUnit,TestNG,EasyMock,Mockitoandsoon.Thesetoolssupportyouinwritingautomatedtests.Atesterwhoisinchargeofintegrationtestingmaylackoftoolsupportforautomatedtestingespeciallywhenitcomestosimulatemessaginginterfaces.

Inatypicalenterpriseapplicationscenariothetestteamhastodealwithdifferentmessaginginterfacesandvarioustransportprotocols.Withoutsufficienttoolsupporttheautomatedintegrationtestingofmessage-basedinteractionsbetweeninterfacepartnersisexhaustingandsometimesbarelypossible.

Thetesterisforcedtosimulateseveralinterfacepartnersinanend-to-endintegrationtest.Thefirstthingthatcomestoourmindismanualtesting.Nodoubtmanualtestingisfast.Inlongtermperspectivemanualtestingistimeconsumingandcausessevereproblemsregardingmaintainabilityastheyareerrorproneandnotrepeatable.

TheCitrusframeworkgivesacompletetestautomationtoolforintegrationtestingofenterpriseapplications.Youcantestyourmessageinterfacestootherapplicationsasclientandserver.EverytimeacodechangeappliesallautomatedCitrustestsensurethestabilityofinterfacesandmessagecommunication.

RegressiontestingandcontinuousintegrationisveryeasyasCitrusfitsintoyourbuildlifecylceasusualJavaunittest.YoucanuseCitruswithJUnitorTestNGinordertointegratewithyourapplicationbuild.

WithpowerfulvalidationcapabilitiesforvariousmessageformatslikeXML,CSVorJSONCitrusisdesignedtoprovidefullyautomatedintegrationtestsforend-to-endusecases.Citruseffectivelycomposescomplexmessagingusecaseswithresponsegeneration,errorsimulation,databaseinteractionandmore.

ThisdocumentationprovidesareferenceguidetoallfeaturesoftheCitrustestframework.Itgivesadetailedpictureofeffectiveintegrationtestingwithautomatedintegrationtestenvironments.Sincethisdocumentisconsideredtobeunderconstruction,pleasedonothesitatetogiveanycommentsorrequeststoususingouruserorsupportmailinglists.

CitrusReferenceGuide

7Preface

Page 8: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

What'snewinCitrus2.7?!Citrus2.7isusingJava8!TheCitrussourcesarecompiledwithJava8whichmeansthatfromnowonyouneedatleastJava8runtimetoworkwithCitrus.WiththisJava8baseCitrusisproudtowelcometwonewcrewmembersforsupportingSeleniumandKubernetesintests.Notenoughwehavethefollowingfeaturesincludedinthebox.

Java8

CitrusisnowusingJava8.ThisismainlybecauseweneedtomoveoninusinglatestversionsofSpringFramework,ApacheCamelandsoon.IfyouarestillstuckonJava7youcannotupdateto2.7astheCitrussourcesarecompiledwithJava8.PleasecontactusincaseyoureallycannotupdatetoJava8inyourproject.WecanthinkofaminorbugfixversionwithCitrus2.6basethatstillsupportsJava7runtime.OnthebrightsidewecannowusethefullpowerofLambdaexpressionsandotherJava8featuresinCitruscodebase.

Kubernetessupport

CitrusisnowabletointeractwithKubernetesremoteAPIinordertomanagepods,servicesandotherresourcesontheKubernetesplatform.TheKubernetesclientisbasedontheFabric8JavaclientthatinteractswiththeKubernetesAPIviaRESTservices.SoyoucanaccessKubernetesresourceswithinCitrusinordertochangeorvalidatetheresourcestateforcontainerizedtesting.Thisisveryusefulwhendealingwithcontainerapplicationenvironmentsaspartoftheintegrationtests.PleasestaytunedforblogpostsandtutorialsamplesonhowCitruscanhelpyoutestMicroserviceswithDockerandKubernetes.Thebasicusageisdescribedinsectionkubernetes.

Seleniumsupport

UserinterfaceandbrowsertestinghasnotbeenafocuswithinCitrusintegrationtestinguntilnowthatwecanintegratewiththefamousSeleniumUItestinglibrary.Thankstothegreatcontributionsmadebythecommunity-especiallybyvdsrd@github-wecanuseSeleniumbasedactionsandfeaturesdirectlyinaCitrustestcase.TheCitrusJavaandXMLDSLbothprovidecomfortableaccesstotheSeleniumAPIinordertosimulate

CitrusReferenceGuide

8Changesnew

Page 9: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

userinteractionwithinabrowser.ThemixofuserbasedactionsandCitrusmessagingtransportsimulationgivescompletenewwaysofhandlingcomplexintegrationscenarios.ReadmoreaboutthisinchapterSelenium.

Environmentbasedbefore/aftersuite

Youcanenable/disablebeforeandaftersuiteactionsbasedonoptionalenvironmentorsystemproperties.Userscangivepropertynamesorpropertyvaluesthatarecheckedbeforeexecution.Onlyincasetheenvironmentpropertychecksdopasstheactionsareexecutedbefore/afterthetestsuiterun.

WsAddressingheadercustomization

WehaveimprovedtheheadercustomizationoptionswhenusingSOAPWSAddressingfeature.YoucannowoverwritethedefaultWSAddressingheaderspertestactioninadditiontodefiningtheheadersonclientendpointcomponentlevel.

JsonPathdatadictionary

Jsondatadictionarywasbasedonasimpledotnotatedsyntax.NowyoucanalsousemorecomplexJsonPathexpressionsinordertooverwriteelementsinJsonmessagesbasedonthedatadictionarysettingsinCitrus.Readmoreaboutthatinchapterdata-dictionary.

JavaDSLtestbehavior

TestbehaviorsinJavaDSLrepresenttemplatesinXMLDSL.ThebehaviorencapsulatesasetoftestactionstoagroupthatcanbeappliedtomultipleJavaDSLtests.ThisenablesyoutocombinecommontestactionsinJavaDSLwithmorecomfortablereuseoftestactiondefinitions.Seechaptertest-behaviorshowtousethat.

Refactoring

DeprecatedAPIsandclassesthatcoexistedalongtimearenowremoved.Ifyourprojectisusingonofthesedeprecatedclassesyoumayrunintocompiletimeerrors.PleasehavealookattheCitrusAPIJavaDocsanddocumentationinordertofindouthowtousethenewAPIsandclassesthatreplacedtheolddeprecatedstuff.

Bugfixes

CitrusReferenceGuide

9Changesnew

Page 10: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Bugsarepartofoursoftwaredevelopersworldandfixingthemispartofyourdailybusiness,too.FindingandsolvingissuesmakesCitrusbettereveryday.ForadetailedlistingofallbugfixespleaserefertothecompletechangeslogofeachreleaseinJIRA(http://www.citrusframework.org/changes-report.html).

CitrusReferenceGuide

10Changesnew

Page 11: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IntroductionNowadaysenterpriseapplicationsusuallycommunicatewithdifferentpartnersoverlooselycoupledmessaginginterfaces.Theinteractionandtheinterfacecontractneedstobetestedinintegrationtesting.

Inatypicalintegrationtestscenarioweneedtosimulatethecommunicationpartnersovervarioustransports.Howcanwetestusecasescenariosthatincludeseveralinterfacepartnersinteractingwitheachother?Howcansomebodyensurethatthesoftwarecomponentsworkcorrectlyregardingtheinterfacecontract?Howcansomebodyrunintegrationtestcasesinanautomatedreproducibleway?Citrustriestoanswerthesequestions!

Overview

Citrusaimstostronglysupportyouinsimulatinginterfacepartnersacrossdifferentmessagingtransports.YoucaneasilyproduceandconsumemessageswithawiderangeofprotocolslikeHTTP,JMS,TCP/IP,FTP,SMTPandmore.Theframeworkisabletobothactasaclientandserver.IneachcommunicationstepCitrusisabletovalidatemessagecontentstowardssyntaxandsemantics.

InadditiontothattheCitrusoffersawiderangeoftestactionstotakecontroloftheprocessflowduringatest(e.g.iterations,systemavailabilitychecks,databaseconnectivity,parallelism,delaying,errorsimulation,scriptingandmanymore).

ThebasicgoalinCitrustestcasesistodescribeawholeusecasescenarioincludingseveralinterfacepartnersthatexchangemanymessageswitheachother.ThecompositionofcomplexmessageflowsinasingletestcasewithseveralteststepsisoneofthemajorfeaturesinCitrus.

ThetestcasedescriptioniseitherdoneinXMLorJavaandcanbeexecutedmultipletimesasautomatedintegrationtest.WithJUnitandTestNGintegrationCitruscaneasilybeintegratedintoyourbuildlifecycleprocess.DuringatestCitrussimulatesallsurroundinginterfacepartners(clientorserver)withoutanycodingeffort.Witheasydefinitionofexpectedmessagecontent(headerandpayload)forXML,CSV,SOAP,JSONorplaintextmessagesCitrusisabletovalidatetheincomingdatatowardssyntaxandsemantics.

CitrusReferenceGuide

11Introduction

Page 12: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Usagescenarios

IfyouareinchargeofanenterpriseapplicationinamessagebasedsolutionwithmessageinterfacestoothersoftwarecomponentsyoushoulduseCitrus.IncaseyourprojectinteractswithothersoftwareoverdifferentmessagingtransportsandincaseyouneedtosimulatetheseinterfacepartnersonclientorserversideyoushoulduseCitrus.Incaseyouneedtocontinuouslycheckthesoftwarestabilitynotonlyonaunittestingbasisbutalsoinanend-to-endintegrationscenarioyoushoulduseCitrus.Bugfixing,releaseorregressiontestingisveryeasywithCitrus.IncaseyouarestrugglingwithcodestabilityandfeeluncomfortableregardingyournextsoftwarereleaseyoushoulddefinitelyuseCitrus.

ThistestsetupistypicalforaCitrususecase.Insuchatestscenariowehaveasystemundertest(SUT)withseveralmessageinterfacestootherapplicationslikeyouwouldhavewithanenterpriseservicebusforinstance.AclientapplicationinvokesservicesontheSUTapplication.TheSUTislinkedtoseveralbackendapplicationsovervariousmessagingtransports(hereSOAP,JMS,andHttp).Interimmessagenotificationsandfinalresponsesaresentbacktotheclientapplication.Thisgeneratesabunchofmessagesthatareexchangedthroughouttheapplicationsinvolved.

IntheautomatedintegrationtestCitrusneedstosendandreceivethosemessagesoverdifferenttransports.Citrustakescareofallinterfacepartners(ClientApplication,Backend1,Backend2,Backend3)andsimulatestheirbehaviorbysendingproperresponsemessagesinordertokeepthemessageflowalive.

Eachcommunicationstepcomeswithmessagevalidationandcomparisonagainstanexpectedmessagetemplate(e.g.XMLorJSONdata).BesidesmessagingactionsCitrusisalsoabletoperformarbitraryothertestactions.Citrusisabletoperformadatabasequerybetweenrequestsasanexample.

CitrusReferenceGuide

12Introduction

Page 13: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheCitrustestcaserunsfullyautomatedasaJavaapplication.InfactaCitrustestcaseisnothingbutaJUnitorTestNGtestcase.Stepbystepthewholeusecasescenarioisperformedlikeinarealproductionenvironment.TheCitrustestisrepeatableandisincludedintothesoftwarebuildprocess(e.g.usingMavenorANT)likeanormalunittestcasewoulddo.Thisgivesyoufullyautomatedintegrationteststoensureinterfacestability.

ThefollowingreferenceguidewalksthroughallCitruscapabilitiesandshowshowtosetupagreatintegrationtestwithCitrus.

CitrusReferenceGuide

13Introduction

Page 14: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SetupThischapterdiscusseshowtogetstartedwithCitrus.Itdealswiththeinstallationandsetupoftheframework,soyouarereadytostartwritingtestcasesafterreadingthischapter.

UsuallyyouwoulduseCitrusasadependencylibraryinyourproject.InMavenyouwouldjustaddCitrusasatest-scopeddependencyinyourPOM.WhenusingANTyoucanalsorunCitrusasnormalJavaapplicationfromyourbuild.xml.AsCitrustestsarenothingbutnormalunittestsyoucouldalsouseJUnitorTestNGanttaskstoexecutetheCitrustestcases.

ThischapterdescribestheCitrusprojectsetuppossibilities,chooseoneofthemthatfitsbesttoincludeCitrusintoyourproject.

UsingMaven

Citrususeshttp://maven.apache.org/internallyasaprojectbuildtoolandprovidesextendedsupportforMavenprojects.Mavenwilleaseupyourlifeasitmanagesprojectdependenciesandprovidesextendedbuildlifecyclesandconventionsforcompiling,testing,packagingandinstallingyourJavaproject.ThereforeitisrecommendedtousetheCitrusMavenprojectsetup.IncaseyoualreadyuseMavenitismostsuitableforyoutoincludeCitrusasatest-scopeddependency.

AsMavenhandlesallprojectdependenciesautomaticallyyoudonotneedtodownloadanyCitrusprojectartifactsinadvance.IfyouarenewtoMavenpleaserefertotheofficialMavendocumentationtofindouthowtosetupaMavenproject.

UseCitrusMavenarchetype

IfyoustartfromscratchorincaseyouwouldliketohaveCitrusoperatinginaseparateMavenmoduleyoucanusetheCitrusMavenarchetypetocreateanewMavenproject.ThearchetypewillsetupabasicCitrusprojectstructurewithbasicsettingsandfiles.

mvnarchetype:generate-DarchetypeCatalog=http://citrusframework.org

Choosearchetype:1:http://citrusframework.org->citrus-archetype(BasicarchetypeforCitrusintegrationtestproject)Chooseanumber:1

CitrusReferenceGuide

14Setup

Page 15: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DefinevalueforgroupId:com.consol.citrus.samplesDefinevalueforartifactId:citrus-sampleDefinevalueforversion:1.0-SNAPSHOTDefinevalueforpackage:com.consol.citrus.samples

InthesampleaboveweusedtheCitrusarchetypecataloglocatedontheCitrushomepage.CitrusarchetypesarealsoavailableinMavencentralrepository.Socanalsojustuse"mvnarchetype:generate".AsthelistofdefaultarchetypesavailableinMavencentralisverylongyoumightwanttofilterthelistwith"citrus"andyouwillgetjustafewpossibilitiestochoosefrom.

Weloadthearchetypeinformationfrom"http://citrusframework.org"andchoosetheCitrusbasicarchetype.Nowyouhavetodefineseveralvaluesforyourproject:thegroupId,theartifactId,thepackageandtheprojectversion.Afterthatwearedone!MavencreatedaCitrusprojectstructureforuswhichisreadyfortesting.Youshouldseethefollowingbasicprojectfolderstructure.

citrus-sample|+src||+main|||+java|||+resources||+citrus|||+java|||+resources|||+testspom.xml

TheCitrusprojectisabsolutelyreadyfortesting.WithMavenwecanbuild,package,installandtestourprojectrightawaywithoutanyadjustments.Trytoexecutethefollowingcommands:

mvnintegration-testmvnintegration-test-Dtest=MyFirstCitrusTest

NoteIfyouneedadditionalassistanceinsettingupaCitrusMavenprojectpleasevisitourMavensetuptutorialonhttp://www.citrusframework.org/tutorials.html.

AddCitrustoexistingMavenproject

CitrusReferenceGuide

15Setup

Page 16: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IncaseyoualreadyhaveaproperMavenprojectyoucanalsointegrateCitruswithit.JustaddtheCitrusprojectdependenciesinyourMavenpom.xmlasadependencylikefollows.

WeaddCitrusastest-scopedprojectdependencytotheprojectPOM(pom.xml)

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-core</artifactId><version>2.7</version><scope>test</scope></dependency>

IncaseyouwouldliketousetheCitrusJavaDSLalsoaddthisdependencytotheproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-java-dsl</artifactId><version>2.7</version><scope>test</scope></dependency>

AddthecitrusMavenplugintoyourproject

<plugin><groupId>com.consol.citrus.mvn</groupId><artifactId>citrus-maven-plugin</artifactId><version>2.7</version><configuration><author>DonaldDuck</author><targetPackage>com.consol.citrus</targetPackage></configuration></plugin>

NowthatwehaveaddedCitrustoourMavenprojectwecanstartwritingnewtestcaseswiththeCitrusMavenplugin:

mvncitrus:create-test

OnceyouhavewrittentheCitrustestcasesyoucanexecutethemautomaticallyinyourMavensoftwarebuildlifecylce.Thetestswillbeincludedintoyourprojectsintegration-testphaseusingtheMavensurefireplugin.Hereisasamplesurefireconfigurationfor

CitrusReferenceGuide

16Setup

Page 17: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Citrus.

<plugin><artifactId>maven-surefire-plugin</artifactId><version>2.4.3</version><configuration><skip>true</skip></configuration><executions><execution><id>citrus-tests</id><phase>integration-test</phase><goals><goal>test</goal></goals><configuration><skip>false</skip></configuration></execution></executions></plugin>

TheCitrussourcedirectoriesaredefinedastestsourceslikefollows:

<testSourceDirectory>src/it/java</testSourceDirectory><testResources><testResource><directory>src/it/java</directory><includes><include>**</include></includes><excludes><exclude>*.java</exclude></excludes></testResource><testResource><directory>src/it/tests</directory><includes><include>**/*</include></includes><excludes></excludes></testResource></testResources>

NoweverythingissetupandyoucancalltheusualMaveninstallgoal(mvncleaninstall)inordertobuildyourproject.TheCitrusintegrationtestsareexecutedautomaticallyduringthebuildprocess.BesidesthatyoucancalltheMavenintegration-

CitrusReferenceGuide

17Setup

Page 18: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

testphaseexplicitlytoexecuteallCitrustestsoraspecifictestbyitsname:

mvnintegration-testmvnintegration-test-Dtest=MyFirstCitrusTest

NoteIfyouneedadditionalassistanceinsettingupaCitrusMavenprojectpleasevisitourMavensetuptutorialonhttp://www.citrusframework.org/tutorials.html.

UsingAnt

Antisaverypopularwaytocompile,test,packageandexecuteJavaprojects.TheApacheprojecthaseffectivelybecomeastandardinbuildingJavaprojects.YoucanrunCitrustestcaseswithAntasCitrusisnothingbutaJavaapplication.ThissectiondescribesthestepstosetupaproperCitrusAntproject.

Preconditions

BeforewestartwiththeCitrussetupbesuretomeetthefollowingpreconditions.Thefollowingsoftwareshouldbeinstalledonyourcomputer,inordertousetheCitrusframework:

Java8orhigher

InstalledJDKplusJAVA_HOMEenvironmentvariablesetupandpointingtoyourJavainstallationdirectory

JavaIDE(optional)

AJavaIDEwillhelpyoutomanageyourCitrusproject(e.g.creatingandexecutingtestcases).YoucanusetheanyJavaIDE(e.g.EclipseorIntelliJIDEA)butalsoanyconvenientXMLEditortowritenewtestcases.

Ant1.8orhigher

Ant(http://ant.apache.org/)willruntestsandcompileyourCitruscodeextensionsifnecessary.

Download

FirstofallweneedtodownloadthelatestCitrusreleasearchivefromtheofficialwebsitehttp://www.citrusframework.org

CitrusReferenceGuide

18Setup

Page 19: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Citruscomestoyouasazippedarchiveinoneofthefollowingpackages:

citrus-x.x-releasecitrus-x.x-src

ThereleasepackageincludestheCitrusbinariesaswellasthereferencedocumentationandsomesampleapplications.

IncaseyouwanttogetintouchwithdevelopinganddebuggingCitrusyoucanalsogowiththesourcearchivewhichgivesyouthecompleteCitrusJavacodesources.ThewholeCitrusprojectisalsoaccessibleforyouonhttp://github.com/christophd/citrus.ThisopengitrepositoryonGitHubenablesyoutobuildCitrusfromscratchwithMavenandcontributecodechanges.

Installation

AfterdownloadingtheCitrusarchivesweextractthoseintoanappropriatelocationonthelocalstorage.WeareseekingfortheCitrusprojectartifactscomingasnormalJavaarchives(e.g.citrus-core.jar,citrus-ws.jar,etc.)

YouhavetoincludethoseCitrusJavaarchivesaswellasalldependencylibrariestoyourApacheAntJavaclasspath.Usuallyyouwouldcopyalllibrariesintoyourproject'slibdirectoryanddeclarethoselibrariesintheAntbuildfile.AsthisapproachcanbeverytimeconsumingIrecommendtouseadependencymanagementAPIsuchasApacheIvywhichgivesyouautomaticdependencyresolutionlikethatfromMaven.Inparticularthiscomesinhandywithallthe3rdpartydependenciesthatwouldberesolvedautomatically.

NomatterwhatapproachyouareusingtosetuptheApacheAntclasspathseethefollowingsampleAntbuildscriptwhichusestheCitrusprojectartifactsincombinationwiththeTestNGAnttaskstorunthetests.

<projectname="citrus-sample"basedir="."default="citrus.run.tests"xmlns:artifact="antlib:org.apache.maven.artifact.ant"

<propertyfile="src/it/resources/citrus.properties"/>

<pathid="maven-ant-tasks.classpath"path="lib/maven-ant-tasks-2.1.3.jar"/><typedefresource="org/apache/maven/artifact/ant/antlib.xml"uri="antlib:org.apache.maven.artifact.ant"classpathref="maven-ant-tasks.classpath"/>

<artifact:pomid="citrus-pom"file="pom.xml"/><artifact:dependenciesfilesetId="citrus-dependencies"pomRefId="citrus-pom"/>

CitrusReferenceGuide

19Setup

Page 20: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<pathid="citrus-classpath"><pathelementpath="src/it/java"/><pathelementpath="src/it/resources"/><pathelementpath="src/it/tests"/><filesetrefid="citrus-dependencies"/></path>

<taskdefresource="testngtasks"classpath="lib/testng-6.8.8.jar"/>

<targetname="compile.tests"><javacsrcdir="src/it/java"classpathref="citrus-classpath"/><javacsrcdir="src/it/tests"classpathref="citrus-classpath"/></target>

<targetname="create.test"description="Createsanewemptytestcase"><inputmessage="Entertestname:"addproperty="test.name"/><inputmessage="Entertestdescription:"addproperty="test.description"/><inputmessage="Enterauthor'sname:"addproperty="test.author"defaultvalue="$default.test.author"<inputmessage="Enterpackage:"addproperty="test.package"defaultvalue="$default.test.package"<inputmessage="Enterframework:"addproperty="test.framework"defaultvalue="testng"/>

<javaclassname="com.consol.citrus.util.TestCaseCreator"><classpathrefid="citrus-classpath"/><argline="-name$test.name-author$test.author-description$test.description-package$test.package-framework$test.framework"</java></target>

<targetname="citrus.run.tests"depends="compile.tests"description="RunsallCitrustests"<testngclasspathref="citrus-classpath"><classfilesetdir="src/it/java"includes="**/*.class"/></testng></target>

<targetname="citrus.run.single.test"depends="compile.tests"description="Runsasingletestbyname"<touchfile="test.history"/><loadpropertiessrcfile="test.history"/>

<echomessage="Lasttestexecuted:$last.test.executed"/><inputmessage="Entertestnameorleaveemptyforlasttestexecuted:"addproperty="testclass"

<propertyfilefile="test.history"><entrykey="last.test.executed"type="string"value="$testclass"/></propertyfile>

<testngclasspathref="citrus-classpath"><classfilesetdir="src/it/java"includes="**/$testclass.class"/></testng></target>

</project>

CitrusReferenceGuide

20Setup

Page 21: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NoteIfyouneeddetailedassistanceforbuildingCitruswithAntdoalsovisitourtutorialssectiononhttp://www.citrusframework.org.ThereyoucanfindatutorialwhichdescribestheCitrusJavaprojectsetupwithAntfromscratch.

CitrusReferenceGuide

21Setup

Page 22: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestcasesNowletusstartwritingtestcases!AtestcaseinCitrusdescribesallstepsforacertainusecaseinonesinglefile.TheCitrustestholdsasequenceoftestactions.Eachactionrepresentsaveryspecialpurposesuchassendingorreceivingamessage.Typicallywithmessage-basedenterpriseapplicationsthesendingandreceivingofmessagesrepresentthemainactionsinsideatest.

HoweveryouwilllearnthatCitrusismorethanjustasimpleSOAPclientforinstance.Eachtestcasecanholdcomplexactionssuchasconnectingtothedatabase,transformingdata,addingloopsandconditionalsteps.WiththedefaultCitrusactionsetyoucanaccomplishverycomplexusecaseintegrationtests.Laterinthisguidewewillbrieflydiscussallavailabletestactionsandlearnhowtousevariousmessagetransportswithinthetest.Fornowwewillconcentrateonthebasictestcasestructure.

ThefigureabovedescribesatypicaltestactionsequenceinCitrus.Alistofsendingandreceivingtestactionscomposingatypicaltestcasehere.EachactionreferencesapredefinedCitrusendpointcomponentthatwearegoingtotalkaboutlateron.

Sohowdowedefinethosetestcases?IngeneralCitrusspecifiestestcasesasJavaclasses.WithTestNGorJUnityoucanexecutetheCitrustestswithinyourJavaruntimeasyouwoulddowithinunittesting.YoucancodetheCitrustestinasingleJavaclass

CitrusReferenceGuide

22Testcase

Page 23: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

doingassertionsandusingSpring'sdependencyinjectionmechanisms.

IfyouarenotfamiliartowritingJavacodeyoucanalsowriteCitrustestsasXMLfiles.WhatevertestlanguageyouchooseforCitrusthewholetestcasedescriptiontakesplaceinonesinglefile(JavaorXML).ThischapterwillintroducethecustomXMLschemalanguageaswellastheJavadomainspecificlanguagesoyouwillbeabletowriteCitrustestcasesnomatterwhatknowledgebaseyoubelongto.

WritingtestcasesinXML

Putsimply,aCitrustestcaseisnothingbutasimpleSpringXMLconfigurationfile.TheSpringframeworkhasbecomeastateoftheartdevelopmentframeworkforenterpriseJavaapplications.AsyouworkwithCitrusyouwillalsolearnhowtousetheSpringIoc(Inversionofcontrol)containerandtheconceptsofdependencyinjection.SoletushavealookatthepureSpringXMLconfigurationsyntaxfirst.YouarefreetowritefullycompatibletestcasesfortheCitrusframeworkjustusingthissyntax.

Springbeandefinitionsyntax

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">

<beanname="MyFirstTest"class="com.consol.citrus.TestCase"><propertyname="variableDefinitions"><!--variablesofthistestgohere--></property><propertyname="actions"><!--actionsofthistestgohere--></property></bean></beans>

CitruscanexecutetheseSpringbeandefinitionsasnormaltestcases-noproblem,butthepureSpringXMLsyntaxisveryverboseandprobablynotthebestwaytodescribeatestcaseinCitrus.InparticularyouhavetoknowalotofCitrusinternalssuchasJavaclassnamesandpropertynames.Inadditiontothatastestscenariosgetmorecomplexthetestcasesgrowinsize.Soweneedamoreeffectiveandcomfortablewayofwritingtests.ThereforeCitrusprovidesacustomXMLschemadefinitionforwritingtestcaseswhichismuchmoreadequateforourtestingpurpose.

CitrusReferenceGuide

23Testcase

Page 24: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThecustomXMLschemaaimstoreachtheconvenienceofdomainspecificlanguages(DSL).LetushavealookattheCitrustestdescribingXMLlanguagebyintroducingafirstverysimpletestcasedefinition:

XMLDSL

<spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:spring="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">

<testcasename="MyFirstTest"><description>Firstexampleshowingthebasictestcasedefinitionelements!</description><variables><variablename="text"value="HelloTestFramework"/></variables><actions><echo><message>$text</message></echo></actions></testcase></spring:beans>

Wedoneedthe<spring:beans>rootelementastheXMLfileisreadbytheSpringIoCcontainer.InsidethisrootelementtheCitrusspecificnamespacedefinitionstakeplace.

Thetestcaseitselfgetsamandatorynamethatmustbeuniquethroughoutalltestcasesinaproject.Youwillreceiveerrorswhenusingduplicatetestnames.ThetestnamehastofollowthecommonJavanamingconventionsandrulesforJavaclasses.Thismeansnamesmustnotcontainanywhitespacecharactersbutcharacterslike'-','.','_'aresupported.Forexample,TestFeature_1isvalidbutTestFeature1isnotasitcontainswhitespacecharacterslikespaces.

NowthatwehaveanXMLdefinitionthatdescribesthestepsofourtestweneedaJavaexecutableforthetest.TheJavaexecutableisneededfortheframeworkinordertorunthetest.SeethefollowingsampleJavaclassthatrepresentsasimpleCitrusJavatest:

importorg.testng.annotations.Test;

CitrusReferenceGuide

24Testcase

Page 25: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.testng.AbstractTestNGCitrusTest;

@TestpublicclassMyFirstTestextendsAbstractTestNGCitrusTest

@CitrusXmlTest(name="MyFirstTest")publicvoidmyFirstTest()

ThesampleaboveisaJavaclassthatrepresentsavalidCitrusJavaexecutable.TheJavaclasshasnoprogramminglogicasweuseaXMLtestcasehere.TheJavaclasscanalsobegeneratedusingtheCitrusMavenplugin.TheJavaclassextendsfrombasicsuperclassAbstractTestNGCitrusTestandthereforeusesTestNGasunittestframework.CitrusalsosupportsJUnitasunittestframework.Readmoreaboutthisinrun-testngandrun-junit.

UptonowitisimportanttounderstandthatCitrusalwaysneedsaJavaexecutabletestclass.IncaseweusetheXMLtestrepresentationtheJavapartisgeneric,canbegeneratedandcontainsnoprogramminglogic.TheXMLtestdefinesallstepsandisourprimarytestcasedefinition.

WritingtestcasesinJava

BeforewegointomoredetailsontheattributesandactionsthattakeplacewithinatestcasewejusthavealookathowtowritetestcaseswithpureJavacode.CitrusworkswithJavaandusesthewellknownJUnitandTestNGframeworkbenefitsthatyoumaybeusedtoasatester.ManyusersmayprefertowriteJavacodeinsteadoftheverboseXMLsyntax.ThereforeyouhaveanotherpossibilityforwritingCitrustestsinpureJava.

WhenusingtheCitrusJavaDSLweneedtoincludeaspecialMavendependencymoduletoourprojectthatprovidestheneededAPI.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-java-dsl</artifactId><version>2.7</version><scope>test</scope></dependency>

CitrusingeneraldifferencesbetweentwowaysoftestcasesinJava.Thesearetest-designersandtest-runnersthatwedealwitheachinthenexttwosections.

CitrusReferenceGuide

25Testcase

Page 26: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLtestdesigner

ThefirstwayofdefiningaCitrustestinJavaisthetest-designer.TheJavaDSLforatestdesignerworkssimilartotheXMLapproach.Thewholetestcaseisbuiltwithalltestactionsfirst.ThenthewholetestcaseisexecutedasawholeCitrustest.ThisishowtodefineaCitrustestwithdesignerJavaDSLmethods:

JavaDSLdesigner

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassMyFirstTestDesignerextendsTestNGCitrusTestDesigner@CitrusTest(name="MyFirstTest")publicvoidmyFirstTest()description("Firstexampleshowingthebasictestcasedefinitionelements!");

variable("text","HelloTestFramework");

echo("$text");

CitrusprovidesabaseJavaclasscom.consol.citrus.dsl.testng.TestNGCitrusTestDesignerthatprovidesallcapabilitiesforyouinformofbuilderpatternmethods.Justusethe@CitrusTestannotationontopofthetestmethod.Citruswillusethemethodnameasthetestnamebydefault.Asyoucanseeintheexampleaboveyoucanalsocustomizethetestnamewithinthe@CitrusTestannotation.Thetestmethodbuildsalltestactionsusingthetestbuilderpattern.Thedefinedtestactionswillthenbecalledlateronduringtestruntime.

Thedesigntimeruntimedifferenceintest-designerisreallyimportanttobeunderstood.YoucanmixtheCitrusJavaDSLexecutionwithotherJavacodewithcertainlimitations.Wewillexplainthislateronwhenintroducingthetest-runner.

ThisisthebasictestJavaclasspatternusedinCitrus.Youasatesterwithdevelopmentbackgroundcaneasilyextendthispatternforcustomizedlogic.AgainifyouarecomingwithoutcodingexperiencedonotworrythisJavacodeisoptional.YoucandoexactlythesamewiththeXMLsyntaxonlyasshownbefore.ThetestdesignerJavaDSLismuchmorepowerfulthoughasyoucanusethefullJavaprogramminglanguagewithclassinheritanceandmethoddelegation.

CitrusReferenceGuide

26Testcase

Page 27: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Wehavementionedthatthetest-designerwillbuildthecompletetestcaseindesigntimewithallactionsfirstbeforeexecutionofthewholetestcasetakesplaceatruntimeofthetest.ThisapproachhastheadvantagethatCitrusknowsalltestactionsinatestbeforeexecution.OntheotherhandyouarelimitedinmixingJavaDSLmethodcallsandnormalJavacode.Thefollowingexampleshouldclarifythingsalittlebit.

JavaDSLdesigner

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassLoggingTestDesignerextendsTestNGCitrusTestDesignerprivateLoggingServiceloggingService=newLoggingService();

@CitrusTest(name="LoggingTest")publicvoidloggingTest()echo("BeforeloggingServicecall");

loggingService.log("Nowcalledcustomloggingservice");

echo("AfterloggingServicecall");

InthisexampletestcaseaboveweuseaninstanceofacustomLoggingServiceandcallsomeoperationlog()inthemiddleofourJavaDSLtest.NowdevelopersmightexpecttheloggingservicecalltobedoneinthemiddleoftheJavaCitrustestcasebutifwehavealookattheloggingoutputofthetestwegetatotaldifferentresult:

Expectedoutput

INFOCitrus|STARTINGTESTLoggingTestINFOEchoAction|BeforeloggingServicecallINFOLoggingService|NowcalledcustomloggingserviceINFOEchoAction|AfterloggingServicecallINFOCitrus|TESTSUCCESSLoggingTest

Actualoutput

INFOLoggingService|NowcalledcustomloggingserviceINFOCitrus|STARTINGTESTLoggingTestINFOEchoAction|BeforeloggingServicecallINFOEchoAction|AfterloggingServicecall

CitrusReferenceGuide

27Testcase

Page 28: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

INFOCitrus|TESTSUCCESSLoggingTest

SoifweanalysetheactualloggingoutputweseethattheloggingservicewascalledevenbeforetheCitrustestcasedidstartitsaction.Thisistheresultoftest-designerbuildingupthewholetestcasefirst.Thedesignercollectsalltestactionsfirstininternalmemorycacheandtheexecutesthewholetestcase.SothecustomservicecallontheLoggingServiceisnotpartoftheCitrusJavaDSLtestandthereforeisexecutedimmediatelyatdesigntime.

Wecanfixthiswiththefollowingtest-designercode:

JavaDSLdesigner

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassLoggingTestDesignerextendsTestNGCitrusTestDesignerprivateLoggingServiceloggingService=newLoggingService();

@CitrusTest(name="LoggingTest")publicvoidloggingTest()echo("BeforeloggingServicecall");

action(newAbstractTestAction()doExecute(TestContextcontext)loggingService.log("Nowcalledcustomloggingservice"););

echo("AfterloggingServicecall");

NowweplacedtheloggingServicecallinsideacustomTestActionimplementationandthereforethispieceofcodeispartoftheCitrusJavaDSLandfollowingfromthatpartoftheCitrustestexecution.Nowwiththatfixwegettheexpectedloggingoutput:

INFOCitrus|STARTINGTESTLoggingTestINFOEchoAction|BeforeloggingServicecallINFOLoggingService|NowcalledcustomloggingserviceINFOEchoAction|AfterloggingServicecallINFOCitrus|TESTSUCCESSLoggingTest

CitrusReferenceGuide

28Testcase

Page 29: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowthisisnoteasytounderstandandpeopledidstrugglewiththisseparationofdesigntimeandruntimeofaCitrusJavaDSLtest.ThisiswhywehaveimplementedanewJavaDSLbaseclasscalledtest-runnerthatwedealwithinthenextsection.Beforewecontinuewehavetomentionthatthetest-designerapproachdoesalsoworkforJUnit.AlthoughwehaveonlyseenTestNGsamplecodeinthissectioneverythingisworkingexactlythesamewaywithJUnitframework.Justusethebaseclasscom.consol.citrus.dsl.junit.JUnit4CitrusTestDesignerinstead.

ImportantNeitherTestNGCitrusTestDesignernorJUnit4CitrusTestDesignerimplementationisthreadsafeforparalleltestexecution.Thisissimplybecausethebaseclassisholdingstatetothecurrenttestdesignerinstanceinordertodelegatemethodcallstothisinstance.Thereforeparalleltestmethodexecutionisnotavailable.Fortunatelywehaveaddedathreadsafebaseclassimplementationthatusesresourceinjection.Readmoreaboutthisintestcase-resource-injection.

JavaDSLtestrunner

Thenewtestrunnerconceptsolvestheissuesthatmaycomealongwhenworkingwiththetestdesigner.Wehavealreadyseenasimpleexamplewherethetestdesignerrequiresstrictseparationofdesigntimeandruntime.Thetestrunnerimplementationexecuteseachtestactionimmediately.ThischangestheprerequisitesinsuchthatthetestactionJavaDSLmethodcallscanbemixedwithusualJavacodestatements.Thetheexamplethatwehaveseenbeforeinatestrunnerimplementation:

JavaDSLrunner

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestRunner;

@TestpublicclassLoggingTestRunnerextendsTestNGCitrusTestRunnerprivateLoggingServiceloggingService=newLoggingService();

@CitrusTest(name="LoggingTest")publicvoidloggingTest()echo("BeforeloggingServicecall");

loggingService.log("Nowcalledcustomloggingservice");

echo("AfterloggingServicecall");

CitrusReferenceGuide

29Testcase

Page 30: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WiththenewtestrunnerimplementationasbaseclassweareabletomixJavaDSLmethodcallsandnormalJavacodestatementinourtestinanunlimitedway.ThisexampleabovewillalsocreatetheexpectedloggingoutputasallJavaDSLmethodcallsareexecutedimmediately.

INFOCitrus|STARTINGTESTLoggingTestINFOEchoAction|BeforeloggingServicecallINFOLoggingService|NowcalledcustomloggingserviceINFOEchoAction|AfterloggingServicecallINFOCitrus|TESTSUCCESSLoggingTest

Incontrarytothetestdesignerthetestrunnerimplementationwillnotbuildthecompletetestcasebeforeexecution.EachtestactionisexecutedimmediatelyasitiscalledwithJavaDSLbuildermethods.Thiscreatesamorenaturalwayofcodingtestcasesasyouarealsoabletouseiterations,trycatchblocks,finallysectionsandsoon.

IntheexampleshereTestNGwasusedasunitframework.OfcoursetheexactsameapproachcanalsoapplytoJUnitframework.Justusethebaseclasscom.consol.citrus.dsl.junit.JUnit4CitrusTestRunnerinstead.Feelfreetochoosethebaseclassfortest-designerortest-runnerasyoulike.Youcanalsomixthosetwoapproachesinyourproject.CitrusisabletohandlebothwaysofJavaDSLcodeinaproject.

ImportantTheTestNGCitrusTestRunnerandJUnit4CitrusTestRunnerimplementationisnotthreadsafeforparalleltestexecution.Thisissimplybecausethebaseclassisholdingstatetothecurrenttestrunnerinstanceinordertodelegatemethodcallstothisinstance.Thereforeparalleltestmethodexecutionisnotavailable.Fortunatelywehaveaddedathreadsafebaseclassimplementationthatusesresourceinjection.Readmoreaboutthisintestcase-resource-injection.

Designer/Runnerinjection

Intheprevioussectionswehaveseenthedifferentapproachesfortestdesignerandrunnerimplementations.Uptonowthedecisionwhichimplementationtousewasmadebyextendingoneofthebaseclasses:

com.consol.citrus.dsl.testng.TestNGCitrusTestRunnercom.consol.citrus.dsl.testng.TestNGCitrusTestDesignercom.consol.citrus.dsl.junit.JUnit4CitrusTestRunnercom.consol.citrus.dsl.junit.JUnit4CitrusTestDesigner

CitrusReferenceGuide

30Testcase

Page 31: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThesefourclassesrepresentthedifferentdesignerandrunnerimplementationsforTestNGorJUnit.NowCitrusalsoprovidesaresourceinjectionmechanismforbothdesignerandrunnerimplementations.Theclassesusingthisfeatureare:

com.consol.citrus.dsl.testng.TestNGCitrusTestcom.consol.citrus.dsl.junit.JUnit4CitrusTest

Sowhatisthedealwiththat?Itissimplewhenlookingatafirstexampleusingresourceinjection:

@TestpublicclassInjectionTestextendsJUnit4CitrusTest

@CitrusTest(name="JUnit4DesignerTest")publicvoiddesignerTest(@CitrusResourceTestDesignerdesigner)designer.echo("Nowworkingondesignerinstance");

@CitrusTest(name="JUnit4RunnerTest")publicvoidrunnerTest(@CitrusResourceTestRunnerrunner)runner.echo("Nowworkingonrunnerinstance");

ThedesignerorrunnerinstanceisinjectedasCitrusresourcetothetestmethodasparameter.Thiswaywecanmixdesignerandrunnerinasingletest.Butthisisnottherealmotivationfortheresourceinjection.Theclearadvantageofthisapproachwithinjecteddesignerandrunnerinstancesissupportformultithreading.IncaseyouwanttoexecutetheCitrustestsinparallelusingmultiplethreadsyouneedtousethisapproach.Thisisbecausetheusualdesignerandrunnerbaseclassesarenotthreadsafe.ThisJUnit4CitrusTestbaseclassisbecausetheresourcesinjectedarenotkeptasstateinthebaseclass.

ThisisourfirstCitrusresourceinjectionusecase.Theframeworkisabletoinjectotherresources,too.Findoutmoreaboutthisinthenextsections.

Testcontextinjection

WhenrunningatestcaseinCitruswemakeuseofbasicframeworkcomponentsandcapabilities.Oneofthesecapabilitiesistousetestvariables,functionsandvalidationmatchers.Uptothispointwehavenotlearnedaboutthesethings.Theywillbedescribedintheupcomingchaptersandsectionsinmoredetail.RightnowIwanttotalkaboutresourceinjectioninCitrus.

CitrusReferenceGuide

31Testcase

Page 32: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AllthesefeaturementionedaboveareboundtosomeimportantCitruscomponent:theCitrustestcontext.Thetestcontextholdsallvariablesandisabletoresolvefunctionsandmatchers.Ingeneralyouasatesterwillnotneedexplicitaccesstothiscomponentastheframeworkisworkingwithitbehindthescenes.IncaseyouneedsomeaccessforadvancedoperationswiththeframeworkCitrusprovidesaresourceinjection.Letshavealookatthissothingsaregettingmoreclear.

publicclassResourceInjectionITextendsJUnit4CitrusTestDesigner

@Test@CitrusTestpublicvoidresourceInjectionIT(@CitrusResourceTestContextcontext)context.setVariable("myVariable","somevalue");echo("$myVariable");

Asyoucanseewehaveaddedamethodparameteroftypecom.consol.citrus.context.TestContexttothetestmethod.Theannotation@CitrusResourcetellsCitrustoinjectthisparameterwiththeaccordinginstanceoftheobjectforthistest.Nowwehaveeasyaccesstothecontextandallitscapabilitiessuchasvariablemanagement.

OfcoursethesameapproachworkswithTestNG,too.AsTestNGalsoprovidesresourceinjectionmechanismswehavetomakesurethatthedifferentresourceinjectionapproachesdonotinterferewitheachother.SowetellTestNGtonotinjectthisparameterbydeclaringitas@OptionalforTestNG.InadditiontothatweneedtointroducetheparametertoTestNGwiththe@Parametersannotation.OtherwiseTestNGwouldcomplainaboutnotknowingthisparameter.ThefinaltestmethodwithCitrusresourceinjectionlookslikefollows:

publicclassResourceInjectionITextendsTestNGCitrusTestDesigner

@Test@Parameters("context")@CitrusTestpublicvoidresourceInjectionIT(@Optional@CitrusResourceTestContextcontext)context.setVariable("myVariable","somevalue");echo("$myVariable");

CitrusReferenceGuide

32Testcase

Page 33: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Somemoreannotationsneededbuttheresultisthesame.WehaveaccesstotheCitrustestcontext.OfcourseyoucancombinetheresourceinjectionfordifferentCitruscomponents.Justaddmoresome@CitrusResourceannotatedmethodparameterstothetestmethod.

JavaDSLtestbehaviors

WhenusingtheJavaDSLtheconceptofbehaviorsisagoodwaytoreusetestactionblocks.Byputtingtestactionstoatestbehaviorwecaninstantiateandapplythebehaviortodifferenttestcasesmultipletimes.Themechanismisexplainedbestwhenhavingasimplesample:

publicclassFooBehaviorextendsAbstractTestBehaviorpublicvoidapply()variable("foo","test");

echo("fooBehavior");

publicclassBarBehaviorextendsAbstractTestBehaviorpublicvoidapply()variable("bar","test");

echo("barBehavior");

Thelistingaboveshowstwotestbehaviorsthataddveryspecifictestactionsandtestvariablestothetestcase.AsyoucanseethetestbehaviorisabletousethesameJavaDSLactionmethodsasanormaltestcasewoulddo.Insidetheapplymethodblockwedefinethebehaviorstestlogic.Nowoncethisisdonewecanusethebehaviorsinatestcaselikethis:

@CitrusTestpublicvoidbehaviorTest()description("ThisisabehaviorTest");author("Christoph");status(TestCaseMetaInfo.Status.FINAL);

variable("var","test");

applyBehavior(newFooBehavior());

echo("Successfullyappliedbarbehavior");

CitrusReferenceGuide

33Testcase

Page 34: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

applyBehavior(newBarBehavior());

echo("Successfullyappliedbarbehavior");

ThebehaviorisappliedtothetestcasebycallingtheapplyBehaviormethod.Asaresultthebehavioriscalledaddingitslogicatthispointofthetestexecution.Thesamebehaviorcannowbecalledinmultipletestcasessowehaveareusablesetoftestactions.

Description

Inthetestexamplesthatwehaveseensofaryoumayhavenoticedthatatestercangiveadetailedtestdescription.Thetestcasedescriptionclarifiesthetestingpurposeandperspectives.Thedescriptionshouldgiveashortintroductiontotheintendedusecasescenariothatwillbetested.Theusershouldgetafirstimpressionwhatthetestcaseisallaboutaswellasspecialinformationtounderstandthetestscenario.Youcanusefreetextinyourtestdescriptionnolimittothenumberofcharacters.ButbeawareoftheXMLvalidationrulesofwellformedXMLwhenusingtheXMLtestsyntax(e.g.specialcharacterescaping,useofCDATAsectionsmayberequired)

TestActions

Nowwegetclosetothemainpartofwritinganintegrationtest.ACitrustestcasedefinesasequenceofactionsthatwilltakeplaceduringthetest.Actionsbydefaultareexecutedsequentiallyinthesameorderastheyaredefinedinthetestcasedefinition.

XMLDSL

<actions><action>[...]</action><action>[...]</action></actions>

Allactionshaveindividualnamesandpropertiesthatdefinetherespectivebehavior.Citrusoffersawiderangeoftestactionsfromscratch,butyouarealsoabletowriteyourowntestactionsinJavaorGroovyandexecutethemduringatest.actionsgivesyouabriefdescriptionofallavailableactionsthatcanbepartofatestcaseexecution.

CitrusReferenceGuide

34Testcase

Page 35: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Theactionsarecombinedinfreesequencetoeachothersothatthetesterisabletodeclareaspecialactionchaininsidethetest.Theseactionscanbesendingorreceivingmessages,delayingthetest,validatingthedatabaseandsoon.Step-by-stepthetestproceedsthroughtheactionchain.Incaseonesingleactionfailsbyreasonthewholetestcaseisredanddeclarednotsuccessful.

Finallytestsection

Javadevelopersmightbefamiliarwiththeconceptofdoingsomethinginthefinallycodesection.Thefinallysectioncontainsalistoftestactionsthatwillbeexecutedguaranteedattheveryendofthetestcaseeveniferrorsdidoccurduringtheexecutionbefore.Thisistherightplacetotidyupthingsthatwerepreviouslycreatedbythetestlikecleaningupthedatabaseforinstance.Thefinallysectionisdescribedinmoredetailinfinally.Howeverhereisthebasicsyntaxinsideatest.

XMLDSL

<finally><echo><message>Dofinally-regardlessofwhathashappenedbefore</message></echo></finally>

JavaDSLdesigner

@CitrusTestpublicvoidsampleTest()echo("HelloTestFramework");

doFinally(echo("Dofinally-regardlessofanyerrorbefore"));

JavaDSLrunner

@CitrusTestpublicvoidsampleTest()echo("HelloTestFramework");

doFinally().actions(echo("Dofinally-regardlessofanyerrorbefore")

CitrusReferenceGuide

35Testcase

Page 36: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

);

Testmetainformation

Theusercanprovidesomeadditionalinformationaboutthetestcase.Themeta-infosectionattheverybeginningofthetestcaseholdsinformationlikeauthor,statusorcreationdate.Indetailthemetainformationisspecifiedlikethis:

XMLDSL

<testcasename="metaInfoTest"><meta-info><author>ChristophDeppisch</author><creationdate>2008-01-11</creationdate><status>FINAL</status><last-updated-by>ChristophDeppisch</last-updated-by><last-updated-on>2008-01-11T10:00:00</last-updated-on></meta-info><description>...</description><actions>...</actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidsampleTest()description("ThisisaTest");author("Christoph");status(Status.FINAL);

echo("HelloCitrus!");

Thestatusallowsfollowingvalues:DRAFT,READY_FOR_REVIEW,DISABLED,FINAL.Themeta-datainformationtoatestisquiteimportanttogivethereaderafirstinformationaboutthetest.Itisalsopossibletogeneratetestdocumentationusingthismeta-datainformation.Thebuilt-inCitrusdocumentationgeneratesHTMLorExceldocumentsthatlistalltestswiththeirmetadatainformationanddescription.

CitrusReferenceGuide

36Testcase

Page 37: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NoteTestswiththestatusDISABLEDwillnotbeexecutedduringatestsuiterun.SosomeonecanjuststartaddingplannedtestcasesthatarenotfinishedyetinstatusDRAFT.Incaseatestisnotrunnableyetbecauseitisnotfinished,someonemaydisableatesttemporarilytoavoidcausingfailuresduringatestrun.UsingthesedifferentstatusesonecaneasilysetuptestplansandreviewtheprogressoftestcoveragebycomparingthenumberofDRAFTteststothoseintheFINALstate.

NowyouknowthepossibilitieshowtowriteCitrustestcasesinXMLorJava.Pleasechoosewhatevercodelanguagetypeyouwant(Java,XML,Springbeansyntax)inordertowriteCitrustestcases.DevelopersmaychooseJava,testerswithoutcodingexperiencemayrunbestwiththeXMLsyntax.WeareconstantlyworkingonevenmoretestwritinglanguagesupportsuchasGroovy,Scala,Xtext,andsoon.IngeneralyoucanmixthedifferentlanguagetypesjustasyoulikewithinyourCitrusprojectwhichgivesyouthebestofflexibility.

CitrusReferenceGuide

37Testcase

Page 38: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestvariablesTheusageoftestvariablesisacoreconceptwhenwritinggoodmaintainabletests.Thekeyidentifiersofatestcaseshouldbeexposedastestvariablesattheverybeginningofatest.Thiswayhardcodedidentifiersandmultipleredundantvaluesinsidethetestcanbeavoidedfromscratch.Asatesteryoudefinealltestvariablesattheverybeginningofyourtest.

XMLDSL

<variables><variablename="text"value="HelloTestFramework"/><variablename="customerId"value="123456789"/></variables>

JavaDSLdesignerandrunner

variable("text","HelloTestFramework");variable("customerId","123456789");

Theconceptoftestvariablesisessentialwhenwritingcomplextestswithlotsofidentifiersandsemanticdata.Testvariablesarevalidforthewholetestcase.Youcanreferencethemseveraltimesusingacommonvariableexpression"$variable-name".Itisgoodpracticetoprovideallimportantentitiesastestvariables.Thismakesthetesteasiertomaintainandmoreflexible.Allessentialentitiesandidentifiersarepresentrightatthebeginningofthetest,whichmayalsogivetheopportunitytoeasilycreatetestvariantsbysimplychangingthevariablevaluesforothertestscenarios.

Thenameofthevariableisarbitrary.Feelfreetospecifyanynameyoucanthinkof.OfcourseyouneedtobecarefulwithspecialcharactersandreservedXMLentitieslike'&','<','>'.IfyouarefamiliarwithJavaoranyotherprogramminglanguagesimplythinkofthenamingrulesthereandyouwillbefinewithworkingonCitrusvariables,too.Thevalueofavariablecanbeanycharactersequence.ButagainbeawareofspecialXMLcharacterslike"<"thatneedtobeescaped("<")whenusedinvariablevalues.

Theadvantageofvariablesisobvious.Oncedeclaredthevariablescanbereferencedmanytimesinthetest.Thismakesitveryeasytovarydifferenttestcasesbyadjustingthevariablesfordifferentmeans(e.g.usedifferenterrorcodesintestcases).

CitrusReferenceGuide

38Testvariables

Page 39: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Globalvariables

Thelastsectiontoldustousevariablesastheyareveryusefulandextendthemaintainabilityoftestcases.Nowthateverytestcasedefineslocalvariablesyoucanalsodefineglobalvariables.Theglobalvariablesarevalidinalltestsbydefault.Thisisagoodopportunitytodeclareconstantvaluesforalltests.AsthesevariablesareglobalweneedtoaddthosetothebasicSpringapplicationcontextfile.ThefollowingexampledemonstrateshowtoaddglobalvariablesinCitrus:

<citrus:global-variables><citrus:variablename="projectName"value="CitrusIntegrationTesting"/><citrus:variablename="userName"value="TestUser"/></citrus:global-variables>

WeaddtheSpringbeancomponenttotheapplicationcontextfile.Thecomponentreceivesalistofname-valuevariableelements.Youcanreferencetheglobalvariablesinyourtestcasesasusual.

Anotherpossibilitytosetglobalvariablesistoloadthosefromexternalpropertyfiles.Thismaygiveyoumorepowerfulglobalvariableswithuserspecificpropertiesforinstance.Seehowtoloadpropertyfilesasglobalvariablesinthisexample:

<citrus:global-variables><citrus:filepath="classpath:global-variable.properties"/></citrus:global-variables>

Wehavejustaddedafilepathreferencetotheglobalvariablescomponent.Citrusloadsthepropertyfilecontentasglobaltestvariables.Youcanmixpropertyfileandname-valuepairvariabledefinitionsintheglobalvariablescomponent.

NoteTheglobalvariablescanhavevariableexpressionsandCitrusfunctions.Itispossibletousepreviouslydefinedglobalvariablesasvaluesofnewvariables,likeinthisexample:

user=Citrusgreeting=Hello$user!date=citrus:currentDate('yyyy-MM-dd')

CreatevariableswithCDATA

CitrusReferenceGuide

39Testvariables

Page 40: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WhenusingthXMLtestcaseDSLwecannothaveXMLvariablevaluesoutofthebox.ThiswouldinterferewiththeXMLDSLelementsdefinedintheCitrustestcaseXSDschema.YoucanuseCDATAsectionswithinthevariablevalueelementinordertodothisthough.

<variables><variablename="persons"><value><data><![CDATA[<persons><person><name>Theodor</name><age>10</age></person><person><name>Alvin</name><age>9</age></person></persons>]]></data></value></variable></variables>

ThatishowyoucanuseXMLvariablevaluesintheXMLDSL.IntheJavaDSLwedonothavetheseproblems.

CreatevariableswithGroovy

Youcanalsouseascripttocreatevariablevalues.Thisisextremelyhandywhenyouhaveverycomplexvariablevalues.JustcodeasmallGroovyscriptforinstanceinordertodefinethevariablevalue.Asmallsampleshouldgiveyoutheideahowthatworks:

<variables><variablename="avg"><value><scripttype="groovy">

</script></value>

CitrusReferenceGuide

40Testvariables

Page 41: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</variable><variablename="sum"><value><scripttype="groovy">

</script></value></variable></variables>

Weusethescriptcoderightinsidethevariablevaluedefinition.Thevalueofthevariableistheresultofthelastoperationperformedwithinthescript.Forlongerscriptcodetheuseof<![CDATA[]]>sectionsisrecommended.

CitrususesthejavaxScriptEnginemechanisminordertoevaluatethescriptcode.BydefaultGroovyissupportedinanyCitrusproject.SoyoucanaddadditionalScriptEngineimplementationstoyourprojectandsupportotherscripttypes,too.

Escapingvariablesexpression

Thetestvariablesexpressionsyntax"$variable-name"ispreservedtoevaluatetoatestvariablewithinthecurrenttestcontext.Howeverthesamesyntaxmaybepartofamessagecontentasis.Soyouneedtosomehowescapethesyntaxfrombeeinginterpretedastestvariablesyntax.Youcandothisbyusingthevariableexpressionescaping//charactersequencewrappingtheactualvariablenamelikethis

Thisisaescapedvariableexpression$//escaped//andshouldnotleadtounknownvariableexceptionswithinCitrus.

Theescapedexpression$//escaped//abovewillresultinthestring$escapedwhereescapedisnottreatedasatestvariablenamebutasanormalstringinthemessagepayload.ThiswayyouareabletohavethesamevariablesyntaxinamessagecontentwithoutinterferingwiththeCitrusvariableexpressionsyntax.AsaresultCitruswillnotcomplainaboutnotfindingthetestvariableescapedinthecurrentcontext.Thevariablesyntaxescapingcharacters//areautomaticallyremovedwhentheexpressionisprocessedbyCitrus.Sowewillgetthefollowingresultafterprocessing.

Thisisaescapedvariableexpression$escapedandshouldnotleadtounknownvariableexceptionswithinCitrus.

CitrusReferenceGuide

41Testvariables

Page 42: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

42Testvariables

Page 43: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

RunningtestsCitrustestcasesarenothingbutJavaclassesthatgetexecutedwithinaJavaruntimeenvironment.EachCitrustestthereforerelatestoaJavaclassrepresentingaJUnitorTestNGunittest.AsoptionaladdonaCitrustestcanhaveaXMLtestdeclarationfile.ThisisforthoseofyouthatdonotwanttocodeinJava.InthiscasetheXMLpartholdsallactionstotellCitruswhatshouldhappeninthetestcase.TheJavapartwillthenjustberesponsiblefortestexecutionandisnotlikelytobechangedatall.InthefollowingsectionsweconcentrateontheJavapartandthetestexecutionmechanism.

IfyoucreatenewtestcasesinCitrus-forinstanceviaMavenpluginorANTbuildscript-Citrusgeneratesbothpartsinyourtestdirectory.Forexample:ifyoucreateanewtestnamedMyFirstCitrusTestyouwillfindthesetwofilesasaresult:

src/it/tests/com/consol/citrus/MyFirstCitrusTest.xmlsrc/it/java/com/consol/citrus/MyFirstCitrusTest.java

NoteIfyouprefertojustwriteJavacodeyoucanthrowawaytheXMLpartimmediatelyandcontinueworkingwiththeJavapartonly.IncaseyouarefamiliarwithwritingJavacodeyoumayjustskipthetesttemplategenerationviaMavenorANTandpreferablyjustcreatenewCitrusJavatestclassesonyourown.

Withthecreationofthistestwehavealreadymadeaveryimportantdecision.Duringcreation,Citrusasksyouwhichexecutionframeworkshouldbeusedforthistest.Therearebasicallythreeoptionsavailable:testngandjunit.

SowhyisCitrusrelatedtoUnittestsalthoughitisintendedtobeaframeworkforintegrationtesting?Theanswertothisquestionisquitesimple:ThisisbecauseCitruswantstobenefitfrombothJUnitandTestNGforJavatestexecution.BoththeJUnitandTestNGJavaAPIsoffervariouswaysofexecutionandbothframeworksarewidelysupportedbyothertools(e.g.continuousbuild,buildlifecycle,developmentIDE).

Usersmightalreadyknowoneoftheseframeworksandthechancesaregoodthattheyarefamiliarwithatleastoneofthem.EverythingyoucandowithJUnitandTestNGtestcasesyoucandowithCitrustestsalso.IncludethemintoyourMavenbuildlifecycle.ExecutetestsfromyourIDE(Eclipse,IDEAorNetBeans).Includethemintoa

CitrusReferenceGuide

43Run

Page 44: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

continuousbuildtool(e.g.Jenkins).GeneratetestexecutionreportsandtestcoveragereportswithSonar,Coberturaandsoon.ThepossibilitieswithJUnitandTestNGareamazing.

SoletushaveacloserlookattheCitrusTestNGandJUnitintegration.

RunwithTestNG

TestNGstandsfornextgenerationtestingandhashadagreatinfluenceinaddingJavaannotationstotheunittestcommunity.CitrusisabletogenerateTestNGJavaclassesthatareexecutableastestcases.SeethefollowingstandardtemplatethatCitruswillgeneratewhenhavingnewtestcases:

packagecom.consol.citrus.samples;

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusXmlTest;importcom.consol.citrus.testng.AbstractTestNGCitrusTest;

/***TODO:Description**@authorUnknown*/@TestpublicclassSampleITextendsAbstractTestNGCitrusTest@CitrusXmlTest(name="SampleIT")publicvoidsampleTest()

IfyouarefamiliarwithTestNGyouwillseethatthegeneratedJavaclassisnothingbutanormalTestNGtestclass.WejustextendabasicCitrusTestNGclasswhichenablestheCitrustestexecutionfeaturesforus.BesidesthatwehaveausualTestNG@Testannotationplacedonourclasssoallmethodsinsidetheclasswillbeexecutedasseparatetestcase.

ThegoodnewsisthatwecanstillusethefantasticTestNGfeaturesinourtestclass.Youcanthinkofparalleltestexecution,testgroups,setupandteardownoperationsandsoon.Justtogiveanexamplewecansimplyaddatestgrouptoourtestlikethis:

@Test(groups="long-running")

FormoreinformationonTestNGpleasevisittheofficialhomepage,whereyoufindacompletereferencedocumentation.

CitrusReferenceGuide

44Run

Page 45: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

YoumighthavenoticedthattheexampleaboveloadstestcasesfromXML.Thisiswhyweareusingthe@CitrusXmlTestannotation.AgainthisapproachisforpeoplethatwanttowritenoJavacode.ThetestlogicisthenprovidedintheXMLtestdefinition.WediscussXMLtestsinCitrusinmoredetailinrun-xml-tests.NextletshavealookataTestNGJavaDSLtest.

WhenwritingtestsinpureJavawehaveprettymuchtheexactsamelogicthatappliestoexecutingCitrustestcases.TheCitrustestextendsfromaTestNGbaseclassandusesthenormal@Testannotationsonmethodorclasslevel.HereisashortsampleTestNGJavaclassforthis:

importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassMyFirstTestDesignerextendsTestNGCitrusTestDesigner@CitrusTest(name="MyFirstIT")publicvoidmyFirstTest()description("Firstexampleshowingthebasictestcasedefinitionelements!");

variable("text","HelloTestFramework");

echo("$test");

YouseetheclassisquitesimilartotheXMLtestvariation.NowweextendaCitrustestdesignerclasswhichenablestheJavaDSLfeaturesinadditiontotheTestNGtestexecutionforus.Thebasic@TestannotationforTestNGhasnotchanged.WestillhaveausualTestNGclasswiththepossibilityofseveralmethodseachrepresentingaseparateunittest.

Nowwhathaschangedisthe@CitrusTestannotation.NowtheCitrustestlogicisplaceddirectlyasthemethodbodywithusingtheJavadomainspecificlanguagefeatures.TheXMLCitrustestpartisnotnecessaryanymore.IfyouarewonderingaboutthedesignersuperclassandtheJavaDSLmethodsforaddingthetestlogictoyourtestpleasebepatientwewilllearnmoreabouttheJavaDSLfeaturesinthisreferenceguidelateron.

UptonowwejustconcentrateontheTestNGintegrationthatisquiteeasyisn'tit.

UsingTestNGDataProviders

CitrusReferenceGuide

45Run

Page 46: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestNGasaframeworkcomeswithlotsofgreatfeaturessuchasdataproviders.Dataprovidersexecuteatestcaseseveraltimes.Eachtestexecutiongetsaspecificparametervalue.WithCitrusyoucanusethosedataproviderparametersinsidethetestasvariables.SeethenextlistingonhowtouseTestNGdataprovidersinCitrus:

publicclassDataProviderITextendsAbstractTestNGCitrusTest

@CitrusXmlTest@CitrusParameters("message")@Test(dataProvider="messageDataProvider")publicvoidDataProviderIT(ITestContexttestContext)

@DataProviderpublicObject[][]messageDataProvider()returnnewObject[][]"HelloWorld!","HalloWelt!","HiCitrus!",;

AbovetestcasemethodisannotatedwithTestNGdataprovidercalledmessageDataProvider.Inthesameclassyoucanwritethedataproviderthatreturnsalistofparametervalues.TestNGwillexecutethetestcaseseveraltimesaccordingtotheprovidedparameterlist.Eachexecutionisshippedwiththerespectiveparametervalue.Accordingtothe@CitrusParameterannotationthetestwillhaveatestvariablecalledmessagethatisaccessibleasusual.

RunwithJUnit

JUnitisaverypopularunittestframeworkforJavaapplicationswidelyacceptedandwidelysupportedbymanytools.IngeneralCitrussupportsbothJUnitandTestNGastestexecutionframeworks.AlthoughtheTestNGcustomizationfeaturesareslightlymorepowerfulthanthoseofferedbyJUnityouasaCitrususershouldbeabletousetheframeworkofyourchoice.Thecompletesupportforexecutingtestcaseswithpackagescansandmultipleannotatedmethodsisgivenforbothframeworks.IfyouchoosejunitasexecutionframeworkCitrusgeneratesaJavafilethatlookslikethis:

packagecom.consol.citrus.samples;

importorg.junit.Test;

CitrusReferenceGuide

46Run

Page 47: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

importcom.consol.citrus.annotations.CitrusXmlTest;importcom.consol.citrus.junit.AbstractJUnit4CitrusTest;

/***TODO:Description**@authorUnknown*/publicclassSampleITextendsAbstractJUnit4CitrusTest@Test@CitrusXmlTest(name="SampleIT")publicvoidsampleTest()

JUnitandTestNGasframeworksrevealslightdifferences,buttheideaisthesame.WeextendabaseJUnitCitrustestclassandhaveonetomanytestmethodsthatloadtheXMLCitrustestcasesforexecution.AsyoucanseethetestclasscanholdseveralannotatedtestmethodsthatgetexecutedasJUnittests.ThefinethinghereisthatwearestillabletouseallJUnitfeaturessuchasbefore/aftertestactionsorenable/disabletests.

TheJavaJUnitclassesaresimplyresponsibleforloadingandexecutingtheCitrustestcases.CitrustakescareonloadingtheXMLtestasafilesystemresourceandtosetuptheSpringapplicationcontext.Thetestisexecutedandsuccess/failurestateisreportedexactlylikeausualJUnitunittestwoulddo.ThisalsomeansthatyoucanexecutethisCitrusJUnitclasslikeeveryotherJUnittest,especiallyoutofanyJavaIDE,withMaven,withANTandsoon.ThismeansthatyoucaneasilyincludetheCitrustestexecutionintoyousoftwarebuildinglifecycleandcontinuousbuild.

TipSonowweknowbothTestNGandJUnitsupportinCitrus.Whichframeworkshouldsomeonechoose?Tobehonest,thereisnoeasyanswertothisquestion.Thebasicfeaturesareequivalent,butTestNGoffersbetterpossibilitiesfordesigningmorecomplextestsetupwithtestgroupsandtasksbeforeandafteragroupoftests.ThisiswhyTestNGisthedefaultoptioninCitrus.Butintheendyouhavetodecideonyourownwhichframeworkfitsbestforyourproject.

Thefirstexampleseenhereisusing@CitrusXmlTestannotationinordertoloadaXMLfileastest.TheJavapartisthenjustanemptyenvelopeforexecutingthetestwithJUnit.ThisapproachisforthoseofyouthatarenotfamiliarwithJavaatall.YoucanfindmoreinformationonloadingXMLfilesasCitrustestsinrun-xml-tests.SecondlyofcoursewealsohavethepossibilitytousetheCitrusJavaDSLwithJUnit.Seethefollowingexampleonhowthislookslike:

CitrusReferenceGuide

47Run

Page 48: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

packagecom.consol.citrus.samples;

importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.JUnit4CitrusTestDesigner;importorg.junit.Test;

/***TODO:Description**@authorUnknown*/publicclassSampleITextendsJUnit4CitrusTestDesigner

@Test@CitrusTestpublicvoidEchoSampleIT()variable("time","citrus:currentDate()");echo("HelloCitrus!");echo("CurrentTimeis:$time");

@Test@CitrusTest(name="EchoIT")publicvoidechoTest()echo("HelloCitrus!");

TheJavaDSLtestcaselooksquitefamiliaraswealsousetheJUnit4@Testannotationinordertomarkourtestforunittestexecution.Inadditiontothatweadda@CitrusTestannotationandextendfromabasicJUnit4CitrustestdesignerwhichenablestheJavadomainspecificlanguagefeatures.TheCitrustestlogicgoesdirectlytothemethodblock.ThereisnoneedforaXMLtestfileanymore.

Asyoucanseethe@CitrusTestannotationsupportsmultipletestmethodsinonesingleclass.EachtestispreparedandexecutedseparatelyjustasyouknowitfromJUnit.YoucandefineanexplicitCitrustestnamethatisusedinCitrustestreports.Ifnoexplicittestnameisgiventhetestmethodnamewillbeusedasatestname.

IfyouneedtoknowmoredetailsaboutthetestdesignerandonhowtousetheCitrusJavaDSLjustcontinuewiththisreferenceguide.Wewilldescribethecapabilitiesindetaillateron.

RunningXMLtests

CitrusReferenceGuide

48Run

Page 49: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Nowwealsousethe@CitrusXmlTestannotationintheJavaclass.ThisannotationmakesCitrussearchforaXMLfilethatrepresentstheCitrustestwithinyourclasspath.LateronwewillalsodiscussanotherCitrusannotation(@CitrusTest)whichstandsfordefiningtheCitrustestjustwithJavadomainspecificlanguagefeatures.FornowwecontinuetodealwiththeXMLCitrustestexecution.

ThedefaultnamingconventionrequiresaXMLfilewiththetestsnameinthesamepackagethattheJavaclassisplacedin.InthebasicexampleabovethismeansthatCitrussearchesforaXMLtestfileincom/consol/citrus/samples/SampleIT.xml.YoutellCitrustosearchforanotherXMLfilebyusingthe@CitrusXmlTestannotationproperties.Followingannotationpropertiesarevalid:

name:Listoftestcasenamestoexecute.NamesalsodefineXMLfilenamestolookfor(.xmlfileextensionisnotneededhere).packageName:CustompackagelocationfortheXMLfilestoloadpackageScan:ListofpackagesthatareautomaticallyscannedforXMLtestfilestoexecute.ForeachXMLfilefoundseparatetestisexecuted.NotethatthisperformsaJavaClasspathpackagescansoallXMLfilesinpackageareassumedtobevalidCitrusXMLtestcases.InordertominimizetheamountofaccidentallyloadedXMLfilesthescanwillonlyloadXMLfileswith*/Test.xmland*/IT.xmlfilenamepattern.

YoucanalsomixthevariousCitrusXmlTestannotationpatternsinasingleJavaclass.SoweareabletohaveseveraltestcasesinonesingleJavaclass.EachannotatedmethodrepresentsoneormoreCitrusXMLtestcases.Sethefollowingexampletoseewhatthisisabout.

@TestpublicclassSampleITextendsAbstractTestNGCitrusTest@CitrusXmlTest(name="SampleIT")publicvoidsampleTest()

@CitrusXmlTest(name="SampleIT","AnotherIT")publicvoidmultipleTests()

@CitrusXmlTest(name="OtherIT",packageName="com.other.testpackage")publicvoidotherPackageTest()

@CitrusXmlTest(packageScan="com.some.testpackage","com.other.testpackage")publicvoidpackageScanTest()

CitrusReferenceGuide

49Run

Page 50: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Youarefreetocombinethesetestannotationsasyoulikeinyourclass.AsthewholeJavaclassisannotatedwiththeTestNG@Testannotationeachmethodgetsexecutedautomatically.CitruswillalsotakecareonexecutingeachXMLtestcaseasaseparateunittest.SothetestreportswillhavetheexactnumberofexecutedtestsandtheJUnit/TestNGtestreportsdohavetheexacttestoutlineforfurtherusage(e.g.incontinuousbuildreports).

NoteWhentestexecutiontakesplaceeachtestmethodannotationisevaluatedinsequence.XMLtestcasesthatmatchseveraltimes,forinstancebyexplicitnamereferenceandapackagescanwillbeexecutedseveraltimesrespectively.

Thebestthingaboutusingthe@CitrusXmlTestannotationisthatyoucancontinuetousethefabulousTestNGcapabilities(e.g.testgroups,invocationcount,threadpools,dataproviders,andsoon).

SonowwehaveseenhowtoexecuteaCitrusXMLtestwithTestNG.

CitrusReferenceGuide

50Run

Page 51: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ConfigurationYouhaveseveraloptionsincustomizingtheCitrusprojectconfiguration.Citrususesdefaultsettingsthatcanbeoverwrittentosomeextend.AsaframeworkCitrusinternallyworkswiththeSpringIoCcontainer.SoCitruswillstartaSpringapplicationcontextandregisterseveralcomponentsasSpringbeans.Youcancustomizethebehaviorofthesebeansandyoucanaddcustomsettingsbysettingsystemproperties.

CitrusSpringXMLapplicationcontext

CitrusstartsaSpringapplicationcontextandaddssomedefaultSpringbeancomponents.BydefaultCitruswillloadsomeinternalSpringJavaconfigclassesdefiningthosebeancomponents.Atsomepointyoumightaddsomecustombeanstothatbasicapplicationcontext.ThisiswhyCitruswillsearchforcustomSpringapplicationcontextfilesinyourproject.Theseareautomaticallyloaded.

BydefaultCitruslooksforcustomXMLSpringapplicationcontextfilesinthislocation:classpath*:citrus-context.xml.Soyoucanaddafilenamedcitrus-context.xmltoyourprojectclasspathandCitruswillloadallSpringbeansautomatically.

ThelocationofthisfilecanbecustomizedbysettingaSystempropertycitrus.spring.application.context.SoyoucancustomizetheXMLSpringapplicationcontextfilelocation.TheSystempropertyissettablewithMavensurefireandfailsafepluginforinstanceorviaJavabeforetheCitrusframeworkgetsloaded.

SeethefollowingsampleXMLconfigurationwhichisanormalSpringbeanXMLconfiguration:

<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"

<citrus:schema-repositoryid="schemaRepository"/>

</beans>

CitrusReferenceGuide

51Configuration

Page 52: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowyoucanaddsomeSpringbeansandyoucanusetheCitrusXMLcomponentssuchasschema-repositoryforaddingcustombeansandcomponentstoyourCitrusproject.CitrusprovidesseveralnamespacesforcustomSpringXMLcomponents.Thesearedescribedinmoredetailintherespectivechaptersandsectionsinthisreferenceguide.

TipYoucanalsouseimportstatementsinthisSpringapplicationcontextinordertoloadotherconfigurationfiles.SoyouarefreetomodularizeyourconfigurationinseveralfilesthatgetloadedbyCitrus.

CitrusSpringJavaconfig

UsingXMLSpringapplicationcontextconfigurationisthedefaultbehaviorofCitrus.HoweversomepeoplemightpreferpureJavacodeconfiguration.YoucandothatbyaddingaSystempropertycitrus.spring.java.configwithacustomSpringJavaconfigclassasvalue.

System.setProperty("citrus.spring.java.config",MyCustomConfig.class.getName())

CitruswillloadtheSpringbeanconfigurationsinMyCustomConfig.classasJavaconfigthen.SeethefollowingexampleforcustomSpringJavaconfiguration:

importcom.consol.citrus.TestCase;importcom.consol.citrus.report.*;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;

@ConfigurationpublicclassMyCustomConfig

@Bean(name="customTestListener")publicTestListenercustomTestListener()returnnewPlusMinusTestReporter();

privatestaticclassPlusMinusTestReporterextendsAbstractTestListenerimplementsTestReporter

/**Logger*/privateLoggerlog=LoggerFactory.getLogger(CustomBeanConfig.class);

privateStringBuildertestReport=newStringBuilder();

@OverridepublicvoidonTestSuccess(TestCasetest)

CitrusReferenceGuide

52Configuration

Page 53: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

testReport.append("+");

@OverridepublicvoidonTestFailure(TestCasetest,Throwablecause)testReport.append("-");

@OverridepublicvoidgenerateTestResults()log.info(testReport.toString());

@OverridepublicvoidclearTestResults()testReport=newStringBuilder();

YoucanalsomixXMLandJavaconfigurationsoCitruswillloadbothconfigurationtotheSpringbeanapplicationcontextonstartup.

Citrusapplicationproperties

TheCitrusframeworkreferencessomebasicSystempropertiesthatcanbeoverwritten.ThepropertiesareloadedfromJavaSystemandarealsosettableviapropertyfile.Justaddapropertyfilenamedcitrus-application.propertiestoyourprojectclasspath.Thispropertyfilecontainscustomizedsettingssuchas:

citrus.spring.application.context=classpath*:citrus-custom-context.xmlcitrus.spring.java.config=com.consol.citrus.config.MyCustomConfigcitrus.file.encoding=UTF-8citrus.xml.file.name.pattern=/**/*Test.xml,/**/*IT.xml

Citrusloadstheseapplicationpropertiesatstartup.AllpropertiesarealsosettablewithJavaSystemproperties.Thelocationofthecitrus-application.propertiesiscustomizablewiththeSystempropertycitrus.application.config.

System.setProperty("citrus.application.config","custom/path/to/citrus-application.properties"

Atthemomentyoucanusethesepropertiesforcustomization:

CitrusReferenceGuide

53Configuration

Page 54: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

citrus.spring.application.context:FilelocationforSpringXMLconfigurationscitrus.spring.java.config:ClassnameforSpringJavaconfigcitrus.file.encoding:DefaultfileencodingusedinCitruswhenreadingandwritingfilecontentcitrus.xml.file.name.pattern:FilenamepatternsusedforXMLtestfilepackagescan

CitrusReferenceGuide

54Configuration

Page 55: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

EndpointsInoneofthepreviouschapterswehavediscussedthebasictestcasestructureasweintroducedvariablesandtestactions.Thesectioncontainsalistoftestactionsthattakeplaceduringthetestcase.Eachtestactionisexecutedinsequentialorderbydefault.Citrusoffersseveralbuilt-intestactionsthattheusercanchoosefromtoconstructacomplextestingworkflowwithouthavingtocodeeverythingfromscratch.InparticularCitrusaimstoprovideallthetestactionsthatyouneedaspredefinedcomponentsreadyforyoutouse.Thegoalistominimizethecodingeffortforyousoyoucanconcentrateonthetestlogicitself.

ExactlythesameapproachisusedinCitrustoprovideready-to-useendpointcomponentforconnectingtodifferentmessagetransports.Thereareseveralwaysinanenterpriseapplicationtoexchangemessageswithsomeotherapplication.WehavesynchronousinterfaceslikeHttpandSOAPWebServices.WehaveasynchronousmessagingwithJMSorfiletransferFTPinterfaces.

Citrusprovidesendpointcomponentsasclientandservertoconnectwiththesetypicalmessagetransports.SoyouasatestermustnotcareabouthowtosendamessagetoaJMSqueue.TheCitrusendpointsareconfiguredintheSpringapplicationcontextandreceiveendpointspecificpropertieslikeendpointuriorportsormessagetimeoutsasconfiguration.

ThenextfigureshowsatypicalmessagesendingendpointcomponentinCitrus:

Theendpointproducerpublishesmessagestoadestination.ThisdestinationcanbeaJMSqueue/topic,aSOAPWebServiceendpoint,aHttpURL,aFTPfolderdestinationandsoon.Theproducerjusttakesapreviouslydefinedmessagedefinition(headerandpayload)andsendsittothemessagedestination.

SimilartothatCitrusdefinestheseveralendpointconsumercomponentstoconsumemessagesfromdestinations.ThiscanbeasimplesubscriptiononmessagechannelsandJMSqueues/topics.IncaseofSOAPWebServicesandHttpGET/POSTthingsare

CitrusReferenceGuide

55Endpoints

Page 56: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

morecomplicatedaswehavetoprovideaservercomponentthatclientscanconnectto.Wewillhandleserverrelatedcommunicationinmoredetaillateron.Fornowtheendpointconsumercomponentinitsmostsimplewayisdefinedlikethis:

ThisisallyouneedtoknowaboutCitrusendpoints.WehavementionedthattheendpointsaredefinedintheSpringapplicationcontext.Let'shaveasimpleexamplethatshowsthebasicidea:

<citrus-jms:endpointid="helloServiceEndpoint"destination-name="Citrus.HelloService.Request.Queue"connection-factory="myConnectionFacotry"/>

ThisisasimpleJMSendpointcomponentinCitrus.TheendpointXMLbeandefinitionfollowsacustomXMLnamespaceanddefinesendpointspecificpropertiesliketheJMSdestinationnameandtheJMSconnectionfactory.Theendpointidisasignificantpropertyasthetestcaseswillreferencethisendpointwhensendingandreceivingmessagesbyitsidentifier.

Inthenextsectionsyouwilllearnhowatestcaseusesthoseendpointcomponentsforproducingandconsumingmessages.

Sendmessageswithendpoints

Theactioninatestcasepublishesmessagestoadestination.Theactualmessagetransportconnectionisdefinedwiththeendpointcomponent.Thetestcasesimplydefinesthemessagecontentsandreferencesapredefinedmessageendpointcomponentbyitsidentifier.EndpointspecificconfigurationsarecentralizedintheSpringbeanapplicationcontextwhilemultipletestcasescanreferencetheendpointtoactuallypublishtheconstructedmessagetoadestination.ThereareseveralmessageendpointimplementationsinCitrusavailablerepresentingdifferenttransportprotocolslikeJMS,SOAP,HTTP,TCP/IPandmanymore.

Againthetypeoftransporttouseisnotspecifiedinsidethetestcasebutinthemessageendpointdefinition.Theseparationofconcerns(testcase/messagesendertransport)givesusagoodflexibilityofourtestcases.Thetestcasedoesnotknowanythingaboutconnectionfactories,queuenamesorendpointuri,connectiontimeoutsandsoon.The

CitrusReferenceGuide

56Endpoints

Page 57: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

transportinternalsunderneathasendingtestactioncanchangeeasilywithoutaffectingthetestcasedefinition.WewillseelaterinthisdocumenthowtocreatedifferentmessageendpointsforvarioustransportsinCitrus.Fornowweconcentrateonconstructingthemessagecontenttobesent.

Weassumethatthemessage'spayloadwillbeplainXMLformat.CitrususesXMLasthedefaultdataformatformessagepayloaddata.ButCitrusisnotlimitedtoXMLmessageformatthough;youcanalwaysdefineothermessagedataformatssuchasJSON,plaintext,CSV.AsXMLisstillaverypopularmessageformatinenterpriseapplicationsandmessage-basedsolutionarchitectureswehavethisasadefaultformat.AnywayCitrusworksbestonXMLpayloadsandyouwillseealotofexamplecodeinthisdocumentusingXML.Finallyletushavealookatafirstexamplehowasendingactionisdefinedinthetest.

XMLDSL

<testcasename="SendMessageTest"><description>Basicsendmessageexample</description>

<actions><sendendpoint="helloServiceEndpoint"><message><payload><TestMessage><Text>Hello!</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/></header></send></actions></testcase>

Nowletshaveacloserlookatthesendingaction.The'endpoint'attributemightcatchyourattentionfirst.ThisattributereferencesthemessageendpointinCitrusconfigurationbyitsidentifier.Aspreviouslymentionedthemessageendpointdefinitionlivesinaseparateconfigurationfileandcontainstheactualmessagetransportsettings.Inthisexamplethe"helloServiceEndpoint"isreferencedwhichisaJMSendpointforsendingoutmessagestoaJMSqueueforinstance.

CitrusReferenceGuide

57Endpoints

Page 58: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thetestcaseisnotawareofanytransportdetails,becauseitdoesnothaveto.Theadvantagesareobvious:Ontheonehandmultipletestcasescanreferencethemessageendpointdefinitionforbetterreuse.Secondlytestcasesareindependentofmessagetransportdetails.Soconnectionfactories,usercredentials,endpointurivaluesandsoonarenotpresentinthetestcase.

Inotherwordsthe"endpoint"attributeofthe<send>elementspecifieswhichmessageendpointdefinitiontouseandthereforewherethemessageshouldgoto.OnceagainallavailablemessageendpointsareconfiguredinaseparateCitrusconfigurationfile.Besuretoalwayspicktherightmessageendpointtypeinordertopublishyourmessagetotherightdestination.

IfyoudonotliketheXMLlanguageyoucanalsousepureJavacodetodefinethesametest.InJavayouwouldalsomakeuseofthemessageendpointdefinitionandreferencethisinstance.ThesametestasshownaboveinJavaDSLlookslikethis:

JavaDSLdesigner

importorg.testng.ITestContext;importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassSendMessageTestDesignerextendsTestNGCitrusTestDesigner

@CitrusTest(name="SendMessageTest")publicvoidsendMessageTest()description("Basicsendmessageexample");

send("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello");

InsteadofusingtheXMLtagsforsendweusemethodsfromTestNGCitrusTestDesignerclass.Thesamemessageendpointisreferencedwithinthesendmessageaction.ThepayloadisconstructedasplainJavacharactersequencewhichisabitverbose.Wewillseelateronhowwecanimprovethis.Fornowitisimportanttounderstandthecombinationofsendtestactionandamessageendpoint.

CitrusReferenceGuide

58Endpoints

Page 59: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TipItisgoodpracticetofollownamingconventionswhendefiningnamesformessageendpoints.Theintendedpurposeofthemessageendpointaswellasthesending/receivingactorshouldbeclearwhenchoosingthename.ForinstancemessageEndpoint1,messageEndpoint2willnotgiveyoumuchhintstothepurposeofthemessageendpoint.

ThisisbasicallyhowtosendmessagesinCitrus.Thetestcaseisresponsibleforconstructingthemessagecontentwhilethepredefinedmessageendpointholdstransportspecificsettings.Testcasesreferenceendpointcomponentstopublishmessagestotheoutsideworld.Thisisjustthestartofaction.Citrussupportsawholepackageofotherwayshowtodefineandmanipulatethemessagecontents.Readmoreaboutmessagesendingactionsinactions-send.

Receivemessageswithendpoints

Nowwehavealookatthemessagereceivingpartinsidethetest.Asimpleexampleshowshowitworks.

XMLDSL

<receiveendpoint="helloServiceEndpoint"><message><payload><TestMessage><Text>Hello!</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/></header></receive>

Ifwerecapthesendactionofthepreviouschapterwecanidentifysomecommonmechanismsthatapplyforbothsendingandreceivingactions.Thetestactionalsousestheendpointattributeforreferencingapredefinedmessageendpoint.Thistimewewanttoreceiveamessagefromtheendpoint.AgainthetestisnotawareofthetransportdetailssuchasJMSconnections,endpointuri,andsoon.Themessageendpointcomponentencapsulatesthisinformation.

CitrusReferenceGuide

59Endpoints

Page 60: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

BeforewegointodetailonvalidatingthereceivedmessagewehaveaquicklookattheJavaDSLvariationforthereceiveaction.ThesamereceiveactionasabovelookslikethisinJavaDSL.

JavaDSLdesigner

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello");

Thereceiveactionwaitsforamessagetoarrive.Thewholetestexecutionisstoppedwhilewaitingforthemessage.Thisisimportanttoensurethestepbysteptestworkflowprocessing.Ofcourseyoucanspecifymessagetimeoutssothereceiverwillonlywaitagivenamountoftimebeforeraisingatimeouterror.Followingfromthattimeoutexceptionthetestcasefailsasthemessagedidnotarriveintime.Citrusdefinesdefaulttimeoutsettingsforallmessagereceivingtasks.

AtthispointyouknowthetwomostimportanttestactionsinCitrus.Sendingandreceivingactionswillbecomethemaincomponentsofyourintegrationtestswhendealingwithlooselycoupledmessagebasedcomponentsinaenterpriseapplicationenvironment.Itisveryeasytocreatecomplexmessageflows,meaningasequenceofsendingandreceivingactionsinyourtestcase.Youcanreplicateusecasesandtestyourmessageexchangewithextendedmessagevalidationcapabilities.Seeactions-receiveforamoredetaileddescriptiononhowtovalidateincomingmessagesandhowtoexpectmessagecontentsinatestcase.

Localmessagestore

Allmessagesthataresentandreceivedduringatestcasearestoredinalocalmemorystorage.Thisisbecausewemightwanttoaccessthemessagecontentlateroninatestcase.Wecandosobyusingmessagestorefunctionsforloadingmessagesthathavebeenexchangedearlierinthetest.WhenstoringamessageinthelocalstorageCitrususesamessagenameasidentifierkey.Thismessagenameislateronusedtoaccessthemessage.Youcandefinethemessagenameinanysendorreceiveaction:

XMLDSL

CitrusReferenceGuide

60Endpoints

Page 61: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<receiveendpoint="helloServiceEndpoint"><messagename="helloMessage"><payload><TestMessage><Text>Hello!</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/></header></receive>

JavaDSLdesigner

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").name("helloMessage").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello");

ThereceiveoperationabovesetthemessagenametohelloMessage.Themessagereceivedisautomaticallystoredinthelocalstoragewiththatname.Youcanaccessthemessagecontentforinstancebyusingafunction:

<echo><message>citrus:message(helloMessage.payload())</message></echo>

ThefunctionloadsthehelloMessageandprintsthepayloadinformationwiththeechotestaction.IncombinationwithXpathorJsonPathfunctionsthismechanismisagoodwaytoaccesstheexchangedmessagecontentslaterinatestcase.

NoteThestorageisforbothsentandreceivedmessagesinatestcase.Thestorageispertestcaseandcontainsallsentandreceivedmessages.

Whennoexplicitmessagenameisgiventhelocalstoragewillconstructadefaultmessagename.Thedefaultnameisbuiltfromtheaction(sendorreceive)plustheendpointusedtoexchangethemessage.Forinstance:

CitrusReferenceGuide

61Endpoints

Page 62: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

send(helloEndpoint)receive(helloEndpoint)

ThenamesabovewouldbegeneratedbyasendandreceiveoperationontheendpointnamedhelloEndpoint.

ImportantThemessagestoreisnotabletohandlemultiplemessageofthesamenameinonetestcase.Somessageswithidenticalnameswilloverwriteexistingmessagesinthelocalstorage.

NowwehaveseenthebasicendpointconceptinCitrus.Theendpointcomponentsrepresenttheconnectionstothetestboundarysystems.Thisishowwecanconnecttothesystemundertestformessageexchange.Andthisisourmaingoalwiththisintegrationtestframework.Wewanttoprovideeasyaccesstocommonmessagetransportsonclientandserversidesothatwecantestthecommunicationinterfacesonarealmessagetransportexchange.

CitrusReferenceGuide

62Endpoints

Page 63: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

MessagevalidationWhenCitrusreceivesamessagefromexternalapplicationsitistimetoverifythemessagecontent.Thismessagevalidationincludessyntaxrulesaswellassemanticvaluesthatneedtobecomparedtoanexpectedbehavior.Citrusprovidespowerfulmessagevalidationcapabilities.Eachincomingmessageisvalidatedwithsyntaxandsemantics.Thetesterisabletodefineexpectedmessageheadersandpayloads.Citrusmessagevalidatorimplementationswillcomparethemessagesandreportdifferencesastestfailure.WiththeupcomingsectionswehaveacloserlookatmessagevalidationofXMLmessageswithXPathandXMLschemavalidationandfurthermessageformatslikeJSONandplaintext.

JavaDSLvalidationcallbacks

TheJavaDSLofferssomeadditionalvalidationtricksandpossibilitieswhendealingwithmessagesthataresentandreceivedoverCitrus.Oneofthemisthevalidationcallbackfunctionality.Withthisfeatureyoucanmarshal/unmarshalmessagepayloadsandcodevalidationstepsonJavaobjects.

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive(bookResponseEndpoint).validationCallback(newMarshallingValidationCallback<AddBookResponseMessage>()@Overridepublicvoidvalidate(AddBookResponseMessageresponse,MessageHeadersheaders)Assert.isTrue(response.isSuccess()););

BydefaultthevalidationcallbackneedssomeXMLunmarshallerimplementationfortransformingtheXMLpayloadtoaJavaobject.CitruswillautomaticallysearchfortheunmarshallerbeaninyourSpringapplicationcontextifnothingspecificisset.Ofcourseyoucanalsosettheunmarshallerinstanceexplicitly.

JavaDSLdesigner

CitrusReferenceGuide

63Validation

Page 64: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@AutowiredprivateUnmarshallerunmarshaller;

@CitrusTestpublicvoidreceiveMessageTest()receive(bookResponseEndpoint).validationCallback(newMarshallingValidationCallback<AddBookResponseMessage>(unmarshaller)@Overridepublicvoidvalidate(AddBookResponseMessageresponse,MessageHeadersheaders)Assert.isTrue(response.isSuccess()););

ObviouslyworkingonJavaobjectsismuchmorecomfortablethanusingtheXMLStringconcatenation.Thisiswhyyoucanalsousethisfeaturewhensendingmessages.

JavaDSLdesigner

@AutowiredprivateMarshallermarshaller;

@CitrusTestpublicvoidsendMessageTest()send(bookRequestEndpoint).payload(createAddBookRequestMessage("978-citrus:randomNumber(10)"),marshaller).header(SoapMessageHeaders.SOAP_ACTION,"addBook");

privateAddBookRequestMessagecreateAddBookRequestMessage(Stringisbn)AddBookRequestMessagerequestMessage=newAddBookRequestMessage();Bookbook=newBook();book.setAuthor("Foo");book.setTitle("FooTitle");book.setIsbn(isbn);book.setYear(2008);book.setRegistrationDate(Calendar.getInstance());requestMessage.setBook(book);returnrequestMessage;

TheexampleabovecreatesaAddBookRequestMessageobjectandputsthisaspayloadtoasendaction.IncombinationwithamarshallerinstanceCitrusisabletocreateaproperXMLmessagepayloadthen.

CitrusReferenceGuide

64Validation

Page 65: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Customizemessagevalidators

IntheprevioussectionswehavealreadyseensomeexamplesonhowtooverwritedefaultmessagevalidatorimplementationsinCitrus.BydefaultallmessagevalidatorscanbeoverwrittenbyplacingaSpringbeanofthesameidtotheSpringapplicationcontext.ThedefaultimplementationsofCitrusare:

defaultXmlMessageValidator:com.consol.citrus.validation.xml.DomXmlMessageValidatordefaultXpathMessageValidator:com.consol.citrus.validation.xml.XpathMessageValidatordefaultJsonMessageValidator:com.consol.citrus.validation.json.JsonTextMessageValidatordefaultJsonPathMessageValidator:com.consol.citrus.validation.json.JsonPathMessageValidatordefaultPlaintextMessageValidator:com.consol.citrus.validation.text.PlainTextMessageValidatordefaultBinaryBase64MessageValidator:com.consol.citrus.validation.text.BinaryBase64MessageValidatordefaultGzipBinaryBase64MessageValidator:com.consol.citrus.validation.text.GzipBinaryBase64MessageValidatordefaultXhtmlMessageValidator:com.consol.citrus.validation.xhtml.XhtmlMessageValidatordefaultGroovyXmlMessageValidator:com.consol.citrus.validation.script.GroovyXmlMessageValidatordefaultGroovyJsonMessageValidator:com.consol.citrus.validation.script.GroovyJsonMessageValidator

Overwritingasinglemessagevalidatorwithacustomimplementationisthenveryeasy.JustaddyourcustomSpringbeantotheapplicationcontextusingoneofthesedefaultbeanidentifiers.IncaseyouwanttochangethemessagevalidatorgangbyaddingorremovingamessagevalidatorimplementationcompletelyyoucanplaceamessagevalidatorcomponentintheSpringapplicationcontext.

<citrus:message-validators><citrus:validatorref="defaultXmlMessageValidator"/><citrus:validatorref="defaultXpathMessageValidator"/><citrus:validatorref="defaultGroovyXmlMessageValidator"/><citrus:validatorref="defaultPlaintextMessageValidator"/><citrus:validatorref="defaultBinaryBase64MessageValidator"/><citrus:validatorref="defaultGzipBinaryBase64MessageValidator"/>

CitrusReferenceGuide

65Validation

Page 66: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<citrus:validatorclass="com.consol.citrus.validation.custom.CustomMessageValidator"/><citrus:validatorref="defaultJsonMessageValidator"/><citrus:validatorref="defaultJsonPathMessageValidator"/><citrus:validatorref="defaultGroovyJsonMessageValidator"/><citrus:validatorref="defaultXhtmlMessageValidator"/></citrus:message-validators>

ThelistingaboveaddsacustommessagevalidatorimplementationtothesequenceofmessagevalidatorsinCitrus.Wereferencedefaultmessagevalidatorsandaddaimplementationoftypecom.consol.citrus.validation.custom.CustomMessageValidator.Thecustomimplementationclasshastoimplementthebasicinterfacecom.consol.citrus.validation.MessageValidator.NowCitruswilltrytomatchthecustomimplementationtoincomingmessagetypesandoccasionallyexecutethemessagevalidatorlogic.ThisishowyoucanaddandchangethebasicmessagevalidatorregistryinCitrus.Youcanaddcustomimplementationsfornewmessageformatsveryeasy.

Thesameapproachappliesincaseyouwanttoremoveamessagevalidatorimplementationbybanningitcompletely.Justdeletetheentryinthemessagevalidatorregistrycomponent:

<citrus:message-validators><citrus:validatorref="defaultJsonMessageValidator"/><citrus:validatorref="defaultJsonPathMessageValidator"/><citrus:validatorref="defaultGroovyJsonMessageValidator"/></citrus:message-validators>

TheCitrusmessagevalidatorcomponentdeletedalldefaultimplementationsexceptofthosedealingwithJSONmessageformat.NowCitrusisonlyabletovalidateJSONmessages.BecarefulasthecompleteCitrusprojectwillbeaffectedbythischange.

CitrusReferenceGuide

66Validation

Page 67: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLmessagevalidation

XMLisaverycommonmessageformatespeciallyintheSOAPWebServicesandJMSmessagingworld.CitrusprovidesXMLmessagevalidatorimplementationsthatareabletocompareXMLmessagestructures.ThevalidatorwillnoticedifferencesintheXMLmessagestructureandsupportsXMLnamespaces,attributesandXMLschemavalidation.ThedefaultXMLmessagevalidatorimplementationisactivebydefaultandcanbeoverwrittenwithacustomimplementationusingthebeaniddefaultXmlMessageValidator.

<beanid="defaultXmlMessageValidator"class="com.consol.citrus.validation.xml.DomXmlMessageValidator"

ThedefaultXMLmessagevalidatorisverypowerfulwhenitcomestocompareXMLstructures.Thevalidatorsupportsnamespaceswithdifferentprefixesandattributesalswellasnamespacequalifiedattributes.Seethefollowingsectionsforadetaileddescriptionofallcapabilities.

XMLpayloadvalidation

OnceCitrushasreceivedamessagethetestercanvalidatethemessagecontentsinvariousways.Firstofallthetestercancomparethewholemessagepayloadtoapredefinedcontrolmessagetemplate.

Thereceivingactionoffersfollowingelementsforcontrolmessagetemplates:

<payload>:DefinesthemessagepayloadasnestedXMLmessagetemplate.Thewholemessagepayloadisdefinedinsidethetestcase.

<data>:DefinesaninlineXMLmessagetemplateasnestedCDATA.SlightlydifferenttothepayloadvariationaswedefinethewholemessagepayloadinsidethetestcaseasCDATAsection.

<resource>:DefinesanexpectedXMLmessagetemplateviaexternalfileresources.Thistimethepayloadisloadedatruntimefromtheexternalfile.

Bothwaysinlinepayloaddefinitionorexternalfileresourcegiveusacontrolmessagetemplatethatthetestcaseexpectstoarrive.Citrususesthiscontroltemplateforextendedmessagecomparison.Allelements,namespaces,attributesandnodevalues

CitrusReferenceGuide

67Xml

Page 68: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

arevalidatedinthiscomparison.WhenusingXMLmessagepayloadsCitruswillnavigatethroughthewholeXMLstructurevalidatingeachelementanditscontent.SamewithJSONpayloads.

Onlyincasereceivedmessageandcontrolmessageareequaltoeachotherasexpectedthemessagevalidationwillpass.IncasedifferencesoccurCitrusgivesdetailederrormessagesandthetestcasefails.

Thecontrolmessagetemplateisnotnecessarilyverystatic.CitrussupportsvariouswaystoadddynamicmessagecontentontheonesideandontheothersideCitruscanignoresomeelementsthatarenotpartofmessagecomparison(e.g.whengeneratedcontentortimestampsarepartofthemessagecontent).Thetestercanenrichtheexpectedmessagetemplatewithtestvariablesorignoreexpressionssowegetamorerobustvalidationmechanism.Wewilltalkaboutthisinthenextsectionstocome.

WhenusingtheCitrusJavaDSLyouwillfaceaverbosemessagepayloaddefinition.ThisisbecauseJavadoesnotsupportmultilinecharactersequencevaluesasStrings.WehavetouseverboseStringconcatenationwhenconstructingXMLmessagepayloadcontentsforinstance.Inadditiontothatreservedcharacterslikequotesmustbeescapedandlinebreaksmustbeexplicitlyadded.AlltheseimpedimentsletmesuggesttouseexternalfileresourcesinJavaDSLwhendealingwithlargecomplexmessagepayloaddata.Hereisanexample:

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").payload(newClassPathResource("com/consol/citrus/message/data/TestRequest.xml")).header("Operation","sayHello").header("MessageId","$messageId");

XMLheadervalidation

Nowthatwehavevalidatedthemessagepayloadinvariouswayswearenowinterestedinvalidatingthemessageheader.Thisissimpleasyouhavetodefinetheheadernameandthecontrolvaluethatyouexpect.Justaddthefollowingheadervalidationtoyourreceivingaction.

XMLDSL

CitrusReferenceGuide

68Xml

Page 69: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<header><elementname="Operation"value="GetCustomer"/><elementname="RequestTag"value="$requestTag"/></header>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").header("Operation","sayHello").header("MessageId","$messageId");

Messageheadersarerepresentedasname-valuepairs.Eachexpectedheaderelementidentifiedbyitsnamehastobepresentinthereceivedmessage.Inadditiontothattheheadervalueiscomparedtothegivencontrolvalue.IfaheaderentryisnotfoundbyitsnameorthevaluedoesnotfitaccordinglyCitruswillraisevalidationerrorsandthetestcasewillfail.

NoteSometimesmessageheadersmaynotapplytothename-valuepairpattern.ForexampleSOAPheaderscanalsocontainXMLfragments.Citrussupportsthesekindofheaderstoo.PleaseseetheSOAPchapterformoredetails.###IgnoreXMLelements

Someelementsinthemessagepayloadmightnotapplyforvalidationatall.Justthinkofcommunicationtimestampsandynamicvaluesinsideamessage:

Thetimestampvalueinournextexamplewilldynamicallychangefromtestruntotestrunandishardlypredictableforthetester,soletsignoreitinvalidation.

XMLDSL

<message><payload><TestMessage><MessageId>$messageId</MessageId><Timestamp>2001-12-17T09:30:47.0Z</Timestamp><VersionId>@ignore@</VersionId></TestMessage></payload><ignorepath="/TestMessage/Timestamp"/></message>

CitrusReferenceGuide

69Xml

Page 70: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AlthoughwehavegivenastatictimestampvalueinthepayloaddatatheelementisignoredduringvalidationastheignoreXPathexpressionmatchestheelement.Inadditiontothatwealsoignoredtheversionidelementinthisexample.Thistimewithaninline@ignore@expression.ThisisforthoseofyouthatdonotlikeXPath.AsaresulttheignoredmessageelementsareautomaticallyskippedwhenCitruscomparesandvalidatesmessagecontentsanddonotbreakthetestcase.

WhenusingtheJavaDSLthe@ignore@placeholderaswellasXPathexpressionscanbeusedseamlessly.Hereisanexampleofthat:

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").payload(newClassPathResource("com/consol/citrus/message/data/TestRequest.xml")).header("Operation","sayHello").header("MessageId","$messageId").ignore("/TestMessage/Timestamp");

Ofcourseyoucanusetheinline@ignore@placeholderinanexternalfileresource,too.

CustomizeXMLparserandserializer

WhenworkingwithXMLdataformatparsingandserializingisacommontask.XMLstructuresareparsedtoaDOM(DocumentObjectModel)representationinordertoprocesselements,attributesandtextnodes.AlsoDOMnodeobjectsgetserializedtoaStringmessagepayloadrepresentation.TheXMLparserandserializeriscustomizabletoacertainlevel.BydefaultCitrususestheDOMLevel3LoadandSaveimplementationwithfollowingsettings:

Parsersettings

cdata-sections=truesplit-cdata-sections=falsevalidate-if-schema=trueelement-content-whitespace=false

Serializersettings

format-pretty-print=truesplit-cdata-sections=false

CitrusReferenceGuide

70Xml

Page 71: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

element-content-whitespace=true

TheparametersarealsodescribedinW3CDOMconfigurationdocumentation.WecancustomizethedefaultsettingsbyaddingaXmlConfigurerSpringbeantotheCitrusapplicationcontext.

<beanid="xmlConfigurer"class="com.consol.citrus.xml.XmlConfigurer"><propertyname="parseSettings"><map><entrykey="validate-if-schema"value="false"value-type="java.lang.Boolean"/></map></property><propertyname="serializeSettings"><map><entrykey="comments"value="false"value-type="java.lang.Boolean"/><entrykey="format-pretty-print"value="false"value-type="java.lang.Boolean"/></map></property></bean>

NoteThisconfigurationisofglobalnature.AllXMLprocessingoperationswillbeaffectedwiththisconfiguration.

GroovyXMLvalidation

WiththeGroovyXmlSlurperyoucaneasilyvalidateXMLmessagepayloadswithouthavingtodealdirectlywithXML.PeoplewhodonotwanttodealwithXPathmayalsolikethisvalidationalternative.Thetesterdirectlynavigatesthroughthemessageelementsandusessimplecodeassertionsinordertocontrolthemessagecontent.HereisanexamplehowtovalidatemessageswithGroovyscript:

XMLDSL

<receiveendpoint="helloServiceClient"timeout="5000"><message><validate><scripttype="groovy">assertroot.children().size()==4assertroot.MessageId.text()=='$messageId'assertroot.CorrelationId.text()=='$correlationId'assertroot.User.text()=='HelloService'assertroot.Text.text()=='Hello'+context.getVariable("user")</script></validate>

CitrusReferenceGuide

71Xml

Page 72: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</message><header><elementname="Operation"value="sayHello"/><elementname="CorrelationId"value="$correlationId"/></header></receive>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceClient").validateScript("assertroot.MessageId.text()=='$messageId';"+"assertroot.CorrelationId.text()=='$correlationId';").header("Operation,"sayHello").header("CorrelationId","$correlationId").timeout(5000L);

TheGroovyXmlSlurpervalidationscriptgoesrightintothemessage-taginsteadofaXMLcontroltemplateorXPathvalidation.TheGroovyscriptsupportsJavaassertstatementsformessageelementvalidation.Citrusautomaticallyinjectstherootelementroottothevalidationscript.ThisistheGroovyXmlSlurperobjectandthestartofelementnavigation.Basedonthisrootelementyoucanaccesschildelementsandattributeswithadotnotatedsyntax.Justusetheelementnamesseparatedbyasimpledot.Veryeasy!Ifyouneedthelistofchildelementsusethechildren()functiononanyelement.Withthetext()functionyougetaccesstotheelement'stext-value.Thesize()isveryusefulforvalidatingthenumberofchildelementswhichcompletesthebasicvalidationstatements.

Asyoucanseefromtheexample,wemayusetestvariableswithinthevalidationscript,too.Citrushasalsoinjectedtheactualtestcontexttothevalidationscript.Thetestcontextobjectholdsalltestvariables.Soyoucanalsoaccessvariableswithcontext.getVariable("user")forinstance.Onthetestcontextyoucanalsosetnewvariablevalueswithcontext.setVariable("user","newUserName").

Thereisevenmoreobjectinjectionforthevalidationscript.WiththeautomaticallyaddedobjectreceivedMessageYouhaveaccesstotheCitrusmessageobjectforthisreceiveaction.Thisenablesyoutodowhateveryouwantwiththemessagepayloadorheader.

XMLDSL

<receiveendpoint="helloServiceClient"timeout="5000">

CitrusReferenceGuide

72Xml

Page 73: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<message><validate><scripttype="groovy">assertreceivedMessage.getPayload(String.class).contains("HelloCitrus!")assertreceivedMessage.getHeader("Operation")=='sayHello'

context.setVariable("request_payload",receivedMessage.getPayload(String.class</script></validate></message></receive>

Thelistingaboveshowssomepowerofthevalidationscript.Wecanaccessthemessagepayload,wecanaccessthemessageheader.Withtestcontextaccesswecanalsosavethewholemessagepayloadasanewtestvariableforlaterusageinthetest.

IngeneralGroovycodeinsidetheXMLtestcasedefinitionoraspartoftheJavaDSLcodeisnotverycomfortabletomaintain.Youdonothavecodesyntaxassistorcodecompletion.Thisiswhywecanalsouseexternalfileresourcesforthevalidationscripts.Thesyntaxlookslikefollows:

XMLDSL

<receiveendpoint="helloServiceClient"timeout="5000"><message><validate><scripttype="groovy"file="classpath:validationScript.groovy"/></validate></message><header><elementname="Operation"value="sayHello"/><elementname="CorrelationId"value="$correlationId"/></header></receive>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceClient").validateScript(newFileSystemResource("validationScript.groovy")).header("Operation,"sayHello").header("CorrelationId","$correlationId").timeout(5000L);

CitrusReferenceGuide

73Xml

Page 74: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WereferencedsomeexternalfileresourcevalidationScript.groovy.Thisfilecontentisloadedatruntimeandisusedasscriptbody.NowthatwehaveanormalgroovyfilewecanusethecodecompletionandsyntaxhighlightingofourfavoriteGroovyeditor.

NoteYoucanusetheGroovyvalidationscriptincombinationwithothervalidationtypeslikeXMLtreecomparisonandXPathvalidation.TipForfurtherinformationontheGroovyXmlSlurperpleaseseetheofficialGroovywebsiteanddocumentation

CitrusReferenceGuide

74Xml

Page 75: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLschemavalidation

ThereareseveralpossibilitiestodescribethestructureofXMLdocuments.ThetwomostpopularwaysareDTD(Documenttypedefinition)andXSD(XMLSchemadefinition).OnceaXMLdocumenthasdecidedtobeclassifiedusingaschemadefinitionthestructureofthedocumenthastofitthepredefinedrulesinsidetheschemadefinition.XMLdocumentinstancesarevalidonlyincasetheymeetallthesestructurerulesdefinedintheschemadefinition.CurrentlyCitruscanvalidateXMLdocumentsusingtheschemalanguagesDTDandXSD.

XSDschemarepositories

CitrustriestovalidateallincomingXMLmessagesagainstaschemadefinitioninordertoensurethatallrulesarefulfilled.AsaconsequencethemessagereceivingactionsinCitrusdohavetoknowtheXMLschemadefinition(*.xsd)fileresourcesthatbelongtoourproject.ThereforeCitrusintroducesacentralschemarepositorycomponentwhichholdsallavailableXMLschemafilesforaproject.

<citrus:schema-repositoryid="schemaRepository"><citrus:schemas><citrus:schemaid="travelAgencySchema"location="classpath:citrus/flightbooking/TravelAgencySchema.xsd"/><citrus:schemaid="royalArilineSchema"location="classpath:citrus/flightbooking/RoyalAirlineSchema.xsd"/><citrus:referenceschema="smartArilineSchema"/></citrus:schemas></citrus:schema-repository>

<citrus:schemaid="smartArilineSchema"location="classpath:citrus/flightbooking/SmartAirlineSchema.xsd"/>

AsyoucanseetheschemarepositoryisasimpleXMLcomponentdefinedinsidetheSpringapplicationcontext.Therepositorycanholdnestedschemadefinitionsdefinedbysomeidentifierandafilelocationforthexsdschemafile.Schemadefinitionscanalsobereferencedbyitsidentifierforusageinseveralschemarepositoryinstances.

ByconventionthedefaultschemarepositorycomponentisdefinedintheCitrusSpringapplicationcontextwiththeidschemaRepository.Springapplicationcontextisthenabletoinjecttheschemarepositoryintoallmessagereceivingtestactionsatruntime.ThereceivingtestactionconsolidatestherepositoryforamatchingschemadefinitionfileinordertovalidatetheincomingXMLdocumentstructure.

CitrusReferenceGuide

75Schema

Page 76: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheconnectionbetweenincomingXMLmessagesandxsdschemafilesintherepositoryisdonebyamappingstrategywhichwewilldiscusslaterinthischapter.BydefaultCitruspickstherightschemabasedonthetargetnamespacethatisdefinedinsidetheschemadefinition.ThetargetnamespaceoftheschemadefinitionhastomatchthenamespaceoftherootelementinthereceivedXMLmessage.WiththismappingstrategyyouwillnothavetowireXMLmessagesandschemafilesmanuallyallisdoneautomaticallybytheCitrusschemarepositoryatruntime.AllyouneedtodoistoregisterallavailableschemadefinitionfilesregardlessofwhichtargetnamespaceornatureinsidetheCitrusschemarepository.

ImportantXMlschemavalidationismandatoryinCitrus.ThismeansthatCitrusalwaystriestofindamatchingschemadefinitioninsidetheschemarepositoryinordertoperformsyntaxvalidationonincomingschemaqualifiedXMLmessages.AclassifiedXMLmessageisdefinedbyitsnamespacedefinitions.Consequentlyyouwillgetvalidationerrorsincasenomatchingschemadefinitionfileisfoundinsidetheschemarepository.SoifyouexplicitlydonotwanttovalidatetheXMLschemaforsomereasonyouhavetodisablethevalidationexplicitlyinyourtestwithschema-validation="false".

<receiveendpoint="httpMessageEndpoint"><messageschema-validation="false"><validate><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"value="$messageId"/><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"value="$correlationId"/><namespaceprefix="ns1"value="http://citrus.com/namespace"/></validate></message><header><elementname="Operation"value="sayHello"/><elementname="MessageId"value="$messageId"/></header></receive>

ThismandatoryschemavalidationmightsoundannoyingtoyoubutinouropinionitisveryimportanttovalidatethestructureofthereceivedXMLmessages,sodisablingtheschemavalidationshouldnotbethestandardforalltests.Disablingautomaticschemavalidationshouldonlyapplytoveryspecialsituations.Sopleasetrytoputallavailableschemadefinitionstotheschemarepositoryandyouwillbefine.

WSDLschemas

CitrusReferenceGuide

76Schema

Page 77: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

InSOAPWebServicesworldtheWSDL(WebServiceSchemaDefinitionLanguage)definesthestructureannatureoftheXMLmessagesexchangedacrosstheinterface.OftentheWSDLfilesdoholdtheXMLschemadefinitionsasnestedelements.InCitrusyoucandirectlysettheWSDLfileaslocationofaschemadefinitionlikethis:

<citrus:schemaid="arilineWsdl"location="classpath:citrus/flightbooking/AirlineSchema.wsdl"/>

CitrusisabletofindthenestedschemadefinitionsinsidetheWSDLfileinordertobuildavalidschemafilefortheschemarepository.SoincomingXMLmessagesthatrefertotheWSDLfilecanbevalidatedforsyntaxrules.

Schemalocationpatterns

Settingallschemasonebyoneinaschemarepositorycanbeverboseanduncomfortable,especiallywhendealingwithlotsofxsdandwsdlfiles.Theschemarepositoryalsosupportslocationpatternexpressions.Seethisexampletoseehowitworks:

<citrus:schema-repositoryid="schemaRepository"><citrus:locations><citrus:locationpath="classpath:citrus/flightbooking/*.xsd"/></citrus:locations></citrus:schema-repository>

Theschemarepositorysearchesforallfilesmatchingtheresourcepathlocationpatternandaddsthemasschemainstancestotherepository.OfcoursethisalsoworkswithWSDLfiles.

Schemacollections

Sometimesmultipleaschemadefinitionisseparatedintomultiplefiles.ThisisaproblemfortheCitrusschemarepositoryastheschemamappingstrategythenisnotabletopicktherightfileforvalidation,inparticularwhenworkingwithtargetnamespacevaluesaskeyfortheschemamappingstrategy.Asasolutionforthisproblemyouhavetoputallschemaswiththesametargetnamespacevalueintoaschemacollection.

<citrus:schema-collectionid="flightbookingSchemaCollection"><citrus:schemas>

CitrusReferenceGuide

77Schema

Page 78: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<citrus:schemalocation="classpath:citrus/flightbooking/BaseTypes.xsd"/><citrus:schemalocation="classpath:citrus/flightbooking/AirlineSchema.xsd"/></citrus:schemas></citrus:schema-collection>

BothschemadefinitionsBaseTypes.xsdandAirlineSchema.xsdsharethesametargetnamespaceandthereforeneedtobecombinedinschemacollectioncomponent.Theschemacollectioncanbereferencedinanyschemarepositoryasnormalschemadefinition.

<citrus:schema-repositoryid="schemaRepository"><citrus:schemas><citrus:referenceschema="flightbookingSchemaCollection"/></citrus:schemas></citrus:schema-repository>

Schemamappingstrategy

TheschemarepositoryinCitrusholdsonetomanyschemadefinitionfilesanddynamicallypicksuptherightoneaccordingtothevalidatedmessagepayload.Therepositoryneedstohavesomestrategyfordecidingwhichschemadefinitiontochoose.Seethefollowingschemamappingstrategiesanddecidewhichofthemissuitableforyou.

TargetNamespaceMappingStrategy

Thisisthedefaultschemamappingstrategy.Schemadefinitionsusuallydefinesometargetnamespacewhichisvalidforallelementsandtypesinsidetheschemafile.ThetargetnamespaceisalsousedasrootnamespaceinXMLmessagepayloads.AccordingtothisinformationCitruscanpickuptherightschemadefinitionfileintheschemarepository.Youcansettheschemamappingstrategyaspropertyintheconfigurationfiles:

<citrus:schema-repositoryid="schemaRepository"schema-mapping-strategy="schemaMappingStrategy"><citrus:schemas><citrus:schemaid="helloSchema"location="classpath:citrus/samples/sayHello.xsd"/></citrus:schemas></citrus:schema-repository>

<beanid="schemaMappingStrategy"

CitrusReferenceGuide

78Schema

Page 79: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

class="com.consol.citrus.xml.schema.TargetNamespaceSchemaMappingStrategy"/>

ThesayHello.xsdschemafiledefinesatargetnamespace(http://consol.de/schemas/sayHello.xsd):

<xs:schemaxmlns:xs="http://www.w3.org/2001/XMLSchema"xmlns="http://consol.de/schemas/sayHello.xsd"targetNamespace="http://consol.de/schemas/sayHello.xsd"elementFormDefault="qualified"attributeFormDefault="unqualified">

</xs:schema>

IncomingrequestmessagesshouldalsohavethetargetnamespacesetintherootelementandthisishowCitrusmatchestherightschemafileintherepository.

<HelloRequestxmlns="http://consol.de/schemas/sayHello.xsd"><MessageId>123456789</MessageId><CorrelationId>1000</CorrelationId><User>Christoph</User><Text>HelloCitrus</Text></HelloRequest>

RootQNameMappingStrategy

ThenextpossibilityformappingincomingrequestmessagestoaschemadefinitionisviatheXMLrootelementQName.EachXMLmessagepayloadstartswitharootelementthatusuallydeclaresthetypeofaXMLmessage.Accordingtothisrootelementyoucansetupmappingsintheschemarepository.

<citrus:schema-repositoryid="schemaRepository"schema-mapping-strategy="schemaMappingStrategy"><citrus:schemas><citrus:referenceschema="helloSchema"/><citrus:referenceschema="goodbyeSchema"/></citrus:schemas></citrus:schema-repository>

<beanid="schemaMappingStrategy"class="com.consol.citrus.xml.schema.RootQNameSchemaMappingStrategy"><propertyname="mappings"><map><entrykey="HelloRequest"value="helloSchema"/><entrykey="GoodbyeRequest"value="goodbyeSchema"/>

CitrusReferenceGuide

79Schema

Page 80: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</map></property></bean>

<citrus:schemaid="helloSchema"location="classpath:citrus/samples/sayHello.xsd"/>

<citrus:schemaid="goodbyeSchema"location="classpath:citrus/samples/sayGoodbye.xsd"/>

Thelistingabovedefinestworootqnamemappings-oneforHelloRequestandoneforGoodbyeRequestmessagetypes.Anincomingmessageoftypeisthenmappedtotherespectiveschemaandsoon.WiththisdedicatedmappingsyouareabletocontrolwhichschemaisusedonaXMLrequest,regardlessoftargetnamespacedefinitions.

Schemamappingstrategychain

Let'sdiscussthepossibilitytocombineseveralschemamappingstrategiesinalogicalchain.Youcandefinemorethanonemappingstrategythatareevaluatedinsequence.Thefirststrategytofindaproperschemadefinitionfileintherepositorywins.

<citrus:schema-repositoryid="schemaRepository"schema-mapping-strategy="schemaMappingStrategy"><citrus:schemas><citrus:referenceschema="helloSchema"/><citrus:referenceschema="goodbyeSchema"/></citrus:schemas></citrus:schema-repository>

<beanid="schemaMappingStrategy"class="com.consol.citrus.xml.schema.SchemaMappingStrategyChain"><propertyname="strategies"><list><beanclass="com.consol.citrus.xml.schema.RootQNameSchemaMappingStrategy"><propertyname="mappings"><map><entrykey="HelloRequest"value="helloSchema"/></map></property></bean><beanclass="com.consol.citrus.xml.schema.TargetNamespaceSchemaMappingStrategy"/></list></property></bean>

CitrusReferenceGuide

80Schema

Page 81: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SotheschemamappingchainusesbothRootQNameSchemaMappingStrategyandTargetNamespaceSchemaMappingStrategyincombination.Incasethefirstrootqnamestrategyfailstofindapropermappingthenexttargetnamespacestrategycomesinandtriestofindaproperschema.

Schemadefinitionoverruling

Nowitistimetotalkaboutschemadefinitionsettingsontestactionlevel.WehavelearnedbeforethatCitrustriestoautomaticallyfindamatchingschemadefinitioninsomeschemarepository.Therecomesatimewhereyouasatesterjusthavetopicktherightschemadefinitionbyyourself.YoucanoverruleallschemamappingstrategiesinCitrusbydirectlysettingthedesiredschemainyourreceivingmessageaction.

<receiveendpoint="httpMessageEndpoint"><messageschema="helloSchema"><validate><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"value="$messageId"/><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"value="$correlationId"/><namespaceprefix="ns1"value="http://citrus.com/namespace"/></validate></message></receive>

<citrus:schemaid="helloSchema"location="classpath:citrus/samples/sayHello.xsd"/>

Intheexampleabovethetesterexplicitlysetsaschemadefinitioninthereceiveaction(schema="helloSchema").Theattributevaluereferstonamedschemabeansomewhereintheapplciationcontext.Thisoverrulesallschemamappingstrategiesusedinthecentralschemarepositoryasthegivenschemaisdirectlyusedforvalidation.Thisfeatureishelpfulwhendealingwithdifferentschemaversionsatthesametimewheretheschemarepositorycannothelpyouanymore.

Anotherpossibilitywouldbetosetacustomschemarepositoryatthispoint.ThismeansyoucanhavemorethanoneschemarepositoryinyourCitrusprojectandyoupicktherightonebyyourselfinthereceiveaction.

<receiveendpoint="httpMessageEndpoint"><messageschema-repository="mySpecialSchemaRepository"><validate><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:MessageId"

CitrusReferenceGuide

81Schema

Page 82: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

value="$messageId"/><xpathexpression="//ns1:TestMessage/ns1:MessageHeader/ns1:CorrelationId"value="$correlationId"/><namespaceprefix="ns1"value="http://citrus.com/namespace"/></validate></message></receive>

Theschema-repositoryattributereferstoaCitrusschemarepositorycomponentwhichisdefinedsomewhereintheSpringapplicationcontext.

ImportantIncaseyouhaveseveralschemarepositoriesinyourprojectdoalwaysdefineadefaultrepository(name="schemaRepository").ThishelpsCitrustoalwaysfindatleastonerepositorytointeractwith.

DTDvalidation

XMLDTD(Documenttypedefinition)isanotherwaytovalidatethestructureofaXMLdocument.ManypeoplesaythatDTDisdeprecatedandXMLschemaisthemuchmoreefficientwaytodescribetherulesofaXMLstructure.Wedonotdisagreewiththat,butwealsoknowthatlegacysystemsmightstilluseDTD.SoinordertoavoidvalidationerrorswehavetodealwithDTDvalidationaswell.

FirstthingyoucandoaboutDTDvalidationistospecifyaninlineDTDinyourexpectedmessagetemplate.

<receiveendpoint="httpMessageEndpoint"><messageschema-validation="false"><data><![CDATA[<!DOCTYPEroot[<!ELEMENTroot(message)><!ELEMENTmessage(text)><!ELEMENTtext(#PCDATA)>]><root><message><text>HelloTestFramework!</text></message></root>]]><data/></message></receive>

CitrusReferenceGuide

82Schema

Page 83: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThesystemundertestmayalsosendthemessagewithainlineDTDdefinition.Sovalidationwillsucceed.

InmostcasestheDTDisreferencedasexternal.dtdfileresource.Youcandothisinyourexpectedmessagetemplateaswell.

<receiveendpoint="httpMessageEndpoint"><messageschema-validation="false"><data><![CDATA[<!DOCTYPErootSYSTEM"com/consol/citrus/validation/example.dtd"><root><message><text>HelloTestFramework!</text></message></root>]]><data/></message></receive>

CitrusReferenceGuide

83Schema

Page 84: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JSONmessagevalidation

MessageformatssuchasJSONhavebecomeverypopular,inparticularwhenspeakingofRESTfulWebServicesandJavaScriptusingJSONasthemessageformattogofor.CitrusisabletoexpectandvalidateJSONmessagesaswewillseeinthenextsections.

ImportantBydefaultCitruswilluseXMLmessageformatswhensendingandreceivingmessages.ThisalsoreflectstothemessagevalidationlogicCitrususesforincomingmessages.SobydefaultCitruswilltrytoparsetheincomingmessageasXMLDOMelementtree.IncasewewouldliketoenableJSONmessagevalidationwehavetotellCitrusthatweexpectaJSONmessagerightnow.

Andthisisquiteeasy.CitrushasaJSONmessagevalidatorimplementationactivebydefaultandimmediatelyaswemarkanincomingmessageasJSONdatathismessagevalidatorwilljumpin.

CitrusprovidesseveraldefaultmessagevalidatorimplementationsforJOSNmessageformat:

com.consol.citrus.validation.json.JsonTextMessageValidator:BasicJSONmessagevalidatorimplementationcomparesJSONobjects(expectedandreceived).TheorderofJSONentriescandifferasspecifiedinJSONprotocol.TesterdefinesanexpectedcontrolJSONobjectwithtestvariablesandignoredentries.JSONArrayaswellasnestedJSONObjectsaresupported,too.TheJSONvalidatorofferstwodifferentmodestooperate.Bydefaultstrictmodeissetandthevalidatorwillalsochecktheexactamountofcontrolobjectfieldstomatch.NoadditionalfieldsinreceivedJSONdatastructurewillbeaccepted.InsoftmodevalidatorallowsadditionalfieldsinreceivedJSONdatastructuresothecontrolJSONobjectcanbeapartialsubsetinwhichcaseonlythecontrolfieldsarevalidated.AdditionalfieldsinthereceivedJSONdatastructureareignoredthen.

com.consol.citrus.validation.script.GroovyJsonMessageValidator:ExtendedgroovymessagevalidatorprovidesspecificJSONslurpersupport.WithJSONslurperthetestercanvalidatetheJSONmessagepayloadwithclosuresforinstance.

YoucanoverwritethisdefaultmessagevalidatorsforJSONbyplacingabeanintotheSpringApplicationcontext.Thebeanusesadefaultnameasidentifier.Thenyourcustombeanwilloverwritethedefaultvalidator:

<beanid="defaultJsonMessageValidator"class="com.consol.citrus.validation.json.JsonTextMessageValidator"

CitrusReferenceGuide

84Json

Page 85: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<beanid="defaultGroovyJsonMessageValidator"class="com.consol.citrus.validation.script.GroovyJsonMessageValidator"

ThisishowyoucancustomizethemessagevalidatorsusedforJSONmessagedata.

WehavementionedbeforethatCitrusisworkingwithXMLbydefault.ThisiswhywehavetotellCitrusthatthemessagethatwearereceivingusestheJSONmessageformat.WehavetotellthetestcasereceivingactionthatweexpectadifferentformatotherthanXML.

<receiveendpoint="httpMessageEndpoint"><messagetype="json"><data>"type":"read","mbean":"java.lang:type=Memory","attribute":"HeapMemoryUsage","path":"@equalsIgnoreCase('USED')@","value":"$heapUsage","timestamp":"@ignore@"</data></message></receive>

Themessagereceivingactioninourtestcasespecifiesamessageformattypetype="json".ThistellsCitrustolookforsomemessagevalidatorimplementationcapableofvalidatingJSONmessages.AswehaveaddedthepropermessagevalidatortotheSpringapplicationcontextCitruswillpicktherightvalidatorandJSONmessagevalidationisperformedonthismessage.Asyoucanseeyouwecanusetheusualtestvariablesandtheignoreelementsyntaxhere,too.CitrusisabletohandledifferentJSONelementorderswhencomparingreceivedandexpectedJSONobject.WecanalsouseJSONarraysandnestedobjects.ThedefaultJSONmessagevalidatorimplementationinCitrusisverypowerfulincomparingJSONobjects.

InsteadofdefininganexpectedmessagepayloadtemplatewecanalsouseGroovyvalidationscripts.LetshavealookattheGroovyJSONmessagevalidatorexample.AsusualthedefaultGroovyJSONmessagevalidatorisactivebydefault.ButthespecialGroovymessagevalidatorimplementationwillonlyjumpinwhenweusedavalidationscriptinourreceivemessagedefinition.Let'shaveanexampleforthat.

CitrusReferenceGuide

85Json

Page 86: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<receiveendpoint="httpMessageEndpoint"><messagetype="json"><validate><scripttype="groovy">

</script></validate></message></receive>

AgainwetellCitrusthatweexpectamessageoftype="json".NowweusedavalidationscriptthatiswritteninGroovy.CitruswillautomaticallyactivatethespecialmessagevalidatorthatexecutesourGroovyscript.ThescriptvalidationismorepowerfulaswecanusethefullpoweroftheGroovylanguage.ThevalidationscriptautomaticallyhasaccesstotheincomingJSONmessageobjectjson.WecanusetheGroovyJSONdotnotatedsyntaxinordertonavigatethroughtheJSONstructure.TheGroovyJSONslurperobjectjsonisautomaticallypassedtothevalidationscript.ThiswayyoucanaccesstheJSONobjectelementsinyourcodedoingsomeassertions.

Thereisevenmoreobjectinjectionforthevalidationscript.WiththeautomaticallyaddedobjectreceivedMessageYouhaveaccesstotheCitrusmessageobjectforthisreceiveaction.Thisenablesyoutodowhateveryouwantwiththemessagepayloadorheader.

XMLDSL

<receiveendpoint="httpMessageEndpoint"><messagetype="json"><validate><scripttype="groovy">assertreceivedMessage.getPayload(String.class).contains("HelloCitrus!")assertreceivedMessage.getHeader("Operation")=='sayHello'

context.setVariable("request_payload",receivedMessage.getPayload(String.class</script></validate></message></receive>

CitrusReferenceGuide

86Json

Page 87: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thelistingaboveshowssomepowerofthevalidationscript.Wecanaccessthemessagepayload,wecanaccessthemessageheader.Withtestcontextaccesswecanalsosavethewholemessagepayloadasanewtestvariableforlaterusageinthetest.

IngeneralGroovycodeinsidetheXMLtestcasedefinitionoraspartoftheJavaDSLcodeisnotverycomfortabletomaintain.Youdonothavecodesyntaxassistorcodecompletion.Thisiswhywecanalsouseexternalfileresourcesforthevalidationscripts.Thesyntaxlookslikefollows:

XMLDSL

<receiveendpoint="helloServiceClient"timeout="5000"><message><validate><scripttype="groovy"file="classpath:validationScript.groovy"/></validate></message></receive>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceClient").validateScript(newFileSystemResource("validationScript.groovy"));

WereferencedsomeexternalfileresourcevalidationScript.groovy.Thisfilecontentisloadedatruntimeandisusedasscriptbody.NowthatwehaveanormalgroovyfilewecanusethecodecompletionandsyntaxhighlightingofourfavoriteGroovyeditor.

ImportantUsingseveralmessagevalidatorimplementationsatthesametimeintheSpringapplicationcontextisalsonoproblem.Citrusautomaticallysearchesforallavailablemessagevalidatorsapplicableforthegivenmessageformatandexecutesthesevalidatorsinsequence.SoseveralmessagevalidatorscancoexistinaCitrusproject.

WhenwehavemultiplemessagevalidatorsthatapplytothemessageformatCitruswillexecutealloftheminsequence.Incaseyouneedtoexplicitlychooseamessagevalidatorimplementationyoucandosointhereceiveaction:

<receiveendpoint="httpMessageEndpoint"><messagetype="json"validator="groovyJsonMessageValidator">

CitrusReferenceGuide

87Json

Page 88: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<validate><scripttype="groovy">

</script></validate></message></receive>

InthisexampleweusethegroovyJsonMessageValidatorexplicitlyinthereceivetestaction.ThemessagevalidatorimplementationwasaddedasSpringbeanwithidgroovyJsonMessageValidatortotheSpringapplicationcontextbefore.NowCitruswillonlyexecutetheexplicitmessagevalidator.Otherimplementationsthatmightalsoapplyareskipped.

TipBydefaultCitruswillconsolidateallavailablemessagevalidatorsforamessageformatinsequence.Youcanexplicitlypickaspecialmessagevalidatorinthereceivemessageactionasshownintheexampleabove.Inthiscaseallothervalidatorswillnottakepartinthisspecialmessagevalidation.Butbecareful:Whenpickingamessagevalidatorexplicitlyyouareofcourselimitedtothismessagevalidatorcapabilities.Validationfeaturesofothervalidatorsarenotvalidinthiscase(e.g.messageheadervalidation,XPathvalidation,etc.)

SomuchforreceivingJSONmessagedatainCitrus.OfcoursesendingJSONmessagesinCitrusisalsoveryeasy.JustuseJSONmessagepayloadsinyoursendingmessageaction.

<sendendpoint="httpMessageEndpoint"><message><data>"type":"read","mbean":"java.lang:type=Memory","attribute":"HeapMemoryUsage","path":"used"</data></message></send>

CitrusReferenceGuide

88Json

Page 89: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

89Json

Page 90: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XHTMLmessagevalidation

WhenCitrusreceivesplainHtmlmessageswelikelywanttousethepowerfulXMLvalidationcapabilitiessuchasXMLtreecomparisonorXPathsupport.UnfortunatelyHtmlmessagesdonotfollowtheXMLwellformedrulesverystrictly.ThisimpliesthatXMLmessagevalidationwillfailbecauseofnonwellformedHtmlcode.

XHTMLclosesthisgapbyautomaticallyfixingthemostcommonHtmlXMLincompatibleruleviolationssuchasmissingendtags(e.g.).

Let'strythiswithasimpleexample.Veryfirstthingforustodoistoaddanewlibrarydependencytotheproject.CitrusisusingthejtidylibraryinordertopreparetheHTMLandXHTMLmessagesforvalidation.Asthis3rdpartydependencyisoptionalinCitruswehavetoadditnowtoourprojectdependencylist.JustaddthejtidydependencytoyourMavenprojectPOM.

<dependency><groupId>net.sf.jtidy</groupId><artifactId>jtidy</artifactId><version>r938</version></dependency>

Pleaserefertothejtidyprojectdocumentationforthelatestversions.Noweverythingisready.AsusualtheCitrusmessagevalidatorforXHTMLisactiveinbackgroundbydefault.YoucanoverwritethisdefaultimplementationbyplacingaSpringbeanwithiddefaultXhtmlMessageValidatortotheCitrusapplicationcontext.

<beanid="defaultXhtmlMessageValidator"class="com.consol.citrus.validation.xhtml.XhtmlMessageValidator"

NowwecantellthetestcasereceivingactionthatwewanttousetheXHTMLmessagevalidationinourtestcase.

<receiveendpoint="httpMessageEndpoint"><messagetype="xhtml"><data><![CDATA[<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.1//EN""org/w3c/xhtml/xhtml1-strict.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"><head>

CitrusReferenceGuide

90Xhtml

Page 91: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<title>CitrusHelloWorld</title></head><body><h1>HelloWorld!</h1><br/><p>Thisisatest!</p></body>]]></data></message></receive>

Themessagereceivingactioninourtestcasehastospecifyamessageformattypetype="xhtml".AsyoucanseetheHtmlmessagepayloadgetXHTMLspecificDOCTYPEprocessinginstruction.Thexhtml1-strict.dtdismandatoryintheXHTMLmessagevalidation.ForbetterconvenienceallXHTMLdtdfilesarepackagedwithinCitrussoyoucanusethisasarelativepath.

TheincomingHtmlmessageisautomaticallyconvertedintoproperXHTMLcodewithwellformedXML.SonowtheXHTMLmessagevalidatorcanusetheXMLmessagevalidationmechanismofCitrusforcomparingreceivedandexpecteddata.Asusualyoucanusetestvariables,ignoreelementexpressionsandXPathexpressions.

CitrusReferenceGuide

91Xhtml

Page 92: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Plaintextmessagevalidation

PlaintextmessagevalidationistheeasiestvalidationinCitrusthatyoucanthinkof.ThisvalidationjustperformsanexactJavaStringmatchofreceivedandexpectedmessagepayloads.

Asusualadefaultmessagevalidatorforplaintextmessagesisactivebydefault.Citruswillpickthismessagevalidatorforallmessagesoftype="plaintext".ThedefaultmessagevalidatorimplementationcanbeoverwrittenbyplacingaSpringbeanwithiddefaultPlaintextMessageValidatortotheSpringapplicationcontext.

<beanid="defaultPlaintextMessageValidator"class="com.consol.citrus.validation.text.PlainTextMessageValidator"

InthetestcasereceivingactionwetellCitrustouseplaintextmessagevalidation.

<receiveendpoint="httpMessageEndpoint"><messagetype="plaintext"><data>HelloWorld!</data></message></receive>

Withthemessageformattypetype="plaintext"setCitrusperformsStringequalsonthemessagepayloads(receivedandexpected).Onlyexactmatchwillpassthetest.

BythewaysendingplaintextmessagesinCitrusisalsoveryeasy.Justusetheplaintextmessagepayloaddatainyoursendingmessageaction.

<sendendpoint="httpMessageEndpoint"><message><data>HelloWorld!</data></message></send>

Ofcoursetestvariablesaresupportedintheplaintextpayloads.Thevariablesarereplacebythereferencedvaluesbeforesendingorreceivingthemessage.

CitrusReferenceGuide

92Plaintext

Page 93: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Binarymessagevalidation

Binarymessagevalidationisnotveryeasytodoespeciallywhenitcomestocomparedatawithagivencontrolmessage.Asatesteryouwanttovalidatethebinarycontent.InCitrusthewaytocomparebinarymessagecontentistousebase64Stringencoding.Thebinarydataisencodedasbase64charactersequenceandthereforeiscomparablewithanexpectedcontent.

Thereceivedmessagecontentdoesnothavetobebase64encoded.Citrusisdoingthisconversionautomaticallybeforevalidationtakesplace.Thebinarydatacanbeanythinge.g.images,pdforgzipcontent.

Thedefaultmessagevalidatorforbinarymessagesisactivebydefault.Citruswillpickthismessagevalidatorforallmessagesoftype="binary_base64".ThedefaultmessagevalidatorimplementationcanbeoverwrittenbyplacingaSpringbeanwithiddefaultBinaryBase64MessageValidatortotheSpringapplicationcontext.

<beanid="defaultBinaryBase64MessageValidator"class="com.consol.citrus.validation.text.BinaryBase64MessageValidator"

InthetestcasereceivingactionwetellCitrustousebinarybase64messagevalidation.

<receiveendpoint="httpMessageEndpoint"><messagetype="binary_base64"><data>citrus:encodeBase64('HelloWorld!')</data></message></receive>

Withthemessageformattypetype="binary_base64"Citrusperformsthebase64charactersequencevalidation.Incomingmessagecontentisautomaticallyencodedasbase64Stringandcomparedtotheexpecteddata.Thiswaywecanmakesurethatthebinarycontentisasexpected.

BythewaysendingbinarymessagesinCitrusisalsoveryeasy.Justusethetype="binary"messagetypeinthesendoperation.Citrusnowconvertsthemessagepayloadtoabinarystreamaspayload.

<sendendpoint="httpMessageEndpoint"><messagetype="binary"><data>HelloWorld!</data></message>

CitrusReferenceGuide

93Binary

Page 94: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</send>

Base64encodingisalsosupportedinoutboundmessages.JustusetheencodeBase64functioninCitrus.Theresultisabase64encodedStringasmessagepayload.

<sendendpoint="httpMessageEndpoint"><message><data>citrus:encodeBase64('HelloWorld!')</data></message></send>

CitrusReferenceGuide

94Binary

Page 95: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Gzipmessagevalidation

Gzipisafamousmessagecompressionlibrary.Whendealingwithlargemessagecontentthecompressionmightbeagoodwaytooptimizethemessagetransportation.Citrusisabletohandlegzippedmessagepayloadsonsendandreceiveoperations.Whensendingcompresseddatawejusthavetousethemessagetypegzip.

<sendendpoint="messageEndpoint"><messagetype="gzip"><data>HelloWorld!</data></message></send>

Justusethetype="gzip"messagetypeinthesendoperation.Citrusnowconvertsthemessagepayloadtoagzipbinarystreamaspayload.

Whenvalidatinggzipbinarymessagecontentthemessagesarecomparedwithagivencontrolmessageinbinarybase64Stringrepresentation.Thegzipbinarydataisautomaticallyunzippedandencodedasbase64charactersequenceinordertocomparewithanexpectedcontent.

Thereceivedmessagecontentisusinggzipformatbuttheactualmessagecontentdoesnothavetobebase64encoded.Citrusisdoingthisconversionautomaticallybeforevalidationtakesplace.Thebinarydatacanbeanythinge.g.images,pdforplaintextcontent.

Thedefaultmessagevalidatorforgzipmessagesisactivebydefault.Citruswillpickthismessagevalidatorforallmessagesoftype="gzip_base64".ThedefaultmessagevalidatorimplementationcanbeoverwrittenbyplacingaSpringbeanwithiddefaultGzipBinaryBase64MessageValidatortotheSpringapplicationcontext.

<beanid="defaultGzipBinaryBase64MessageValidator"class="com.consol.citrus.validation.text.GzipBinaryBase64MessageValidator"

InthetestcasereceivingactionwetellCitrustousegzipmessagevalidation.

<receiveendpoint="messageEndpoint"><messagetype="gzip_base64"><data>citrus:encodeBase64('HelloWorld!')</data></message></receive>

CitrusReferenceGuide

95Gzip

Page 96: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Withthemessageformattypetype="gzip_base64"Citrusperformsthegzipbase64charactersequencevalidation.Incomingmessagecontentisautomaticallyunzippedandencodedasbase64Stringandcomparedtotheexpecteddata.Thiswaywecanmakesurethatthebinarycontentisasexpected.

NoteIfyouareusinghttpclientandservercomponentsthegzipcompressionsupportisbuiltinwiththeunderlyingSpringandhttpcommonslibraries.SoinhttpcommunicationyoujusthavetosettheheaderAccept-Encoding=gziporContent-Encoding=gzip.Themessagedataisthenautomaticallyzipped/unzippedbeforeCitrusgetsthemessagedataforvalidation.Readmoreaboutthishttpspecificgzipcompressioninchapterhttp.

CitrusReferenceGuide

96Gzip

Page 97: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

UsingXPathSometimeagointhisdocumentwehavealreadyseenhowXMLmessagepayloadsareconstructedwhensendingandreceivingmessages.NowusingXPathisaverypowerfulwayofaccessingelementsincomplexXMLstructures.TheXPathexpressionlanguageisveryhandywhenitcomestosaveelementvaluesastestvariablesorwhenvalidatingspecialelementsinaXMLmessagestructure.

XPathisaverypowerfultechnologyforwalkingXMLtrees.ThisW3CstandardstandsforadvancedXMLtreehandlingusingaspecialsyntaxasquerylanguage.CitrussupportstheXPathsyntaxinthefollowingfields:

<message><elementpath="[XPath-Expression]"></message><validate><xpathexpression="[XPath-Expression]"/></validate><extract><messagepath="[XPath-Expression]"></extract><ignorepath="[XPath-Expression]"/>

ThenextprogramlistingindicatesthepowerinusingXPathwithCitrus:

<message><validate><xpathexpression="//User/Name"value="John"/><xpathexpression="//User/Address[@type='office']/Street"value="Companystreet21"/><xpathexpression="//User/Name"value="$userName"/><xpathexpression="//User/@isAdmin"value="$isAdmin"/><xpathexpression="//User/@isAdmin"value="true"result-type="boolean"/><xpathexpression="//*[.='search-for']"value="searched-for"/><xpathexpression="count(//orderStatus[.='success'])"value="3"result-type="number"/></validate></message>

NowwedescribetheXPathusageinCitrusstepbystep.

ManipulatewithXPath

SomeelementsinXMLmessagepayloadsmightbeofdynamicnature.Justthinkofgeneratedidentifiersortimestamps.Alsowedonotwanttorepeatthesamestaticidentifierseveraltimesinourtestcases.Thisisthetimewheretestvariablesanddynamicmessageelementoverwritecomeinhandy.Theideaissimple.Wewantto

CitrusReferenceGuide

97Xpath

Page 98: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

overwriteaspecificmessageelementinourpayloadwithadynamicvalue.ThiscanbedonewithXPathorinlinevariabledeclarations.Letshavealookatanexamplelistingshowingbothways:

XMLDSL

<message><payload><TestMessage><MessageId>$messageId</MessageId><CreatedBy>_</CreatedBy><VersionId>$version</VersionId></TestMessage></payload><elementpath="/TestMessage/CreatedBy"value="$user"/></message>

Theprogramlistingaboveshowswaysofsettingvariablevaluesinsideamessagetemplate.Firstofallyoucansimplyplacevariableexpressionsinsidethemessage(seehow$messageIdisused).InadditiontothatyoucanalsouseXPathexpressionstoexplicitlyoverwritemessageelementsbeforevalidation.

<elementpath="/TestMessage/CreatedBy"value="$user"/>

TheXPathexpressionevaluatesandsearchesfortherightelementinthemessagepayload.Thepreviouslydefinedvariable$userreplacestheelementvalue.OfcoursethisworkswithXMLattributestoo.

BothwaysviaXPathorinlinevariableexpressionsareequaltoeachother.WithrespecttothecomplexityofXMLnamespacesandXPathyoumayfindtheinlinevariableexpressionmorecomfortabletouse.Anywayfeelfreetochoosethewaythatfitsbestforyou.Thisishowwecanadddynamicvariablevaluestothecontroltemplateinordertoincreasemaintainabilityandrobustnessofmessagevalidation.

TipValidationmatchersputvalidationmechanismstoanewlevelofferingdynamicassertionstatementsforvalidation.Havealookatthepossibilitieswithassertionstatementsinvalidation-matchers###ValidatewithXPath

WehavealreadyseenhowtovalidatewholeXMLstructureswithcontrolmessagetemplates.Allelementsarevalidatedandcomparedoneafteranother.Insomecasesthisapproachmightbetooextensive.Imaginethetesteronlyneedstovalidateasmall

CitrusReferenceGuide

98Xpath

Page 99: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

subsetofmessageelements.Thedefinitionofcontroltemplatesincombinationwithseveralignorestatementsisnotappropriateinthiscase.Youwouldratherwanttouseexplicitelementvalidation.

XMLDSL

<message><validate><xpathexpression="/TestRequest/MessageId"value="$messageId"/><xpathexpression="/TestRequest/VersionId"value="2"/></validate></message>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").validate("/TestRequest/MessageId","$messageId").validate("//VersionId","2").header("Operation","sayHello");

InsteadofcomparingthewholemessagesomemessageelementsarevalidatedexplicitlyviaXPath.CitrusevaluatestheXPathexpressiononthereceivedmessageandcomparestheresultvaluetothecontrolvalue.Thebasicmessagestructureaswellasallothermessageelementsarenotincludedintothisexplicitvalidation.

NoteIfthistypeofelementvalidationischosenneithernornortemplatedefinitionsareallowedinCitrusXMLtestcases.

TipCitrusoffersanalternativedot-notatedsyntaxinordertowalkthroughXMLtrees.IncaseyouarenotfamiliarwithXPathorsimplyneedaveryeasywaytofindyourelementinsidetheXMLtreeyoumightusethisway.EveryelementhierarchyintheXMLtreeisrepresentedwithasimpledot-forexample:

TestRequest.VersionId

TheexpressionwillsearchtheXMLtreefortherespectiveelement.Attributesaresupportedtoo.Incasethelastelementinthedot-notatedexpressionisaXMLattributetheframeworkwillautomaticallyfindit.

CitrusReferenceGuide

99Xpath

Page 100: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Ofcoursethisdot-notatedsyntaxisverysimpleandmightnotbeapplicableformorecomplextreenavigation.XPathismuchmorepowerful-nodoubt.Howeverthedot-notatedsyntaxmighthelpthoseofyouthatarenotfamiliarwithXPath.Sothedot-notationissupportedwhereverXPathexpressionsmightapply.

TheXpathexpressionscanevaluatetodifferentresulttypes.BydefaultCitrusisoperatingonNODEandSTRINGresulttypessothatyoucanvalidatesomeelementvalue.ButyoucanalsousedifferentresulttypessuchasNODESETandBOOLEAN.Seethisexamplehowthatworks:

XMLDSL

<message><validate><xpathexpression="/TestRequest/Error"value="false"result-type="boolean"/><xpathexpression="/TestRequest/Status[.='success']"value="3"result-type="number"/><xpathexpression="/TestRequest/OrderType"value="[single,multi,multi]"result-type="node-set"</validate></message>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").validate("boolean:/TestRequest/Error",false).validate("number:/TestRequest/Status[.='success']",3).validate("node-set:/TestRequest/OrderType","[single,multi,multi]").header("Operation","sayHello");

Intheexampleaboveweusedifferentexpressionresulttypes.Firstwewanttomakesurenor/TestRequest/Errorelementispresent.Thiscanbedonewithabooleanresulttypeandfalsevalue.Secondwewanttovalidatethenumberoffoundelementsfortheexpression/TestRequest/Status[.='success'].TheXPathexpressionevaluatestoanodelistthatresultsinitslistsizetobechecked.Andlastnotleastweevaluatetoanode-setresulttypewhereallvaluesinthenodelistwillbetranslatedtoacommadelimitedstringvalue.

Nowletshavealookatsomemorepowerfulvalidationexpressionsusingmatcherimplementations.UptonowwehaveseenthatXPathexpressionresultsarecomparablewithequalTooperations.Wewouldliketoaddsomemorepowerful

CitrusReferenceGuide

100Xpath

Page 101: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

validationsuchasgreaterThan,lessThan,hasSizeandmuchmore.ThereforewehaveintroducedHamcrestvalidationmatchersupportinCitrus.Hamcrestisaverypowefulmatcherlibrarythatprovidesafantasticsetofmatcherimplementations.Letsseehowwecanaddtheseinourtestcase:

XMLDSL

<message><validate><xpathexpression="/TestRequest/Error"value="@assertThat(anyOf(empty(),nullValue()))@"/><xpathexpression="/TestRequest/Status[.='success']"value="@assertThat(greaterThan(0))@"<xpathexpression="/TestRequest/OrderType"value="@assertThat(hasSize(3))@"result-type="node-set"</validate></message>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").validate("/TestRequest/Error",anyOf(empty(),nullValue())).validate("number:/TestRequest/Status[.='success']",greaterThan(0)).validate("node-set:/TestRequest/OrderType",hasSize(3)).header("Operation","sayHello");

WhenusingtheXMLDSLwehavetousetheassertThatvalidationmatchersyntaxfordefiningtheHamcrestmatchers.YoucancombinematcherimplementationasseenintheanyOf(empty(),nullValue())expression.WhenusingtheJavaDSLyoucanjustaddthematcherasexpectedresultobject.Citrusevaluatesthematchersandmakessureeverythingisasexpected.Thisisaverypowerfulvalidationmechanismasitalsoworkswithnode-setscontainingmultiplevaluesaslist.

ThisishowyoucanaddverypowerfulmessageelementvalidationinXMLusingXPathexpressions.

ExtractvariableswithXPath

Imagineyoureceiveamessageinyourtestwithsomegeneratedmessageidentifiervalues.Youhavenochancetopredicttheidentifiervaluebecauseitwasgeneratedatruntimebyaforeignapplication.Youcanignorethevalueinordertoprotectyourvalidation.Butinmanycasesyoumightneedtoreturnthisidentifierintherespective

CitrusReferenceGuide

101Xpath

Page 102: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

responsemessageorsomewhatlateroninthetest.Sowehavetosavethedynamicmessagecontentforreuseinlaterteststeps.Thesolutionissimpleandverypowerful.Wecanextractdynamicvaluesfromreceivedmessagesandsavethosetotestvariables.Addthiscodetoyourmessagereceivingaction.

XMLDSL

<extract><headername="Operation"variable="operation"/><messagepath="/TestRequest/VersionId"variable="versionId"/></extract>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("helloServiceServer").extractFromHeader("Operation","operation").extractFromPayload("//TestRequest/VersionId","versionId");

echo("Extractedoperationfromheaderis:$operation");echo("Extractedversionfrompayloadis:$versionId");

AsyoucanseeCitrusisabletoextractbothheaderandmessagepayloadcontentintotestvariables.Itdoesnotmatterifyouusenewtestvariablesorexistingvariablesastarget.Theextractionwillautomaticallycreateanewvariableincaseitdoesnotexist.Thetimethevariablewascreatedallfollowingtestactionscanaccessthetestvariablesasusual.Soyoucanreferencethevariablevaluesinresponsemessagesorotherteststepsahead.

TipWecanalsouseexpressionresulttypesinordertomanipulatethetestvariableoutcome.Incaseweuseabooleanresulttypetheexistenceofelementscanbesavedtovariablevalues.Theresulttypenode-settranslatesanodelistresulttoacommaseparatedstringofallvaluesinthisnodelist.Simplyusetheexpressionresulttypeattributesasshowninprevioussections.

XMLnamespacesinXPath

WhenitcomestoXMLnamespacesyouhavetobecarefulwithyourXPathexpressions.LetshavealookatanexamplemessagethatusesXMLnamespaces:

CitrusReferenceGuide

102Xpath

Page 103: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ns1:TestMessagexmlns:ns1="http://citrus.com/namespace"><ns1:TestHeader><ns1:CorrelationId>_</ns1:CorrelationId><ns1:Timestamp>2001-12-17T09:30:47.0Z</ns1:Timestamp><ns1:VersionId>2</ns1:VersionId></ns1:TestHeader><ns1:TestBody><ns1:Customer><ns1:Id>1</ns1:Id></ns1:Customer></ns1:TestBody></ns1:TestMessage>

NowwewouldliketovalidatesomeelementsinthismessageusingXPath

<message><validate><xpathexpression="//TestMessage/TestHeader/VersionId"value="2"/><xpathexpression="//TestMessage/TestHeader/CorrelationId"value="$correlationId"/></validate></message>

ThevalidationwillfailalthoughtheXPathexpressionlookscorrectregardingtheXMLtree.Becausethemessageusesthenamespacexmlns:ns1="http://citrus.com/namespace"withitsprefixns1ourXPathexpressionisnotabletofindtheelements.ThecorrectXPathexpressionusesthenamespaceprefixasdefinedinthemessage.

<message><validate><xpathexpression="//ns1:TestMessage/ns1:TestHeader/ns1:VersionId"value="2"/><xpathexpression="//ns1:TestMessage/ns1:TestHeader/ns1:CorrelationId"value="$correlationId"</message>

Nowtheexpressionsworkfineandthevalidationissuccessful.Butthisisquiteerrorprone.Thisisbecausethetestisnowdependingonthenamespaceprefixthatisusedbysomeapplication.Assoonasthemessageissentwithadifferentnamespaceprefix(e.g.ns2)thevalidationwillfailagain.

Youcanavoidthiseffectwhenspecifyingyourownnamespacecontextandyourownnamespaceprefixduringvalidation.

CitrusReferenceGuide

103Xpath

Page 104: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<message><validate><xpathexpression="//pfx:TestMessage/pfx:TestHeader/pfx:VersionId"value="2"/><xpathexpression="//pfx:TestMessage/pfx:TestHeader/pfx:CorrelationId"value="$correlationId"<namespaceprefix="pfx"value="http://citrus.com/namespace"/></validate></message>

Nowthetestinindependentfromanynamespaceprefixinthereceivedmessage.Thenamespacecontextwillresolvethenamespacesandfindtheelementsalthoughthemessagemightusedifferentprefixes.Theonlythingthatmattersisthatthenamespacevalue(http://citrus.com/namespace)matches.

TipInsteadofthisnamespacecontextonvalidationlevelyoucanalsohaveaglobalnamespacecontextwhichisvalidinalltestcases.WejustaddabeaninthebasicSpringapplicationcontextconfigurationwhichdefinesglobalnamespacemappings.

<namespace-context><namespaceprefix="def"uri="http://www.consol.de/samples/sayHello"/></namespace-context>

OncedefinedthedefnamespaceprefixisvalidinalltestcasesandallXPathexpressions.Thisenablesyoutofreeyourtestcasesfromnamespaceprefixbindingsthatmightbebrokenwithtime.YoucanusetheseglobalnamespacemappingswhereverXPathexpressionsarevalidinsideatestcase(validation,ignore,extract).

DefaultnamespacesinXPath

IntheprevioussectionwehaveseenthatXMLnamespacescangettrickywithXPathvalidation.Defaultnamespacescandoevenmore!Soletslookattheexamplewithdefaultnamespaces:

<TestMessagexmlns="http://citrus.com/namespace"><TestHeader><CorrelationId>_</CorrelationId><Timestamp>2001-12-17T09:30:47.0Z</Timestamp><VersionId>2</VersionId></TestHeader><TestBody><Customer><Id>1</Id></Customer>

CitrusReferenceGuide

104Xpath

Page 105: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</TestBody></TestMessage>

Themessageusesdefaultnamespaces.ThefollowingapproachinXPathwillfailduetonamespaceproblems.

<message><validate><xpathexpression="//TestMessage/TestHeader/VersionId"value="2"/><xpathexpression="//TestMessage/TestHeader/CorrelationId"value="$correlationId"/></validate></message>

EvendefaultnamespacesneedtobespecifiedintheXPathexpressions.Lookatthefollowingcodelistingthatworksfinewithdefaultnamespaces:

<message><validate><xpathexpression="//:TestMessage/:TestHeader/:VersionId"value="2"/><xpathexpression="//:TestMessage/:TestHeader/:CorrelationId"value="$correlationId"/></validate></message>

TipItisrecommendedtousethenamespacecontextasdescribedinthepreviouschapterwhenvalidating.Onlythisapproachensuresflexibilityandstabletestcasesregardingnamespacechanges.

CitrusReferenceGuide

105Xpath

Page 106: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

UsingJSONPathJSONPathistheJSONequivalenttoXPathintheXMLmessageworld.WithJSONPathexpressionsyoucanqueryandmanipulateentriesofaJSONmessagestructure.TheJSONPathexpressionsevaluateagainstaJSONmessagewheretheJSONobjectstructureisrepresentedinadotnotatedsyntax.

YouwillseethatJSONPathisaverypowerfultechnologywhenitcomestofindobjectentriesinacomplexJSONhierarchystructure.AlsoJSONPathcanhelptodomessagemanipulationsbeforeamessageissentoutforinstance.CitrussupportsJSONPathexpressionsinvariousscenarios:

<message><elementpath="[JSONPath-Expression]"></message><validate><json-pathexpression="[JSONPath-Expression]"/></validate><extract><messagepath="[JSONPath-Expression]"></extract><ignorepath="[JSONPath-Expression]"/>

ManipulatewithJSONPath

FirstthingwewanttodowithJSONPathistomanipulateamessagecontentbeforeitisactuallysentout.Thisisveryusefulwhenworkingwithmessagefileresourcesthatarereusedaccrossmultipletestcases.EachtestcasecanmanipulatethemessagecontentindividuallywithJSONPathbeforesending.Letshavealookatthissimplesample:

<messagetype="json"><resourcefile="file:path/to/user.json"/><elementpath="$.user.name"value="Admin"/><elementpath="$.user.admin"value="true"/><elementpath="$..status"value="closed"/></message>

Weuseabasicmessagecontentfilethatiscalleduser.json.ThecontentofthefileisfollowingJSONdatastructure:

user:"id":citrus:randomNumber(10)"name":"Unknown","admin":"?","projects":

CitrusReferenceGuide

106JsonPath

Page 107: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

["name":"Project1","status":"open","name":"Project2","status":"open","name":"Project3","status":"closed"]

Citrusloadsthefilecontentanduseditasmessagepayload.BeforethemessageissentouttheJSONPathexpressionshavethechancetomanipulatethemessagecontent.AllJSONPathexpressionsareevaluatedandthegivevaluesoverwriteexistingvaluesaccordingly.Theresultingmessagelookslikefollows:

user:"id":citrus:randomNumber(10)"name":"Admin","admin":"true","projects":["name":"Project1","status":"closed","name":"Project2","status":"closed","name":"Project3","status":"closed"]

TheJSONPathexpressionshavesettheusernametoAdmin.Theadminbooleanpropertywassettotrueandallprojectstatusvaluesweresettoclosed.Nowthemessageisreadytobesentout.IncaseaJSONPathexpressionshouldfailtofindamatchingelementwithinthemessagestructurethetestcasewillfail.

CitrusReferenceGuide

107JsonPath

Page 108: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WiththisJSONPathmechanismouareabletomanipulatemessagecontentbeforeitissentorreceivedwithinCitrus.Thismakeslifeveryeasywhenusingmessageresourcefilesthatarereusedacrossmultipletestcases.

ValidatewithJSONPath

LetscontinuetouseJSONPathexpressionswhenvalidatingareceivemessageinCitrus:

XMLDSL

<messagetype="json"><validate><json-pathexpression="$.user.name"value="Penny"/><json-pathexpression="$['user']['name']"value="$userName"/><json-pathexpression="$.user.aliases"value="["penny","jenny","nanny"]"/><json-pathexpression="$.user[?(@.admin)].password"value="@startsWith('$%00')@"/><json-pathexpression="$.user.address[?(@.type='office')]"value=""city":"Munich","street":"CompanyStreet","type":"office""/></validate></message>

JavaDSL

receive(someEndpoint).messageType(MessageType.JSON).validate("$.user.name","Penny").validate("$['user']['name']","$userName").validate("$.user.aliases","["penny","jenny","nanny"]").validate("$.user[?(@.admin)].password","@startsWith('$%00')@").validate("$.user.address[?(@.type='office')]",""city":"Munich","street":"CompanyStreet","type":"office"");

TheaboveJSONPathexpressionswillbeevaluatedwhenCitrusvalidatesthereceivedmessage.Theexpressionresultiscomparedtotheexpectedvaluewhereexpectationscanbestaticvaluesaswellastestvariablesandvalidationmatcherexpressions.IncaseaJSONPathexpressionshouldnotbeabletofindanyelementsthetestcasewillalsofail.

JSONisaprettysimpleyetpowerfulmessageformat.SimplifiedaJSONmessagejustknowsJSONObject,JSONArrayandJSONValueitems.ThehandlingofJSONObjectandJSONValueitemsinJSONPathexpressionsisstraightforward.Wejustuseadot

CitrusReferenceGuide

108JsonPath

Page 109: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

notatedsyntaxforwalkingthroughtheJSONObjecthierarchy.ThehandlingofJSONArrayitemsisalsonotverydifficulteither.CitruswilltrythebesttoconvertJSONArrayitemstoStringrepresentationvaluesforcomparison.

ImportantJSONPathexpressionswillonlyworkonJSONmessageformats.ThisiswhywehavetotellCitrusthecorrectmessageformat.BydefaultCitrusisworkingwithXMLmessagedataandthereforetheXMLvalidationmechanismsdoapplybydefault.WiththemessagetypeattributesettojsonwemakesurethatCitrusenablesJSONspecificfeaturesonthemessagevalidationsuchasJSONPathsupport.

NowletsgetabitmorecomplexwithvalidationmatchersandJSONobjectfunctions.CitrustriestogiveyouthemostcomfortablevalidationcapabilitieswhencomparingJSONobjectvaluesandJSONarrays.OnefirstthingyoucanuseisobjectfunctionslikekeySet()orsize().ThesefunctionalityisnotcoveredbyJSONPathoutofthebowbutaddedbyCitrus.Sethefollowingexampleonhowtouseit:

XMLDSL

<messagetype="json"><validate><json-pathexpression="$.user.keySet()"value="[id,name,admin,projects]"/><json-pathexpression="$.user.aliases.size()"value="3"/></validate></message>

JavaDSL

receive(someEndpoint).messageType(MessageType.JSON).validate("$.user.keySet()","[id,name,admin,projects]").validate("$.user.aliases.size()","3");

TheobjectfunctionsdoreturnspecialJSONobjectrelatedpropertiessuchasthesetofkeysforanobjectorthesizeofanJSONarray.

Nowletsgetevenmorecomfortablevalidationcapabilitieswithmatchers.CitrussupportsHamcrestmatcherswhichgivesusaverypowerfulwayofvalidatingJSONobjectelementsandarrays.Seethefollowingexamplesthatdemonstratehowthisworks:

XMLDSL

<messagetype="json">

CitrusReferenceGuide

109JsonPath

Page 110: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<validate><json-pathexpression="$.user.keySet()"value="@assertThat(contains(id,name,admin,projects))@"<json-pathexpression="$.user.aliases.size()"value="@assertThat(allOf(greaterThan(0),lessThan(5)))@"</validate></message>

JavaDSL

receive(someEndpoint).messageType(MessageType.JSON).validate("$.user.keySet()",contains("id","name","admin","projects")).validate("$.user.aliases.size()",allOf(greaterThan(0),lessThan(5)));

WhenusingtheXMLDSLwehavetousetheassertThatvalidationmatchersyntaxfordefiningtheHamcrestmatchers.YoucancombinematcherimplementationasseenintheallOf(greaterThan(0),lessThan(5))expression.WhenusingtheJavaDSLyoucanjustaddthematcherasexpectedresultobject.Citrusevaluatesthematchersandmakessureeverythingisasexpected.ThisisaverypowerfulvalidationmechanismasitcombinestheHamcrestmatchercapabilitieswithJSONmessagevalidation.

ExtractvariableswithJSONPath

Citrusisabletosavemessagecontenttotestvariablesattestruntime.Whenanincomingmessageispassingthemessagevalidationtheusercanextractsomevaluesofthatreceivedmessagetonewtestvariablesforlateruseinthetest.Thisisespeciallyhandsomewhenhavingtosendbacksomedynamicvalues.SoletssavesomevaluesusingJSONPath:

<messagetype="json"><data>user:"name":"Admin","password":"secret","admin":"true","aliases":["penny","chef","master"]</data><extract><messagepath="$.user.name"variable="userName"/><messagepath="$.user.aliases"variable="userAliases"/><messagepath="$.user[?(@.admin)].password"variable="adminPassword"/>

CitrusReferenceGuide

110JsonPath

Page 111: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</extract></message>

WiththisexamplewehaveextractedthreenewtestvariablesviaJSONPathexpressionevaluation.Thethreetestvariableswillbeavailabletoallupcomingtestactions.Thevariablevaluesare:

userName=AdminuserAliases=["penny","chef","master"]adminPassword=secret

AsyoucanseewecanalsoextractcomplexJSONObjectitemsorJSONArrayitems.ThetestvariablevalueisaStringrepresentationofthecomplexobject.

IgnorewithJSONPath

ThenextusagescenarioforJSONPathexpressionsinCitrusistheignoringofelementsduringmessagevalidation.AsyoualreadyknowCitrusprovidespowerfulvalidationmechanismsforXMLandJSONmessageformat.Theframeworkisabletocomparereceivedandexpectedmessagecontentswithpowerfulvalidatorimplementations.NowitthistimewewanttouseaJSONPathexpressionforignoringaveryspecificentryintheJSONobjectstructure.

<messagetype="json"><data>"users":["name":"Jane","token":"?","lastLogin":0,"name":"Penny","token":"?","lastLogin":0,"name":"Mary","token":"?","lastLogin":0]</data><ignoreexpression="$.users[*].token"/>

CitrusReferenceGuide

111JsonPath

Page 112: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ignoreexpression="$..lastLogin"/></message>

ThistimeweaddJSONPathexpressionsasignorestatements.Thismeansthatweexplicitlyleaveouttheevaluatedelementsfromvalidation.Obviouslythismechanismisagoodthingtodowhendynamicmessagedatasimplyisnotdeterministicsuchastimestampsanddynamicidentifiers.IntheexampleaboveweexplicitlyskipthetokenentryandalllastLoginvaluesthatareobviouslytimestampvaluesinmilliseconds.

TheJSONPathevaluationisverypowerfulwhenitcomestoselectasetofJSONobjectsandelements.ThisishowwecanignoreseveralelementswithonesingleJSONPathexpressionwhichisverypowerful.

CitrusReferenceGuide

112JsonPath

Page 113: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestactionsThischaptergivesabriefdescriptiontoalltestactionsthatatestercanincorporateintothetestcase.Besidessendingandreceivingmessagesthetestermayaccesstheseactionsinordertobuildamorecomplextestscenariothatfitsthedesiredusecase.

CitrusReferenceGuide

113Actions

Page 114: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Sendingmessages

Inaintegrationtestscenariowewanttotriggerprocessesandcallinterfaceservicesonthesystemundertest.Inordertodothisweneedtobeabletosendmessagestovariousmessagetransports.ThereforethesendmessagetestactioninCitrusisoneofthemostimportanttestactions.FirstofallletushavealookattheCitrusmessagedefinitioninCitrus:

Amessageconsistsofamessageheader(name-valuepairs)andamessagepayload.Laterinthissectionwewillseedifferentwaysofconstructingamessagewithpayloadandheadervalues.Butfirstofalllet'sconcentrateonasimplesendingmessageactioninsideatestcase.

XMLDSL

<testcasename="SendMessageTest"><description>Basicsendmessageexample</description>

<variables><variablename="text"value="HelloCitrus!"/><variablename="messageId"value="Mx1x123456789"/></variables>

<actions><sendendpoint="helloServiceEndpoint"><messagename="helloMessage">

CitrusReferenceGuide

114Send

Page 115: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<payload><TestMessage><Text>$text</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/><elementname="MessageId"value="$messageId"/></header></send></actions></testcase>

Themessagenameisoptionalanddefinesthemessageidentifierinthelocalmessagestore.Thismessagenameisveryusefulwhenaccessingthemessagecontentlateronduringthetestcase.Thelocalmessagestoreishandledpertestcaseandcontainsallexchangedmessages.Thesampleusesbothheaderandpayloadasmessagepartstosend.Inbothpartsyoucanusevariabledefinitions(see$textand$messageId).Sofirstofallletusrecapwhatvariablesdo.Testvariablesaredefinedattheverybeginningofthetestcaseandarevalidthroughoutallactionsthattakeplaceinthetest.Thismeansthatactionscansimplyreferenceavariablebytheexpression$variable-name.

TipUsevariableswhereveryoucan!Atleasttheimportantentitiesofatestshouldbedefinedasvariablesatthebeginning.Thetestcaseimprovesmaintainabilityandflexibilitywhenusingvariables.

Nowletshaveacloserlookatthesendingaction.The'endpoint'attributemightcatchyourattentionfirst.ThisattributereferencesamessageendpointinCitrusconfigurationbyname.Aspreviouslymentionedthemessageendpointdefinitionlivesinaseparateconfigurationfileandcontainstheactualmessagetransportsettings.Inthisexamplethe"helloServiceEndpoint"isreferencedwhichisamessageendpointforsendingoutmessagesviaJMSorHTTPforinstance.

Thetestcaseisnotawareofanytransportdetails,becauseitdoesnothaveto.Theadvantagesareobvious:Ontheonehandmultipletestcasescanreferencethemessageendpointdefinitionforbetterreuse.Secondlytestcasesareindependentofmessagetransportdetails.Soconnectionfactories,usercredentials,endpointurivaluesandsoonarenotpresentinthetestcase.

CitrusReferenceGuide

115Send

Page 116: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Inotherwordsthe"endpoint"attributeofthe<send>elementspecifieswhichmessageendpointdefinitiontouseandthereforewherethemessageshouldgoto.OnceagainallavailablemessageendpointsareconfiguredinaseparateCitrusconfigurationfile.Wewillcometothislateron.Besuretoalwayspicktherightmessageendpointtypeinordertopublishyourmessagetotherightdestination.

IfyoudonotliketheXMLlanguageyoucanalsousepureJavacodetodefinethesametest.InJavayouwouldalsomakeuseofthemessageendpointdefinitionandreferencethisinstance.ThesametestasshownaboveinJavaDSLlookslikethis:

JavaDSLdesigner

importorg.testng.ITestContext;importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;

@TestpublicclassSendMessageTestDesignerextendsTestNGCitrusTestDesigner

@CitrusTest(name="SendMessageTest")publicvoidsendMessageTest()description("Basicsendmessageexample");

variable("text","HelloCitrus!");variable("messageId","Mx1x123456789");

send("helloServiceEndpoint").name("helloMessage").payload("<TestMessage>"+"<Text>$text</Text>"+"</TestMessage>").header("Operation","sayHello").header("RequestTag","$messageId");

JavaDSLrunner

importorg.testng.ITestContext;importorg.testng.annotations.Test;importcom.consol.citrus.annotations.CitrusTest;importcom.consol.citrus.dsl.testng.TestNGCitrusTestRunner;

@TestpublicclassSendMessageTestRunnerextendsTestNGCitrusTestRunner

CitrusReferenceGuide

116Send

Page 117: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTest(name="SendMessageTest")publicvoidsendMessageTest()variable("text","HelloCitrus!");variable("messageId","Mx1x123456789");

send(action->action.endpoint("helloServiceEndpoint").name("helloMessage").payload("<TestMessage>"+"<Text>$text</Text>"+"</TestMessage>").header("Operation","sayHello").header("RequestTag","$messageId"));

InsteadofusingtheXMLtagsforsendweusemethodsfromTestNGCitrusTestDesignerclass.Thesamemessageendpointisreferencedwithinthesendmessageaction.

Nowthatthemessagesenderpatternisclearwecanconcentrateonhowtospecifythemessagecontenttobesent.ThereareseveralpossibilitiesforyoutodefinemessagecontentinCitrus:

message:Thiselementconstructsthemessagetobesent.Thereareseveralchildelementsavailable:payload:NestedXMLpayloadasdirectchildnode.data:InlineCDATAdefinitionofthemessagepayloadresource:ExternalfileresourceholdingthemessagepayloadThesyntaxwouldbe:<resourcefile="classpath:com/consol/citrus/messages/TestRequest.xml"/>Thefilepathprefixindicatestheresourcetype,sothefilelocationisresolvedeitherasfilesystemresource(file:)orclasspathresource(classpath:).element:ExplicitlyoverwritevaluesintheXMLmessagepayloadusingXPath.Youcanreplacemessagecontentwithdynamicvaluesbeforesending.Eachentryprovidesa"path"and"value"attribute.The"path"givesaXPathexpressionevaluatingtoaXMLnodeelementorattributeinthemessage.The"value"canbeavariableexpressionoranyotherstaticvalue.Citruswillreplacethevaluebeforesendingthemessage.header:Definesaheaderforthemessage(e.g.JMSheaderinformationorSOAPheader):element:Eachheaderreceivesa"name"and"value".The"name"willbethenameoftheheaderentryand"value"itsrespectivevalue.Againtheusageofvariableexpressionsasvalueissupportedhere,too.

CitrusReferenceGuide

117Send

Page 118: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLDSL

<sendendpoint="helloServiceEndpoint"><message><payload><!--messagepayloadasXML--></payload></message></send>

<sendendpoint="helloServiceEndpoint"><message><data><![CDATA[<!--messagepayloadasXML-->]]></data></message></send>

<sendendpoint="helloServiceEndpoint"><message><resourcefile="classpath:com/consol/citrus/messages/TestRequest.xml"/></message></send>

Themostimportantthingwhendealingwithsendingactionsistopreparethemessagepayloadandheader.YouareabletoconstructthemessagepayloadeitherbynestedXMLchildnodes(payload),asinlineCDATA()orexternalfile().

NoteSometimesthenestedXMLmessagepayloadelementsmaycauseXSDschemavalidationruleviolations.ThisisbecauseofvariablevaluesnotfittingtheXSDschemarulesforexample.InthisscenarioyoucouldalsousesimpleCDATAsectionsaspayloaddata.Inthiscaseyouneedtousethe<data>elementincontrasttothe<payload>elementthatwehaveusedinourexamplessofar.

WiththisalternativeyoucanskiptheXMLschemavalidationfromyourIDEatdesigntime.UnfortunatelyyouwillloosetheXSDautocompletionfeaturesmanyXMLeditorsofferwhenconstructingyourpayload.

TheThesamepossibilitiesapplytotheCitrusJavaDSL.

JavaDSLdesigner

CitrusReferenceGuide

118Send

Page 119: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidmessagingTest()send("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>");

@CitrusTestpublicvoidmessagingTest()send("helloServiceEndpoint").payload(newClassPathResource("com/consol/citrus/messages/TestRequest.xml"));

@CitrusTestpublicvoidmessagingTest()send("helloServiceEndpoint").payloadModel(newTestRequest("HelloCitrus!"));

@CitrusTestpublicvoidmessagingTest()send("helloServiceEndpoint").message(newDefaultMessage("HelloWorld!")));

BesidesdefiningmessagepayloadsasnormalStringsandviaexternalfileresource(classpathandfilesystem)youcanalsousemodelobjectsaspayloaddatainJavaDSL.ThismodelobjectpayloadrequiresapropermessagemarshallerthatshouldbeavailableasSpringbeaninsidetheapplicationcontext.BydefaultCitrusissearchingforabeanoftypeorg.springframework.oxm.Marshaller.

IncaseyouhavemultiplemessagemarshallersintheapplicationcontextyouhavetotellCitruswhichonetouseinthisparticularsendmessageaction.

@CitrusTestpublicvoidmessagingTest()send("helloServiceEndpoint").payloadModel(newTestRequest("HelloCitrus!"),"myMessageMarshallerBean");

CitrusReferenceGuide

119Send

Page 120: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowCitruswillmarshalthemessagepayloadwiththemessagemarshallerbeannamedmyMessageMarshallerBean.Thiswayyoucanhavemultiplemessagemarshallerimplementationsactiveinyourproject(XML,JSON,andsoon).

LastnotleastthemessagecanbedefinedasCitrusmessageobject.HereyoucanchooseoneofthedifferentmessageimplementationsusedinCitrusforSOAP,HttporJMSmessages.Oryoujustusethedefaultmessageimplementationormaybeacustomimplementation.

Beforesendingtakesplaceyoucanexplicitlyoverwritesomemessagevaluesinpayload.Youcanthinkofoverwritingspecificmessageelementswithvariablevalues.AlsoyoucanoverwritevaluesusingXPath(xpath)orJSONPath(json-path)expressions.

Themessageheaderispartofourdutyofdefiningpropermessages,too.SoCitrususesname-valuepairslike"Operation"and"MessageId"inthenextexampletosetmessageheaderentries.Dependingonwhatmessageendpointisusedandwhichmessagetransportunderneaththeheadervalueswillbeshippedindifferentways.InJMStheheadersgototheheadersectionofthemessage,inHttpwesetmimeheadersaccordingly,inSOAPwecanaccesstheSOAPheaderelementsandsoon.Citrusaimstodothehardworkforyou.SoCitrusknowshowtosetheadersondifferentmessagetransports.

XMLDSL

<sendendpoint="helloServiceEndpoint"><message><payload><TestMessage><Text>Hello!</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/></header></receive>

Themessageheaderstosendaredefinedbyasimplenameandvaluepair.Ofcourseyoucanusetestvariablesinheadervaluesaswell.Let'sseehowthislookslikeinJavaDSL:

JavaDSLdesigner

CitrusReferenceGuide

120Send

Page 121: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello");

JavaDSLrunner

@CitrusTestpublicvoidmessagingTest()receive(action->action.endpoint("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello"));

ThisisbasicallyhowtosendmessagesinCitrus.Thetestcaseisresponsibleforconstructingthemessagecontentwhilethepredefinedmessageendpointholdstransportspecificsettings.Testcasesreferenceendpointcomponentstopublishmessagestotheoutsideworld.Thevariablesupportinmessagepayloadandmessageheaderenablesyoutoadddynamicvaluesbeforesendingoutthemessage.

CitrusReferenceGuide

121Send

Page 122: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Receivingmessages

Justlikesendingmessagesthereceivingpartisaveryimportantactioninanintegrationtest.HonestlythereceiveactionisevenmoreimportantinCitrusaswealsowanttovalidatetheincomingmessagecontents.Wearewritingatestsowealsoneedassertionsandchecksthateverythingworksasexpected.

Asalreadymentionedbeforeamessageconsistsofamessageheader(name-valuepairs)andamessagepayload.Laterinthisdocumentwewillseehowtovalidateincomingmessageswithpayloadandheadervalues.Westartwithaverysimpleexample:

XMLDSL

<receiveendpoint="helloServiceEndpoint"><messagename="helloRequest"><payload><TestMessage><Text>$text</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/><elementname="MessageId"value="$messageId"/></header></receive>

Overallthereceivemessageactionlooksquitesimilartothesendmessageaction.Conceptsareidenticalaswedefinethemessagecontentwithpayloadandheadervalues.Themessagenameisoptionalanddefinesthemessageidentifierinthelocalmessagestore.Thismessagenameisveryusefulwhenaccessingthemessagecontentlateronduringthetestcase.Thelocalmessagestoreishandledpertestcaseandcontainsallexchangedmessages.

Wecanusetestvariablesinbothmessagepayloadanheaders.NowletushavealookattheJavaDSLrepresentationofthissimpleexample:

JavaDSLdesigner

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint")

CitrusReferenceGuide

122Receive

Page 123: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.name("helloRequest").payload("<TestMessage>"+"<Text>$text</Text>"+"</TestMessage>").header("Operation","sayHello").header("MessageId","$messageId");

JavaDSLrunner

@CitrusTestpublicvoidmessagingTest()receive(action->action.endpoint("helloServiceEndpoint").name("helloRequest").payload("<TestMessage>"+"<Text>$text</Text>"+"</TestMessage>").header("Operation","sayHello").header("MessageId","$messageId"));

Thereceiveactionwaitsforamessagetoarrive.Thewholetestexecutionisstoppedwhilewaitingforthemessage.Thisisimportanttoensurethestepbysteptestworkflowprocessing.Ofcourseyoucanspecifymessagetimeoutssothereceiverwillonlywaitagivenamountoftimebeforeraisingatimeouterror.Followingfromthattimeoutexceptionthetestcasefailsasthemessagedidnotarriveintime.Citrusdefinesdefaulttimeoutsettingsforallmessagereceivingtasks.

Inagoodcasescenariothemessagearrivesintimeandthecontentcanbevalidatedasanextstep.Thisvalidationcanbedoneinvariousways.OntheonehandyoucanspecifyawholeXMLmessagethatyouexpectascontroltemplate.Inthiscasethereceivedmessagestructureiscomparedtotheexpectedmessagecontentelementbyelement.Ontheotherhandyoucanuseexplicitelementvalidationwhereonlyasmallsubsetofmessageelementsisincludedintovalidation.

BesidesthemessagepayloadCitruswillalsoperformvalidationonthereceivedmessageheadervalues.Testvariableusageissupportedasusualduringthewholevalidationprocessforpayloadandheaderchecks.

Ingeneralthevalidationcomponent(validator)inCitrusworkshandinhandwithamessagereceivingcomponentasthefollowingfigureshows:

CitrusReferenceGuide

123Receive

Page 124: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Themessagereceivingcomponentpassesthemessagetothevalidatorwheretheindividualvalidationstepsareperformed.Letushaveacloserlookatthevalidationoptionsandfeaturesstepbystep.

Validatemessagepayloads

Themostdetailedvalidationofincomingmessagesistodefinesomeexpectedmessagepayload.TheCitrusmessagevalidatorwillthenperformadetailedmessagepayloadcomparison.Theincomingmessagehastomatchexactlytotheexpectedmessagepayload.ThedifferentmessagevalidatorimplementationsinCitrusprovidedeepcomparisonofmessagestructuressuchasXML,JSONandsoon.

Sobydefininganexpectedmessagepayloadwevalidatetheincomingmessageinsyntaxandsemantics.Incaseadifferenceisidentifiedbythemessagevalidatorthevalidationandthetestcasefailswithrespectiveexceptions.Thisishowyoucandefinemessagepayloadsinreceiveaction:

XMLDSL

<receiveendpoint="helloServiceEndpoint"><message><payload><!--messagepayloadasXML--></payload></message></receive>

<receiveendpoint="helloServiceEndpoint"><message><data><![CDATA[<!--messagepayloadasXML-->]]></data></message></receive>

<receiveendpoint="helloServiceEndpoint"><message>

CitrusReferenceGuide

124Receive

Page 125: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<resourcefile="classpath:com/consol/citrus/messages/TestRequest.xml"/></message></receive>

Thethreeexamplesaboverepresentthreedifferentwaysofdefiningthemessagepayloadinareceivemessageaction.OntheonehandwecanuseinlinemessagepayloadsasnestedXMLorCDATAsectionsinthetest.Ontheotherhandwecanloadthemessagecontentfromexternalfileresource.

NoteSometimesthenestedXMLmessagepayloadelementsmaycauseXSDschemavalidationruleviolations.ThisisbecauseofvariablevaluesnotfittingtheXSDschemarulesforexample.InthisscenarioyoucouldalsousesimpleCDATAsectionsaspayloaddata.Inthiscaseyouneedtousethe<data>elementincontrasttothe<payload>elementthatwehaveusedinourexamplessofar.

WiththisalternativeyoucanskiptheXMLschemavalidationfromyourIDEatdesigntime.UnfortunatelyyouwillloosetheXSDautocompletionfeaturesmanyXMLeditorsofferwhenconstructingyourpayload.

InJavaDSLwealsohavemultipleoptionsforspecifyingthemessagepayloads:

JavaDSLdesigner

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>");

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payload(newClassPathResource("com/consol/citrus/messages/TestRequest.xml"));

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payloadModel(newTestRequest("HelloCitrus!"));

CitrusReferenceGuide

125Receive

Page 126: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").message(newDefaultMessage("HelloWorld!")));

TheexamplesaboverepresentthebasicvariationsofhowtodefinemessagepayloadsinCitrusJavaDSL.ThepayloadcanbeasimpleStringoraSpringfileresource(classpathorfilesystem).Inadditiontothatwecanuseamodelobject.WhenusingmodelobjectsaspayloadsweneedapropermessagemarshallerimplementationintheSpringapplicationcontext.Bydefaultthisisamarshallerbeanoftypeorg.springframework.oxm.MarshallerthathastobepresentintheSpringapplicationcontext.YoucanaddsuchabeanforXMLandJSONmessagemarshallingforinstance.

IncaseyouhavemultiplemessagemarshallersintheapplicationcontextyouhavetotellCitruswhichonetouseinthisparticularsendmessageaction.

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payloadModel(newTestRequest("HelloCitrus!"),"myMessageMarshallerBean");

NowCitruswillmarshalthemessagepayloadwiththemessagemarshallerbeannamedmyMessageMarshallerBean.Thiswayyoucanhavemultiplemessagemarshallerimplementationsactiveinyourproject(XML,JSON,andsoon).

LastnotleastthemessagecanbedefinedasCitrusmessageobject.HereyoucanchooseoneofthedifferentmessageimplementationsusedinCitrusforSOAP,HttporJMSmessages.Oryoujustusethedefaultmessageimplementationormaybeacustomimplementation.

IngeneraltheexpectedmessagecontentcanbemanipulatedusingXPath(xpath)orJSONPath(json-path).Inadditiontothatyoucanignoresomeelementsthatareskippedincomparison.Wewilldescribethislateroninthissection.Nowletscontinuewithmessageheadervalidation.

Validatemessageheaders

CitrusReferenceGuide

126Receive

Page 127: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Messageheadersareusedwidelyinenterprisemessagingsolution:Themessageheadersarepartofthemessagesemanticsandneedtobevalidated,too.Citruscanvalidatemessageheaderbynameandvalue.

XMLDSL

<receiveendpoint="helloServiceEndpoint"><message><payload><TestMessage><Text>Hello!</Text></TestMessage></payload></message><header><elementname="Operation"value="sayHello"/></header></receive>

Theexpectedmessageheadersaredefinedbyanameandvaluepair.Citruswillcheckthattheexpectedmessageheaderispresentandwillcheckthevalue.IncasethemessageheaderisnotfoundorthevaluedoesnotmatchCitruswillraiseanexceptionandthetestfails.Youcanusevalidationmatchers(validation-matchers)foramorepowerfulvalidationofheadervalues,too.

Let'sseehowthislookslikeinJavaDSL:

JavaDSLdesigner

@CitrusTestpublicvoidmessagingTest()receive("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>").header("Operation","sayHello");

JavaDSLrunner

@CitrusTestpublicvoidmessagingTest()receive(action->action.endpoint("helloServiceEndpoint").payload("<TestMessage>"+"<Text>Hello!</Text>"+"</TestMessage>")

CitrusReferenceGuide

127Receive

Page 128: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.header("Operation","sayHello"));

HeaderdefinitioninJavaDSLisstraightforwardaswejustdefinenameandvalueasusual.ThiscompletesthemessagevalidationwhenreceivingamessageinCitrus.ThemessagevalidatorimplementationsmayaddadditionalvalidationcapabilitiessuchasXMLschemavalidationorXPathandJSONPathvalidation.Pleaserefertotherespectivechaptersinthisguidetolearnmoreaboutthat.

Messageselectors

The<selector>elementinsidethereceivingactiondefineskey-valuepairsinordertofilterthemessagesbeingreceived.Thefilterappliestothemessageheaders.Thismeansthatareceiverwillonlyacceptmessagesmatchingaheaderelementvalue.Inmessagingapplicationstheheaderinformationoftenholdsmessageids,correlationids,operationnamesandsoon.Withthisinformationgivenyoucanexplicitlylistenformessagesthatbelongtoyourtestcase.Thisisveryhelpfultoavoidreceivingmessagesthatarestillavailableonthemessagedestination.

Letssaythetestedsoftwareapplicationkeepssendingmessagesthatbelongtoprevioustestcases.Thiscouldhappeninretrysituationswheretheapplicationerrorhandlingautomaticallytriestosolveacommunicationproblemthatoccurredduringprevioustestcases.Asaresultamessagedestination(e.g.aJMSmessagequeue)containsmessagesthatarenotvalidanymoreforthecurrentlyrunningtestcase.Thetestcasemightfailbecausethereceivedmessagedoesnotapplytotheactualusecase.Sowewilldefinitelyrunintovalidationerrorsastheexpectedmessagecontrolvaluesdonotmatch.

Nowwehavetofindawaytoavoidtheseproblems.Thetestcouldfilterthemessagesonadestinationtoonlyreceivemessagesthatapplyfortheusecasethatisbeingtested.TheJavaMessagingSystem(JMS)cameupwithamessageheaderselectorthatwillonlyacceptmessagesthatfittheexpectedheadervalues.

Letushaveacloserlookatamessageselectorinsideareceivingaction:

XMLDSL

<selector><element>name="correlationId"value="Cx1x123456789"</element><element>name="operation"value="getOrders"</element></selector>

CitrusReferenceGuide

128Receive

Page 129: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLdesigner

@CitrusTestpublicvoidreceiveMessageTest()receive("testServiceEndpoint").selector("correlationId='Cx1x123456789'ANDoperation='getOrders'");

JavaDSLrunner

@CitrusTestpublicvoidreceiveMessageTest()receive(action->action.endpoint("testServiceEndpoint").selector("correlationId='Cx1x123456789'ANDoperation='getOrders'"));

Thisexampleshowshowmessageselectorswork.Theselectorwillonlyacceptmessagesthatmeetthecorrelationidandtheoperationintheheadervalues.Allothermessagesonthemessagedestinationareignored.TheselectorelementsareautomaticallyassociatedtoeachotherusingthelogicalANDoperator.Thismeansthatthemessageselectorstringwouldlooklikethis:correlationId='Cx1x123456789'ANDoperation='getOrders'.

Insteadofusingseveralelementsintheselectoryoucanalsodefineaselectorstringdirectlywhichgivesyoumorepowerinconstructingtheselectionlogicyourself.ThiswayyoucanuseANDlogicaloperatorsyourself.

<selector><value>correlationId='Cx1x123456789'ANDoperation='getOrders'</value></selector>

ImportantIncaseyouwanttoruntestsinparallelmessageselectorsbecomeessentialinyourtestcases.Thedifferenttestsrunningatthesametimewillstealmessagesfromeachotherwhenyoulackofmessageselectionmechanisms.

ImportantPreviouslyonlyJMSmessagedestinationsofferedsupportformessageselectors!WithCitrusversion1.2weintroducedmessageselectorsupportforSpringIntegrationmessagechannels,too(seemessage-channel-selector-support).

GroovyMarkupBuilder

CitrusReferenceGuide

129Receive

Page 130: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WiththeGroovyMarkupBuilderyoucanbuildXMLmessagepayloadsinasimpleway,withouthavingtowritethetypicalXMLoverhead.ForexampleweuseaGroovyscripttoconstructtheXMLmessagetobesentout.InsteadofaplainCDATAXMLsectionorthenestedpayloadXMLdatawewriteaGroovyscriptsnippet.TheGroovyMarkupBuildergeneratestheXMLmessagepayloadwithexactlythesameresult:

XMLDSL

<sendendpoint="helloServiceEndpoint"><message><buildertype="groovy">markupBuilder.TestMessageMessageId('$messageId')Timestamp('?')VersionId('2')Text('HelloCitrus!')</builder><elementpath="/TestMessage/Timestamp"value="$createDate"/></message><header><elementname="Operation"value="sayHello"/><elementname="MessageId"value="$messageId"/></header></send>

WeusethebuilderelementwithtypegroovyandtheMarkupBuildercodeisdirectlywrittentothiselement.Asyoucanseefromtheexampleabove,youcanmixXPathandGroovymarkupbuildercode.TheMarkupBuildersyntaxisveryeasyandfollowsthesimplerule:markupBuilder.ROOT-ELEMENTCHILD-ELEMENTS.HoweverthetesterhastofollowsomesimplerulesandnamingconventionswhenusingtheCitrusMarkupBuilderextension:

TheMarkupBuilderisaccessedwithinthescriptoveranobjectnamedmarkupBuilder.Thenameofthecustomrootelementfollowswithallitschildelements.Childelementsmaybedefinedwithincurlybracketsaftertheroot-element(thesameappliesforfurthernestedchildelements)Attributesandelementvaluesaredefinedwithinroundbrackets,aftertheelementnameAttributeandelementvalueshavetostandwithinapostrophes(e.g.attribute-name:'attribute-value')

CitrusReferenceGuide

130Receive

Page 131: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheGroovyMarkupBuilderscriptmayalsobeusedwithinreceiveactionsasshowninthefollowinglisting:

XMLDSL

<sendendpoint="helloServiceEndpoint"><message><buildertype="groovy"file="classpath:com/consol/citrus/groovy/helloRequest.groovy"/></message></send>

<receiveendpoint="helloServiceEndpoint"timeout="5000"><message><buildertype="groovy">markupBuilder.TestResponse(xmlns:'http://www.consol.de/schemas/samples/sayHello.xsd')MessageId('$messageId')CorrelationId('$correlationId')User('HelloService')Text('Hello$user')</builder></message></receive>

Asyoucanseeitisalsopossibletodefinethescriptasexternalfileresource.Inadditiontothatnamespacesupportisgivenasnormalattributedefinitionwithintheroundbracketsaftertheelementname.

TheMarkupBuilderimplementationinGroovyoffersgreatpossibilitiesindefiningmessagepayloads.WedonotneedtowriteXMLtagoverheadandwecanconstructcomplexmessagepayloadswithGroovylogiclikeiterationsandconditionalelements.FordetailedMarkupBuilderdescriptionspleaseseetheofficialGroovydocumentation.

CitrusReferenceGuide

131Receive

Page 132: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Databaseactions

Inmanycasesitisnecessarytoaccessthedatabaseduringatest.Thisenablesatestertoalsovalidatethepersistentdatainadatabase.Itmightalsobehelpfultopreparethedatabasewithsometestdatabeforerunningatest.Youcandothisusingthetwodatabaseactionsthataredescribedinthefollowingsections.

IngeneralCitrushandlesSELECTstatementsdifferentlytootherstatementslikeINSERT,UPDATEandDELETE.WhenexecutingaSQLquerywithSELECTyouareabletoaddvalidationstepsontheresultsetsreturnedfromthedatabase.ThisisnotallowedwhenexecutingupdatestatementslikeINSERT,UPDATE,DELETE.

ImportantDonotmixstatementsoftypeSELECTwithothersinasinglesqltestaction.ThiswillleadtoerrorsbecausevalidationstepsarenotvalidforstatementsotherthanSELECT.Pleaseuseseparatetestactionsforupdatestatements.

SQLupdate,insert,delete

TheactionsimplyexecutesagroupofSQLstatementsinordertochangedatainadatabase.Typicallytheactionisusedtopreparethedatabaseatthebeginningofatestortocleanupthedatabaseattheendofatest.YoucanspecifySQLstatementslikeINSERT,UPDATE,DELETE,CREATETABLE,ALTERTABLEandmanymore.

OntheonehandyoucanspecifythestatementsasinlineSQLorstoredinanexternalSQLresourcefileasshowninthenexttwoexamples.

XMLDSL

<actions><sqldatasource="someDataSource"><statement>DELETEFROMCUSTOMERS</statement><statement>DELETEFROMORDERS</statement></sql>

<sqldatasource="myDataSource"><resourcefile="file:tests/unit/resources/script.sql"/></sql></actions>

JavaDSLdesigner

@Autowired

CitrusReferenceGuide

132Database

Page 133: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@Qualifier("myDataSource")privateDataSourcedataSource;

@CitrusTestpublicvoidsqlTest()sql(dataSource).statement("DELETEFROMCUSTOMERS").statement("DELETEFROMORDERS");

sql(dataSource).sqlResource("file:tests/unit/resources/script.sql");

JavaDSLrunner

@Autowired@Qualifier("myDataSource")privateDataSourcedataSource;

@CitrusTestpublicvoidsqlTest()sql(action->action.dataSource(dataSource).statement("DELETEFROMCUSTOMERS").statement("DELETEFROMORDERS"));

sql(action->action.dataSource(dataSource).sqlResource("file:tests/unit/resources/script.sql"));

ThefirstactionusesinlineSQLstatementsdefineddirectlyinsidethetestcase.ThenextactionusesanexternalSQLresourcefileinstead.ThefileresourcecanholdseveralSQLstatementsseparatedbynewlines.Allstatementsinsidethefileareexecutedsequentiallybytheframework.

ImportantYouhavetopayattentiontosomeruleswhendealingwithexternalSQLresources.

EachstatementshouldbegininanewlineItisnotallowedtodefinestatementswithwordwrappingCommentsbeginwithtwodashes"--"

NoteTheexternalfileisreferencedeitherasfilesystemresourceorclasspathresource,byusingthe"file:"or"classpath:"prefix.

CitrusReferenceGuide

133Database

Page 134: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Bothexamplesusethe"datasource"attribute.Thisvaluedefinesthedatabasedatasourcetobeused.Theconnectiontoadatasourceismandatory,becausethetestcasedoesnotknowaboutusercredentialsordatabasenames.The'datasource'attributereferencespredefineddatasourcesthatarelocatedinaseparateSpringconfigurationfile.

SQLquery

ThequeryactionisspeciallydesignedtoexecuteSQLqueries(SELECT*FROM).Sothetestisabletoreaddatafromadatabase.Thequeryresultsarevalidatedagainstexpecteddataasshowninthenextexample.

XMLDSL

<sqldatasource="testDataSource"><statement>selectNAMEfromCUSTOMERSwhereID='$customerId'</statement><statement>selectcount(*)fromERRORS</statement><statement>selectIDfromORDERSwhereDESCLIKE'Def%'</statement><statement>selectDESCRIPTIONfromORDERSwhereID='$id'</statement>

<validatecolumn="ID"value="1"/><validatecolumn="NAME"value="Christoph"/><validatecolumn="COUNT(*)"value="$rowsCount"/><validatecolumn="DESCRIPTION"value="null"/></sql>

JavaDSLdesigner

@Autowired@Qualifier("testDataSource")privateDataSourcedataSource;

@CitrusTestpublicvoiddatabaseQueryTest()query(dataSource).statement("selectNAMEfromCUSTOMERSwhereCUSTOMER_ID='$customerId'").statement("selectCOUNT(1)asoverall_cntfromERRORS").statement("selectORDER_IDfromORDERSwhereDESCRIPTIONLIKE'Migrate%'").statement("selectDESCRIPTIONfromORDERSwhereORDER_ID=2").validate("ORDER_ID","1").validate("NAME","Christoph").validate("OVERALL_CNT","$rowsCount").validate("DESCRIPTION","NULL");

CitrusReferenceGuide

134Database

Page 135: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLrunner

@Autowired@Qualifier("testDataSource")privateDataSourcedataSource;

@CitrusTestpublicvoiddatabaseQueryTest()query(action->action.dataSource(dataSource).statement("selectNAMEfromCUSTOMERSwhereCUSTOMER_ID='$customerId'").statement("selectCOUNT(1)asoverall_cntfromERRORS").statement("selectORDER_IDfromORDERSwhereDESCRIPTIONLIKE'Migrate%'").statement("selectDESCRIPTIONfromORDERSwhereORDER_ID=2").validate("ORDER_ID","1").validate("NAME","Christoph").validate("OVERALL_CNT","$rowsCount").validate("DESCRIPTION","NULL"));

Theactionoffersawiderangeofvalidatingfunctionalityfordatabaseresultsets.FirstofallyouhavetoselectthedataviaSQLstatements.HereagainyouhavethechoicetouseinlineSQLstatementsorexternalfileresourcepattern.

Theresultsetsarevalidatedthroughelements.Itispossibletodoadetailedcheckoneveryselectedcolumnoftheresultset.Simplyrefertotheselectedcolumnnameinordertovalidateitsvalue.Theusageoftestvariablesissupportedaswellasdatabaseexpressionslikecount(),avg(),min(),max().

Yousimplydefinetheentrywiththecolumnnameasthe"column"attributeandanyexpectedvalueexpressionasexpected"value".Theframeworkthenwillcheckthecolumntofittheexpectedvalueandraisevalidationerrorsincaseofmismatch.

LookingatthefirstSELECTstatementintheexampleyouwillseethattestvariablesaresupportedintheSQLstatements.Theframeworkwillreplacethevariablewithitsrespectivevaluebeforesendingittothedatabase.

Inthevalidationsectionvariablescanbeusedtoo.Lookatthethirdvalidationentry,wherethevariable"$rowsCount"isused.Thelastvalidationinthisexampleshows,thatNULLvaluesarealsosupportedasexpectedvalues.

Ifasinglevalidationhappenstofail,thewholeactionwillfailwithrespectivevalidationerrors.

CitrusReferenceGuide

135Database

Page 136: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ImportantThevalidationwith""meetssinglerowresultsetsasyouspecifyasinglecolumncontrolvalue.Incaseyouhavemultiplerowsinaresultsetyouratherneedtovalidatethecolumnswithmultiplecontrolvalueslikethis:

<validatecolumn="someColumnName"><values><value>Valuein1strow</value><value>Valuein2ndrow</value><value>Valuein3rdrow</value><value>Valueinxrow</value></values></validate>

WithinJavayoucanpassavariableargumentlisttothevalidatemethodlikethis:

query(dataSource).statement("selectNAMEfromWEEKDAYSwhereNAMELIKE'S%'").validate("NAME","Saturday","Sunday")

Nextexampleshowshowtoworkwithmultiplerowresultsetsandmultiplevaluestoexpectwithinonecolumn:

<sqldatasource="testDataSource"><statement>selectWEEKDAYasDAY,DESCRIPTIONfromWEEK</statement><validatecolumn="DAY"><values><value>Monday</value><value>Tuesday</value><value>Wednesday</value><value>Thursday</value><value>Friday</value><value>@ignore@</value><value>@ignore@</value></values></validate><validatecolumn="DESCRIPTION"><values><value>IhateMondays!</value><value>Tuesdayissportsday</value><value>Themidoftheweek</value><value>Thursdayweplaychess</value><value>Friday,theweekendisnear!</value><value>@ignore@</value><value>@ignore@</value></values>

CitrusReferenceGuide

136Database

Page 137: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</validate></sql>

Forthevalidationofmultiplerowsthe<validate>elementisabletohostalistofcontrolvaluesforacolumn.Asyoucanseefromtheexampleabove,youhavetoaddacontrolvalueforeachrowintheresultset.Thisalsomeansthatwehavetotakecareofthetotalnumberofrows.Fortunatelywecanusetheignoreplaceholder,inordertoskipthevalidationofaspecificrowintheresultset.Functionsandvariablesaresupportedasusual.

ImportantItisimportant,thatthecontrolvaluesaredefinedinthecorrectorder,becausetheyarecomparedoneononewiththeactualresultsetcomingfromdatabasequery.Youmayneedtoadd"orderby"SQLexpressionstogettherightorderofrowsreturned.Ifanyofthevaluesfailsinvalidationorthetotalnumberofrowsisnotequal,thewholeactionwillfailwithrespectivevalidationerrors.

GroovySQLresultsetvalidation

GroovyprovidesgreatsupportforaccessingJavalistobjectsandmaps.AsaJavaSQLresultsetisnothingbutalistofmaprepresentations,whereeachentryinthelistdefinesarowintheresultsetandeachmapentryrepresentsthecolumnsandvalues.SowithGroovy'slistandmapaccesswehavegreatpossibilitiestovalidateaSQLresultset-outofthebox.

XMLDSL

<sqldatasource="testDataSource"><statement>selectIDfromCUSTOMERSwhereNAME='$customerName'</statement><statement>selectORDERTYPE,STATUSfromORDERSwhereID='$orderId'</statement>

<validate-scripttype="groovy">assertrows.size()==2assertrows[0].ID=='1'assertrows[1].STATUS=='inprogress'assertrows[1]==[ORDERTYPE:'SampleOrder',STATUS:'inprogress']</validate-script></sql>

JavaDSLdesigner

query(dataSource).statement("selectORDERTYPE,STATUSfromORDERSwhereID='$orderId'").validateScript("assertrows.size==2;"+

CitrusReferenceGuide

137Database

Page 138: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

"assertrows[0].ID=='1';"+"assertrows[0].STATUS=='inprogress';","groovy");

JavaDSLrunner

query(action->action.dataSource(dataSource).statement("selectORDERTYPE,STATUSfromORDERSwhereID='$orderId'").validateScript("assertrows.size==2;"+"assertrows[0].ID=='1';"+"assertrows[0].STATUS=='inprogress';","groovy"));

AsyoucanseeGroovyprovidesfantasticaccessmethodstotheSQLresultset.Wecanbrowsetheresultsetwithnamedcolumnvaluesandcheckthesizeoftheresultset.Wearealsoabletosearchforanentry,iterateovertheresultsetandhaveotherhelpfuloperations.ForadetaileddescriptionofthelistandmaphandlinginGroovymyadviceforyouistohavealookattheofficialGroovydocumentation.

NoteIngeneralotherscriptlanguagesdoalsosupportthiskindoflistandmapaccess.FornowwejusthaveimplementedtheGroovyscriptsupport,buttheframeworkisreadytoworkwithallothergreatscriptlanguagesoutthere,too(e.g.Scala,Clojure,Fantom,etc.).Soifyouprefertoworkwithanotherlanguagejoinandhelpusimplementthosefeatures.

Saveresultsetvalues

Nowthevalidationofdatabaseentriesisaverypowerfulfeaturebutsometimeswesimplydonotknowthepersistedcontentvalues.Thetestmaywanttoreaddatabaseentriesintotestvariableswithoutvalidation.Citrusisabletodothatwiththefollowingexpressions:

XMLDSL

<sqldatasource="testDataSource"><statement>selectIDfromCUSTOMERSwhereNAME='$customerName'</statement><statement>selectSTATUSfromORDERSwhereID='$orderId'</statement>

<extractcolumn="ID"variable="$customerId"/><extractcolumn="STATUS"variable="$orderStatus"/></sql>

JavaDSLdesigner

CitrusReferenceGuide

138Database

Page 139: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

query(dataSource).statement("selectSTATUSfromORDERSwhereID='$orderId'").extract("STATUS","orderStatus");

JavaDSLrunner

query(action->action.dataSource(dataSource).statement("selectSTATUSfromORDERSwhereID='$orderId'").extract("STATUS","orderStatus"));

Wecansavethedatabasecolumnvaluesdirectlytotestvariables.Ofcourseyoucancombinethevalueextractionwiththenormalcolumnvalidationdescribedearlierinthischapter.Pleasekeepinmindthatwecannotusetheseoperationsonresultsetswithmultiplerows.Citruswillalwaysusethefirstrowinaresultset.

CitrusReferenceGuide

139Database

Page 140: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Sleep

Thisactionshowshowtomakethetestframeworksleepforagivenamountoftime.Theattribute'time'definestheamountoftimetowaitinseconds.Asshowninthenextexampledecimalvaluesaresupportedtoo.Whennowaitingtimeisspecifiedthedefaulttimeof50000millisecondsapplies.

XMLDSL

<testcasename="sleepTest"><actions><sleepseconds="3.5"/>

<sleepmilliseconds="500"/>

<sleep/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidsleepTest()sleep(500);//sleep500milliseconds

sleep();//sleepdefaulttime

Whenshouldsomebodyusethisaction?Tousthisactionwasalwaysveryusefulincasethetestneededtowaituntilanapplicationhaddonesomework.Forexampleinsomecasestheapplicationtooksometimetowritesomedataintothedatabase.Wewaitedthenasmallamountoftimeinordertoavoidunnecessarytestfailures,becausethetestframeworksimplyvalidatedthedatabasetooearly.Orasanotherexamplethetestmaywaitagiventimeuntilretrymechanismsaretriggeredinthetestedapplicationandthenproceedwiththetestactions.

CitrusReferenceGuide

140Sleep

Page 141: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Java

ThetestframeworkiswritteninJavaandrunsinsideaJavavirtualmachine.ThefunctionalityofcallingotherJavaobjectsandmethodsinthissameJavaVMthroughJavaReflectionisself-evident.WiththisactionyoucancallanyJavaAPIavailableatruntimethroughthespecifiedJavaclasspath.

Theactionsyntaxlookslikefollows:

<javaclass="com.consol.citrus.test.util.InvocationDummy"><constructor><argumenttype="">TestInvocation</argument></constructor><methodname="invoke"><argumenttype="String[]">1,2</argument></method></java>

<javaclass="com.consol.citrus.test.util.InvocationDummy"><constructor><argumenttype="">TestInvocation</argument></constructor><methodname="invoke"><argumenttype="int">4</argument><argumenttype="String">TestInvocation</argument><argumenttype="boolean">true</argument></method></java>

<javaclass="com.consol.citrus.test.util.InvocationDummy"><methodname="main"><argumenttype="String[]">4,Test,true</argument></method></java>

TheJavaclassisspecifiedbyfullyqualifiedclassname.Constructorargumentsareaddedusingtheelementwithalistofchildelements.Thetypeoftheargumentisdefinedwithintherespectiveattribute"type".BydefaultthetypewouldbeString.

TheinvokedmethodontheJavaobjectissimplyreferencedbyitsname.Methodargumentsdonotbringanythingnewafterknowingtheconstructorargumentdefinition,dothey?.

CitrusReferenceGuide

141Java

Page 142: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Methodargumentssupportdatatypeconversiontoo,evenstringarrays(usefulwhencallingCLIs).Inthethirdactionintheexamplecodeyoucanseethatcolonseparatedstringsareautomaticallyconvertedtostringarrays.

Simpledatatypesaredefinedbytheirname(int,boolean,floatetc.).Besurethattheinvokedmethodandclassconstructorfityourargumentsandviceversa,otherwiseyouwillcauseerrorsatruntime.

BesidesinstantiatingafullynewobjectinstanceforaclasshowaboutreusingabeaninstanceavailableinSpringbeancontainer.SimplyusetherefattributeandrefertoanexistingbeaninSpringapplicationcontext.

<javaref="invocationDummy"><methodname="invoke"><argumenttype="int">4</argument><argumenttype="String">TestInvocation</argument><argumenttype="boolean">true</argument></method></java>

<beanid="invocationDummy"class="com.consol.citrus.test.util.InvocationDummy"/>

ThemethodisinvokedontheSpringbeaninstance.Thisisveryusefulasyoucaninjectotherobjects(e.g.viaAutowiring)totheSpringbeaninstancebeforemethodinvocationintesttakesplace.ThisenablesyoutoexecuteanyJavalogicinsideatestcase.

CitrusReferenceGuide

142Java

Page 143: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Receivetimeout

Insomecasesitmightbenecessarytovalidatethatamessageisnotpresentonadestination.Thismeansthatthisactionexpectsatimeoutwhenreceivingamessagefromanendpointdestination.Forinstancethetesterintendstoensurethatnomessageissenttoacertaindestinationinatimeperiod.Inthatcasethetimeoutwouldnotbeatestabortingerrorbuttheexpectedbehavior.Andincontrasttothenormalbehaviorwhenamessageisreceivedinthetimeperiodthetestwillfailwitherror.

Inordertovalidatesuchatimeoutsituationtheactionshallhelp.Theusageisverysimpleasthefollowingexampleshows:

XMLDSL

<testcasename="receiveJMSTimeoutTest"><actions><expect-timeoutendpoint="myEndpoint"wait="500"/></actions></testcase>

JavaDSLdesigner

@Autowired@Qualifier("myEndpoint")privateEndpointmyEndpoint;

@CitrusTestpublicvoidreceiveTimeoutTest()receiveTimeout(myEndpoint).timeout(500);

JavaDSLrunner

@Autowired@Qualifier("myEndpoint")privateEndpointmyEndpoint;

@CitrusTestpublicvoidreceiveTimeoutTest()receiveTimeout(action->action.endpoint(myEndpoint).timeout(500));

CitrusReferenceGuide

143Timeout

Page 144: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Theactionofferstwoattributes:

endpoint:Referencetoamessageendpointthatwilltrytoreceivemessages.

wait/timeout:Timeperiodtowaitformessagestoarrive

Sometimesyoumaywanttoaddsomeselectoronthetimeoutreceivingaction.Thiswayyoucanveryselectivecheckonamessagetonotbepresentonamessagedestination.Thisispossiblewithdefiningamessageselectoronthetestactionasfollows.

XMLDSL

<expect-timeoutendpoint="myEndpoint"wait="500"><select>MessageId='123456789'<select/><expect-timeout/>

JavaDSLdesigner

@CitrusTestpublicvoidreceiveTimeoutTest()receiveTimeout(myEndpoint).selector("MessageId='123456789'").timeout(500);

JavaDSLrunner

@CitrusTestpublicvoidreceiveTimeoutTest()receiveTimeout(action->action.endpoint(myEndpoint).selector("MessageId='123456789'").timeout(500));

CitrusReferenceGuide

144Timeout

Page 145: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Echo

Theactionprintsmessagestotheconsole/logger.Thisfunctionalityisusefulwhendebuggingtestruns.Theproperty"message"definesthetextthatisprinted.Testermightuseittoprintoutdebugmessagesandvariablesasshownthenextcodeexample:

XMLDSL

<testcasename="echoTest"><variables><variablename="date"value="citrus:currentDate()"/></variables><actions><echo><message>HelloTestFramework</message></echo>

<echo><message>Currentdateis:$date</message></echo></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidechoTest()variable("date","citrus:currentDate()");

echo("HelloTestFramework");echo("Currentdateis:$date");

Resultontheconsole:

HelloTestFrameworkCurrenttimeis:05.08.2008

CitrusReferenceGuide

145Echo

Page 146: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Stoptime

Timemeasurementduringatestcanbeveryhelpful.Theactioncreatesandmonitorsmultipletimelines.Theactionofferstheattribute"id"toidentifyatimeline.Thetestercanofcourseusemorethanonetimelinewithdifferentidssimultaneously.

Readthenextexampleandyouwillunderstandthemixofdifferenttimelines:

XMLDSL

<testcasename="StopTimeTest"><actions><trace-time/>

<trace-timeid="time_line_id"/>

<sleepseconds="3.5"/>

<trace-timeid="time_line_id"/>

<sleepmilliseconds="5000"/>

<trace-time/>

<trace-timeid="time_line_id"/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidstopTimeTest()stopTime();stopTime("time_line_id");sleep(3.5);//dosomethingstopTime("time_line_id");sleep(5000);//dosomethingstopTime();stopTime("time_line_id");

Thetestoutputlookslikefollows:

StartingTimeWatcher:StartingTimeWatcher:time_line_id

CitrusReferenceGuide

146Stoptime

Page 147: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TimeWatchertime_line_idafter3500millisecondsTimeWatcherafter8500secondsTimeWatchertime_line_idafter8500milliseconds

NoteIncasenotimelineidisspecifiedtheframeworkwillmeasurethetimeforadefaulttimeline.Toprintoutthecurrentelapsedtimeforatimelineyousimplyhavetoplacetheactionintotheactionchainagainandagain,usingtherespectivetimelineidentifier.Theelapsedtimewillbeprintedouttotheconsoleeverytime.

CitrusReferenceGuide

147Stoptime

Page 148: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Createvariables

Asyouknowvariablesusuallyaredefinedatthebeginningofthetestcase(testcase-variables).Itmightalsobehelpfultoresetexistingvariablesaswellastodefinenewvariablesduringthetest.Theactionisabletodeclarenewvariablesoroverwriteexistingones.

XMLDSL

<testcasename="createVariablesTest"><variables><variablename="myVariable"value="12345"/><variablename="id"value="54321"/></variables><actions><echo><message>Currentvariablevalue:$myVariable</message></echo>

<create-variables><variablename="myVariable"value="$id"/><variablename="newVariable"value="'thisisatest'"/></create-variables>

<echo><message>Currentvariablevalue:$myVariable</message></echo>

<echo><message>Newvariable'newVariable'hasthevalue:$newVariable</message></echo></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidcreateVariableTest()variable("myVariable","12345");variable("id","54321");

echo("Currentvariablevalue:$myVariable");

createVariable("myVariable","$id");

CitrusReferenceGuide

148Createvariables

Page 149: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

createVariable("newVariable","thisisatest");

echo("Currentvariablevalue:$myVariable");

echo("Newvariable'newVariable'hasthevalue:$newVariable");

NotePleasenotethedifferencebetweenthevariable()methodandthecreateVariable()method.Thefirstinitializesthetestcasewiththetestvariables.Soallvariablesdefinedwiththismethodarevalidfromtheverybeginningofthetest.IncontrarytothatthecreateVariable()isexecutedwithinthetestactionchain.Thenewlycreatedvariablesarethenvalidfortherestofthetest.Trailingactionscanreferencethevariablesasusualwiththevariableexpression.

CitrusReferenceGuide

149Createvariables

Page 150: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Tracevariables

Youalreadyknowtheactionthatprintsmessagestotheconsoleorlogger.Theactionisspeciallydesignedtotraceallcurrentlyvalidtestvariablestotheconsole.Thiswasmainlyusedbyusfordebugreasons.Theusageisquitesimple:

XMLDSL

<testcasename="traceVariablesTest"><variables><variablename="myVariable"value="12345"/><variablename="nextVariable"value="54321"/></variables><actions><trace-variables><variablename="myVariable"/><variablename="nextVariable"/></trace-variables>

<trace-variables/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidtraceTest()variable("myVariable","12345");variable("nextVariable","54321");

traceVariables("myVariable","nextVariable");traceVariables();

Simplyaddtheactiontoyouractionchainandallvariableswillbeprintedouttotheconsole.Youareabletodefineaspecialsetofvariablesbyusingthechildelements.Seetheoutputthatwasgeneratedbythetestexampleabove:

CurrentvalueofvariablemyVariable=12345CurrentvalueofvariablenextVariable=54321

CitrusReferenceGuide

150Trace

Page 151: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

151Trace

Page 152: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Transform

The<transform>actiontransformsXMLfragmentswithXSLTinordertoconstructvariousXMLrepresentations.Thetransformationresultisstoredintoatestvariableforfurtherusage.Thepropertyxml-datadefinestheXMLsource,thatisgoingtobetransformed,whilexslt-datadefinestheXSLTtransformationrules.Theattributevariablespecifiesthetargettestvariablewhichreceivesthetransformationresult.ThetestermightusetheactiontotransformXMLmessagesasshowninthenextcodeexample:

XMLDSL

<testcasename="transformTest"><actions><transformvariable="result"><xml-data><![CDATA[<TestRequest><Message>HelloWorld!</Message></TestRequest>]]></xml-data><xslt-data><![CDATA[<xsl:stylesheetversion="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:templatematch="/"><html><body><h2>TestRequest</h2><p>Message:<xsl:value-ofselect="TestRequest/Message"/></p></body></html></xsl:template></xsl:stylesheet>]]></xslt-data></transform><echo><message>$result</message></echo></actions></testcase>

Thetransformationaboveresultsto:

CitrusReferenceGuide

152Transform

Page 153: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<html><body><h2>TestRequest</h2><p>Message:HelloWorld!</p></body></html>

IntheexampleweusedCDATAsectionstodefinethetransformationsourceaswellastheXSLtransformationrules.Asusualyoucanalsouseexternalfileresourceshere.Thetransformactionwithexternalfileresourceslookslikefollows:

<transformvariable="result"><xml-resourcefile="classpath:transform-source.xml"/><xslt-resourcefile="classpath:transform.xslt"/></transform>

TheJavaDSLalternativefortransformingdataviaXSTLinCitruslookslikefollows:

JavaDSLdesigner

@CitrusTestpublicvoidtransformTest()transform().source("<TestRequest>"+"<Message>HelloWorld!</Message>"+"</TestRequest>").xslt("<xsl:stylesheetversion=\"1.0\"xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n""<xsl:templatematch=\"/\">\n"+"<html>\n"+"<body>\n"+"<h2>TestRequest</h2>\n"+"<p>Message:<xsl:value-ofselect=\"TestRequest/Message\"/></p>\n""</body>\n"+"</html>\n"+"</xsl:template>\n"+"</xsl:stylesheet>").result("result");

echo("$result");

transform().source(newClassPathResource("com/consol/citrus/actions/transform-source.xml")).xslt(newClassPathResource("com/consol/citrus/actions/transform.xslt")).result("result");

CitrusReferenceGuide

153Transform

Page 154: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

echo("$result");

JavaDSLrunner

@CitrusTestpublicvoidtransformTest()transform(action->action.source("<TestRequest>"+"<Message>HelloWorld!</Message>"+"</TestRequest>").xslt("<xsl:stylesheetversion=\"1.0\"xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n""<xsl:templatematch=\"/\">\n"+"<html>\n"+"<body>\n"+"<h2>TestRequest</h2>\n"+"<p>Message:<xsl:value-ofselect=\"TestRequest/Message\"/></p>\n"+"</body>\n"+"</html>\n"+"</xsl:template>\n"+"</xsl:stylesheet>").result("result"));

echo("$result");

transform(action->action.source(newClassPathResource("com/consol/citrus/actions/transform-source.xml")).xslt(newClassPathResource("com/consol/citrus/actions/transform.xslt")).result("result"));

echo("$result");

Definingmulti-lineStringswithnestedquotesisnofuninJava.Soyoumaywanttouseexternalfileresourcesforyourscriptsasshowninthesecondpartoftheexample.InfactyoucouldalsousescriptlanguageslikeGroovyorScalathathavemuchbettersupportformulti-lineStrings.

CitrusReferenceGuide

154Transform

Page 155: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Groovyscriptexecution

GroovyisanagiledynamiclanguagefortheJavaPlatform.GroovyshipswithalotofverypowerfulfeaturesandfitsperfectlywithJavaasitisbasedonJavaandrunsinsidetheJVM.

TheCitrusGroovysupportmightbetheentranceforyoutowritecustomizedtestactions.YoucaneasilyexecuteGroovycodeinsideatestcase,justlikeanormaltestaction.ThewholetestcontextwithallvariablesisavailabletotheGroovyaction.Thismeanssomeonecanchangevariablevaluesorcreatenewvariablesveryeasily.

Let'shavealookatsomeexamplesinordertounderstandthepossibleGroovycodeinteractionsinCitrus:

XMLDSL

<testcasename="groovyTest"><variables><variablename="time"value="citrus:currentDate()"/></variables><actions><groovy>println'HelloCitrus'</groovy><groovy>println'Thevariableis:$time'</groovy><groovyresource="classpath:com/consol/citrus/script/example.groovy"/></actions></testcase>

JavaDSLdesigner

@CitrusTestpublicvoidgroovyTest()groovy("println'HelloCitrus'");groovy("println'Thevariableis:$time'");

groovy(newClassPathResource("com/consol/citrus/script/example.groovy"));

JavaDSLrunner

@CitrusTest

CitrusReferenceGuide

155Groovy

Page 156: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

publicvoidgroovyTest()groovy(action->action.script("println'HelloCitrus'"));groovy(action->action.script("println'Thevariableis:$time'"));

groovy(action->action.script(newClassPathResource("com/consol/citrus/script/example.groovy"

AsyoucanseeitispossibletowriteGroovycodedirectlyintothetestcase.CitruswillinterpretandexecutetheGroovycodeatruntime.Asusualnestedvariableexpressionsarereplacedwithrespectivevalues.IngeneralthisisdoneinadvancebeforetheGroovycodeisinterpreted.FormorecomplexGroovycodesectionswhichgrowinlinesofcodeyoucanalsoreferenceexternalfileresources.

AfterthisbasicGroovycodeusageinsideatestcasewemightbeinterestedaccessingthewholeTestContext.TheTestContextJavaobjectholdsalltestvariablesandfunctiondefinitionsforthetestcaseandcanbereferencedinGroovycodeviasimplenamingconvention.Justaccesstheobjectreference'context'andyouareabletomanipulatetheTestContext(e.g.settinganewvariablewhichisdirectlyreadyforuseinfollowingtestactions).

XMLDSL

<testcasename="groovyTest"><actions><groovy>context.setVariable("greetingText","HelloCitrus")printlncontext.getVariable("greetingText")</groovy><echo><message>Newvariable:$greetingText</message></echo></actions></testcase>

NoteTheimplicitTestContextaccessthatwasshownintheprevioussampleworkswithadefaultGroovyscripttemplateprovidedbyCitrus.TheGroovycodeyouwriteinthetestcaseisautomaticallysurroundedwithaGroovyscriptwhichtakescareofhandlingtheTestContext.Thedefaulttemplatelookslikefollows:

importcom.consol.citrus.*importcom.consol.citrus.variable.*importcom.consol.citrus.context.TestContextimportcom.consol.citrus.script.GroovyAction.ScriptExecutor

CitrusReferenceGuide

156Groovy

Page 157: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

publicclassGScriptimplementsScriptExecutorpublicvoidexecute(TestContextcontext)@SCRIPTBODY@

Yourcodeisplacedinsubstitutiontothe@SCRIPTBODY@placeholder.NowyoumightunderstandhowCitrushandlesthecontextautomatically.YoucanalsowriteyourownscripttemplatesmakingmoreadvancedusageofotherJavaAPIsandGroovycode.Justaddascripttemplatepathtothetestactionlikethis:

<groovyscript-template="classpath:my-custom-template.groovy">[...]</groovy>

Ontheotherhandyoucandisabletheautomaticscripttemplatewrappinginyouractionatall:

<groovyuse-script-template="false">println'JustusesomeGroovycode'</groovy>

ThenextexampledealswithadvancedGroovycodeandwritingwholeclasses.WewriteanewGroovyclasswhichimplementstheScriptExecutorinterfaceofferedbyCitrus.ThisinterfacedefinesaspecialexecutemethodandprovidesaccesstothewholeTestContextforadvancedtestvariablesaccess.

<testcasename="groovyTest"><variables><variablename="time"value="citrus:currentDate()"/></variables><actions><groovy><![CDATA[importcom.consol.citrus.*importcom.consol.citrus.variable.*importcom.consol.citrus.context.TestContextimportcom.consol.citrus.script.GroovyAction.ScriptExecutor

publicclassGScriptimplementsScriptExecutorpublicvoidexecute(TestContextcontext)printlncontext.getVariable("time")

CitrusReferenceGuide

157Groovy

Page 158: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

]]></groovy></actions></testcase>

ImplementingtheScriptExecutorinterfaceinacustomGroovyclassisapplicableforveryspecialtestcontextmanipulationsasyouareabletoimportanduseotherJavaAPIclassesinthiscode.

CitrusReferenceGuide

158Groovy

Page 159: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Failingthetest

Thefailactionwillgenerateanexceptioninordertoterminatethetestcasewitherror.Thetestcasewillthereforenotbesuccessfulinthereports.

Theusercanspecifyacustomerrormessagefortheexceptioninordertodescribetheerrorcause.Hereisaverysimpleexampletoclarifythesyntax:

XMLDSL

<testcasename="failTest"><actions><failmessage="Testwillfailwithcustommessage"/></actions></testcase>

Testresults:

Executionoftest:failTestfailed!Nestedexceptionis:com.consol.citrus.exceptions.CitrusRuntimeException:Testwillfailwithcustommessage

[...]

CITRUSTESTRESULTS

failTest:failed-Exceptionis:Testwillfailwithcustommessage

Found1testcasestoexecuteSkipped0testcases(0.0%)Executed1testcases,containing3actionsTestsfailed:1(100.0%)Testssuccessfully:0(0.0%)

WhileusingtheJavaDSLtestermightwanttoraisesomeJavaexceptionsinthemiddleofconfiguringthetestcase.Butthisisnotpossibleaswehavetoseparatethedesigntimeandtheexecutiontimeofthetestcase.The@CitrusTestannotatedconfigurationmethodiscalledforbuildingupthewholetestcase.Afterthismethodwasprocessedthetestgetsexecutedinruntimeoththetest.Ifyouspecifyathrowsexceptionstatementintheconfigurationmethodthiswillnotbedoneatruntimebutatdesigntime.ThisiswhyyouhavetousethespecialfailtestactionwhichraisesaJavaexceptionduringtheruntimeofthetest.Thenextexamplewillnotworkasexpected:

CitrusReferenceGuide

159Fail

Page 160: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLdesignerandrunner

@CitrusTestpublicvoidwrongUsageSample()//sometestactions

thrownewValidationException("Thistestshouldfailnow");//doesnotworkasexpected

Thevalidationexceptionaboveisdirectlyraisedbeforethetestisabletostartasthe@CitrusTestannotatedmethoddoesnotrepresentthetestruntime.Insteadofthiswehavetousethefailactionasfollows:

JavaDSLdesignerandrunner

@CitrusTestpublicvoidfailTest()//sometestactions

fail("Thistestshouldfailnow");//failsattestruntimeasexpected

Nowthetestfailsatruntimeasthefailactionisraisedduringthetestexecutionasexpected.

CitrusReferenceGuide

160Fail

Page 161: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Input

Duringthetestcaseexecutionitispossibletoreadsomeuserinputfromthecommandline.Thetestexecutionwillstopandwaitforkeyboardinputsoverthestandardinputstream.Theuserhastotypetheinputandenditwiththereturnkey.

Theuserinputisstoredtotherespectivevariablevalue.

XMLDSL

<testcasename="inputTest"><variables><variablename="userinput"value=""></variable><variablename="userinput1"value=""></variable><variablename="userinput2"value="y"></variable><variablename="userinput3"value="yes"></variable><variablename="userinput4"value=""></variable></variables><actions><input/><echo><message>userinputwas:$userinput</message></echo>

<inputmessage="Nowpressenter:"variable="userinput1"/><echo><message>userinputwas:$userinput1</message></echo>

<inputmessage="Doyouwanttocontinue?"valid-answers="y/n"variable="userinput2"/><echo><message>userinputwas:$userinput2</message></echo>

<inputmessage="Doyouwanttocontinue?"valid-answers="yes/no"variable="userinput3"/><echo><message>userinputwas:$userinput3</message></echo>

<inputvariable="userinput4"/><echo><message>userinputwas:$userinput4</message></echo></actions></testcase>

Asyoucanseetheinputactioniscustomizablewithapromptmessagethatisdisplayedtotheuserandsomevalidanswerpossibilities.Theuserinputisstoredtoatestvariableforfurtheruseinthetestcase.Indetailtheinputactionoffersfollowingattributes:

message->messagedisplayedtotheuser

valid-answers->optionalslashseparatedstringcontainingthepossiblevalidanswers

CitrusReferenceGuide

161Input

Page 162: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

variable->resultvariablenameholdingtheuserinput(default=$userinput)

ThesameactioninJavaDSLnowlooksquitefamiliartousalthoughattributenamingisslightlydifferent:

JavaDSLdesigner

@CitrusTestpublicvoidinputActionTest()variable("userinput","");variable("userinput1","");variable("userinput2","y");variable("userinput3","yes");variable("userinput4","");

input();echo("userinputwas:$userinput");input().message("Nowpressenter:").result("userinput1");echo("userinputwas:$userinput1");input().message("Doyouwanttocontinue?").answers("y","n").result("userinput2");echo("userinputwas:$userinput2");input().message("Doyouwanttocontinue?").answers("yes","no").result("userinput3");echo("userinputwas:$userinput3");input().result("userinput4");echo("userinputwas:$userinput4");

JavaDSLrunner

@CitrusTestpublicvoidinputActionTest()variable("userinput","");variable("userinput1","");variable("userinput2","y");variable("userinput3","yes");variable("userinput4","");

input(action->);echo("userinputwas:$userinput");input(action->action.message("Nowpressenter:").result("userinput1"));echo("userinputwas:$userinput1");input(action->action.message("Doyouwanttocontinue?").answers("y","n").result("userinput2"echo("userinputwas:$userinput2");input(action->action.message("Doyouwanttocontinue?").answers("yes","no").result("userinput3"echo("userinputwas:$userinput3");input(action->action.result("userinput4"));echo("userinputwas:$userinput4");

CitrusReferenceGuide

162Input

Page 163: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Whentheuserinputisrestrictedtoasetofvalidanswerstheinputvalidationofcoursecanfailduetomismatch.Thisisthecasewhentheuserprovidessomeinputnotmatchingthevalidanswersgiven.Inthiscasetheuserisagainaskedtoprovidevalidinput.Thetestactionwillcontinuetoaskforvalidinputuntilavalidanswerisgiven.

NoteUserinputsmaynotfittoautomatictestingintermsofcontinuousintegrationtestingwherenouserispresenttotypeinthecorrectansweroverthekeyboard.Inthiscaseyoucanalwaysskiptheuserinputinadvancebyspecifyingavariablethatmatchestheuserinputvariablename.Astheuserinputvariableisthenalreadypresenttheuserinputismissedoutandthetestproceedsautomatically.

CitrusReferenceGuide

163Input

Page 164: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Load

Youareabletoloadpropertiesfromexternalpropertyfilesandstorethemastestvariables.Theactionwillrequireafileresourceeitherfromclasspathorfilesysteminordertoreadthepropertyvalues.

Letuslookatanexampletogetanideaaboutthisaction:

Contentofload.properties:

username=MickeyMousegreeting.text=HelloTestFramework

XMLDSL

<testcasename="loadPropertiesTest"><actions><load><propertiesfile="file:tests/resources/load.properties"/></load>

<trace-variables/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidloadPropertiesTest()load("file:tests/resources/load.properties");

traceVariables();

Output:

Currentvalueofvariableusername=MickeyMouseCurrentvalueofvariablegreeting.text=HelloTestFramework

Theactionwillloadallavailablepropertiesinthefileload.propertiesandstorethemtothetestcaseaslocalvariables.

CitrusReferenceGuide

164Load

Page 165: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ImportantPleasebeawareofthefactthatexistingvariablesareoverwritten!

CitrusReferenceGuide

165Load

Page 166: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Wait

Withthisactionyoucanmakeyourtestwaituntilacertainconditionissatisfied.Theattributesecondsdefinestheamountoftimetowaitinseconds.Youcanalsousethemillisecondsattributeforamorefinegrainedtimevalue.Theattributeintervaldefinestheamountoftimetowaitbetweeneachcheck.Theintervalisalwaysspecifiedasmillisecondtimeinterval.

Ifthecheckdoesnotexceedwithinthedefinedoverallwaitingtimethenthetestexecutionfailswithanappropriateerrormessage.Therearedifferenttypesofconditionstocheck.

http:ThisconditionisbasedonaHttprequestcallonaserverendpoint.CitruswillwaituntiltheHttpresponseisasdefined(e.g.Http200OK).Thisisusefulwhenyouwanttowaitforaservertostart.file:Thisconditionchecksfortheexistenceofafileonthelocalfilesystem.Citruswillwaituntilthefileispresent.message:Thisconditionchecksfortheexistenceofamessageinthelocalmessagestoreofthecurrenttestcase.Citruswillwaituntilthemessagewiththegivennameispresent.

Nextletushavealookatasimpleexample:

XMLDSL

<testcasename="waitTest"><actions><waitseconds="10"interval="2000"><httpurl="http://sample.org/resource"statusCode="200"timeout="2000"/><wait/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidwaitTest()waitFor().http("http://sample.org/resource").seconds(10L).interval(2000L);

CitrusReferenceGuide

166Wait

Page 167: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheexamplewaitsforsomeHttpserverresourcetobeavailablewithHttp200OKresponse.CitruswilluseHEADrequestmethodbydefault.YoucansettherequestmethodwiththemethodattributeontheHttpcondition.

Nextletushavealookatthefileconditionusage:

XMLDSL

<testcasename="waitTest"><actions><waitseconds="10"interval="2000"><filepath="path/to/resource/file.txt"/><wait/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidwaitTest()waitFor().file("path/to/resource/file.txt");

Citruschecksforthefiletoexistunderthegivenpath.Onlyifthefileexiststhetestwillcontinuewithfurthertestactions.

Nextletushavealookatthemessageconditionusage:

XMLDSL

<testcasename="waitTest"><actions><waitseconds="10"interval="2000"><messagename="helloRequest"/><wait/></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidwaitTest()waitFor().message("helloRequest");

CitrusReferenceGuide

167Wait

Page 168: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitruschecksforthemessagewiththenamehelloRequestinthelocalmessagestore.Onlyifthemessagewiththegivennameisfoundthetestwillcontinuewithfurthertestactions.Thelocalmessagestoreisautomaticallyfilledwithallexchangedmessages(sendorreceive)inatestcase.Themessagenamesaredefinedintherespectivesendorreceiveoperationsinthetest.

Whenshouldsomebodyusethisaction?Thisactionisveryusefulwhenyouwantyourtesttowaitforacertaineventtooccurbeforecontinuingwiththetestexecution.ForexampleifyouwishthatyourtestwaitsuntilaDockercontainerisstartedorforanapplicationtocreatealogfilebeforecontinuing,thenusethisaction.Youcanalsocreateyourownconditionstatementsandbindittothetestaction.

CitrusReferenceGuide

168Wait

Page 169: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

PurgingJMSdestinations

PurgingJMSdestinationsduringthetestrunisquiteessential.DifferenttestcasescaninfluenceeachotherwhensendingmessagestothesameJMSdestinations.Atestcaseshouldonlyreceivethosemessagesthatactuallybelongtoit.ThereforeitisagoodideatopurgeallJMSqueuedestinationsbetweenthetestcases.ObsoletemessagesthatarestuckinaJMSqueueforsomereasonarethenremovedsothatthefollowingtestcaseisnotoffended.

NoteCitrusprovidesspecialsupportforJMSrelatedfeatures.WehavetoactivatethoseJMSfeaturesinourtestcasebyaddingaspecial"jms"namespaceandschemadefinitionlocationtothetestcaseXML.

<spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jms="http://www.citrusframework.org/schema/jms/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsdhttp://www.citrusframework.org/schema/jms/testcasehttp://www.citrusframework.org/schema/jms/testcase/citrus-jms-testcase.xsd">

[...]

</beans>

NowwearereadytousetheJMSfeaturesinourtestcaseinordertopurgesomeJMSqueues.Thiscanbedonewithfollowingactiondefinition:

XMLDSL

<testcasename="purgeTest"><actions><jms:purge-jms-queues><jms:queuename="Some.JMS.QUEUE.Name"/><jms:queuename="Another.JMS.QUEUE.Name"/><jms:queuename="My.JMS.QUEUE.Name"/></jms:purge-jms-queues>

<jms:purge-jms-queuesconnection-factory="connectionFactory"><jms:queuename="Some.JMS.QUEUE.Name"/><jms:queuename="Another.JMS.QUEUE.Name"/>

CitrusReferenceGuide

169PurgeJMSqueues

Page 170: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<jms:queuename="My.JMS.QUEUE.Name"/></jms:purge-jms-queues></actions></testcase>

Noticethatwehavereferencedthejmsnamespacewhenusingthepurge-jms-queuestestaction.

JavaDSLdesigner

@Autowired@Qualifier("connectionFactory")privateConnectionFactoryconnectionFactory;

@CitrusTestpublicvoidpurgeTest()purgeQueues().queue("Some.JMS.QUEUE.Name").queue("Another.JMS.QUEUE.Name");

purgeQueues(connectionFactory).timeout(150L)//customtimeoutinms.queue("Some.JMS.QUEUE.Name").queue("Another.JMS.QUEUE.Name");

JavaDSLrunner

@Autowired@Qualifier("connectionFactory")privateConnectionFactoryconnectionFactory;

@CitrusTestpublicvoidpurgeTest()purgeQueues(action->action.queue("Some.JMS.QUEUE.Name").queue("Another.JMS.QUEUE.Name"));

purgeQueues(action->action.connectionFactory(connectionFactory).timeout(150L)//customtimeoutinms.queue("Some.JMS.QUEUE.Name").queue("Another.JMS.QUEUE.Name"));

CitrusReferenceGuide

170PurgeJMSqueues

Page 171: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

PurgingtheJMSqueuesineverytestcaseisquiteexhaustingbecauseeverytestcaseneedstodefineapurgingactionattheverybeginningofthetest.Fortunatelythetestsuitedefinitionofferstaskstorunbefore,betweenandafterthetestcaseswhichshouldeaseupthistasksalot.Thetestsuiteoffersaverysimplewaytopurgethedestinationsbetweenthetests.Seetestsuite-before-testformoreinformationaboutthis.

AsyoucanseeinthenextexampleitisquiteeasytospecifyagroupofdestinationsintheSpringconfigurationthatgetpurgedbeforeatestisexecuted.

<citrus:before-testid="purgeBeforeTest"><citrus:actions><jms:purge-jms-queues><jms:queuename="Some.JMS.QUEUE.Name"/><jms:queuename="Another.JMS.QUEUE.Name"/></jms:purge-jms-queues></citrus:actions></citrus:before-test>

NotePleasekeepinmindthattheJMSrelatedconfigurationcomponentsinCitrusbelongtoaseparateXMLnamespacejms:.WehavetoaddthisnamespacedeclarationtoeachtestcaseXMLandSpringbeanXMLconfigurationfileasdescribedattheverybeginningofthissection.

Thesyntaxforpurgingthedestinationsisthesameasweuseditinsidethetestcase.SonowweareabletopurgeJMSdestinationswithgivendestinationnames.ButsometimeswedonotwanttorelyonqueueortopicnamesasweretrievedestinationsoverJNDIforinstance.WecandealwithdestinationscomingfromJNDIlookuplikefollows:

<jee:jndi-lookupid="jmsQueueHelloRequestIn"jndi-name="jms/jmsQueueHelloRequestIn"/><jee:jndi-lookupid="jmsQueueHelloResponseOut"jndi-name="jms/jmsQueueHelloResponseOut"/>

<citrus:before-testid="purgeBeforeTest"><citrus:actions><jms:purge-jms-queues><jms:queueref="jmsQueueHelloRequestIn"/><jms:queueref="jmsQueueHelloResponseOut"/></jms:purge-jms-queues></citrus:actions></citrus:before-test>

CitrusReferenceGuide

171PurgeJMSqueues

Page 172: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Wejustusetheattribute'ref'insteadof'name'andCitrusislookingforabeanreferenceforthatidentifierthatresolvestoaJMSdestination.YoucanusetheJNDIbeanreferencesinsideatestcase,too.

XMLDSL

<testcasename="purgeTest"><actions><jms:purge-jms-queues><jms:queueref="jmsQueueHelloRequestIn"/><jms:queueref="jmsQueueHelloResponseOut"/></jms:purge-jms-queues></actions></testcase>

OfcourseyoucanusequeueobjectreferencesalsoinJavaDSLtestcases.HereweeasilycanuseSpring'sdependencyinjectionwithautowiringtogettheobjectreferencesfromtheIoCcontainer.

JavaDSLdesigner

@Autowired@Qualifier("jmsQueueHelloRequestIn")privateQueuejmsQueueHelloRequestIn;

@Autowired@Qualifier("jmsQueueHelloResponseOut")privateQueuejmsQueueHelloResponseOut;

@CitrusTestpublicvoidpurgeTest()purgeQueues().queue(jmsQueueHelloRequestIn).queue(jmsQueueHelloResponseOut);

JavaDSLrunner

@Autowired@Qualifier("jmsQueueHelloRequestIn")privateQueuejmsQueueHelloRequestIn;

@Autowired@Qualifier("jmsQueueHelloResponseOut")privateQueuejmsQueueHelloResponseOut;

CitrusReferenceGuide

172PurgeJMSqueues

Page 173: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidpurgeTest()purgeQueues(action->action.queue(jmsQueueHelloRequestIn).queue(jmsQueueHelloResponseOut));

NoteYoucanmixqueuenameandqueueobjectreferencesasyoulikewithinonesinglepurgequeuetestaction.

CitrusReferenceGuide

173PurgeJMSqueues

Page 174: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Purgingmessagechannels

MessagechannelsdefinecentralmessagingdestinationsinCitrus.Thesearenamelyinmemorymessagequeuesholdingmessagesfortestcases.Thesemessagesmaybecomeobsoleteduringatestrun,especiallywhentestcasesfailandstopintheirmessageconsumption.Purgingthesemessagechanneldestinationsisessentialinthesescenariosinordertonotinfluenceupcomingtestcases.Eachtestcaseshouldonlyreceivethosemessagesthatactuallyrefertothetestmodel.Thereforeitisagoodideatopurgeallmessagechanneldestinationsbetweenthetestcases.Obsoletemessagesthatgetstuckinamessagechanneldestinationforsomereasonarethenremovedsothatupcomingtestcasearenotbroken.

Followingactiondefinitionpurgesallmessagesfromalistofmessagechannels:

XMLDSL

<testcasename="purgeChannelTest"><actions><purge-channel><channelname="someChannelName"/><channelname="anotherChannelName"/></purge-channel>

<purge-channel><channelref="someChannel"/><channelref="anotherChannel"/></purge-channel></actions></testcase>

AsyoucanseethetestactionsupportschannelnamesaswellaschannelreferencestoSpringbeaninstances.WhenusingchannelreferencesyourefertotheSpringbeanidornameinyourapplicationcontext.

TheJavaDSLworksquitesimilarasyoucanreadfromnextexamples:

JavaDSLdesigner

@Autowired@Qualifier("channelResolver")privateDestinationResolver<MessageChannel>channelResolver;

@CitrusTestpublicvoidpurgeTest()

CitrusReferenceGuide

174Purgechannels

Page 175: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

purgeChannels().channelResolver(channelResolver).channelNames("ch1","ch2","ch3").channel("ch4");

JavaDSLrunner

@Autowired@Qualifier("channelResolver")privateDestinationResolver<MessageChannel>channelResolver;

@CitrusTestpublicvoidpurgeTest()purgeChannels(action->action.channelResolver(channelResolver).channelNames("ch1","ch2","ch3").channel("ch4"));

Thechannelresolverreferenceisoptional.BydefaultCitruswillautomaticallyuseaSpringapplicationcontextchannelresolversoyoujusthavetousetherespectiveSpringbeannamesthatareconfiguredintheSpringapplicationcontext.Howeversettingacustomchannelresolvermaybeadequateforyouinsomespecialcases.

WhilespeakingofSpringapplicationcontextbeanreferencesthenextexampleusessuchbeanreferencesforchannelstopurge.

JavaDSLdesigner

@Autowired@Qualifier("channel1")privateMessageChannelchannel1;

@Autowired@Qualifier("channel2")privateMessageChannelchannel2;

@Autowired@Qualifier("channel3")privateMessageChannelchannel3;

@CitrusTestpublicvoidpurgeTest()purgeChannels().channels(channel1,channel2).channel(channel3);

CitrusReferenceGuide

175Purgechannels

Page 176: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLrunner

@Autowired@Qualifier("channel1")privateMessageChannelchannel1;

@Autowired@Qualifier("channel2")privateMessageChannelchannel2;

@Autowired@Qualifier("channel3")privateMessageChannelchannel3;

@CitrusTestpublicvoidpurgeTest()purgeChannels(action->action.channels(channel1,channel2).channel(channel3));

Messageselectorsenableyoutoselectivelyremovemessagesfromthedestination.Allmessagesthatpassthemessageselectionlogicgetdeletedtheothermessageswillremainunchangedinsidethechanneldestination.ThemessageselectorisaSpringbeanthatimplementsaspecialmessageselectorinterface.Apossibleimplementationcouldbeaselectordeletingallmessagesthatareolderthanfiveseconds:

importorg.springframework.messaging.Message;importorg.springframework.integration.core.MessageSelector;

publicclassTimeBasedMessageSelectorimplementsMessageSelector

publicbooleanaccept(Message<?>message)if(System.currentTimeMillis()-message.getHeaders().getTimestamp()>5000)returnfalse;elsereturntrue;

NoteThemessageselectorreturnsfalseforthosemessagesthatshouldbedeletedfromthechannel!

CitrusReferenceGuide

176Purgechannels

Page 177: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

YousimplydefinethemessageselectorasanewSpringbeanintheCitrusapplicationcontextandreferenceitinyourtestactionproperty.

<beanid="specialMessageSelector"class="com.consol.citrus.special.TimeBasedMessageSelector"/>

Nowletushavealookathowyoureferencetheselectorinyourtestcase:

XMLDSL

<purge-channelsmessage-selector="specialMessageSelector"><channelname="someChannelName"/><channelname="anotherChannelName"/></purge-channels>

JavaDSLdesigner

@Autowired@Qualifier("specialMessageSelector")privateMessageSelectorspecialMessageSelector;

@CitrusTestpublicvoidpurgeTest()purgeChannels().channelNames("ch1","ch2","ch3").selector(specialMessageSelector);

JavaDSLrunner

@Autowired@Qualifier("specialMessageSelector")privateMessageSelectorspecialMessageSelector;

@CitrusTestpublicvoidpurgeTest()purgeChannels(action->action.channelNames("ch1","ch2","ch3").selector(specialMessageSelector));

CitrusReferenceGuide

177Purgechannels

Page 178: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IntheexamplesaboveweuseamessageselectorimplementationthatgetsinjectedviaSpringIoCcontainer.

Purgingchannelsineachtestcaseeverytimeisquiteexhaustingbecauseeverytestcaseneedstodefineapurgingactionattheverybeginningofthetest.Amorestraightforwardapproachwouldbetointroducesomepurgingactionwhichisautomaticallyexecutedbeforeeachtest.FortunatelytheCitrustestsuiteoffersaverysimplewaytodothis.Itisdescribedintestsuite-before-test.

Whenusingthespecialactionsequencebeforetestcasesweareabletopurgechanneldestinationseverytimeatestcaseexecutes.SeetheupcomingexampletofindouthowtheactionisdefinedintheSpringconfigurationapplicationcontext.

<citrus:before-testid="purgeBeforeTest"><citrus:actions><purge-channel><channelname="fooChannel"/><channelname="barChannel"/></purge-channel></citrus:actions></citrus:before-test>

Justusethisbefore-testbeanintheSpringbeanapplicationcontextandthepurgechannelactionisactive.Obsoletemessagesthatarewaitingonthemessagechannelsforconsumptionarepurgedbeforethenexttestinlineisexecuted.

TipPurgingmessagechannelsbecomesalsoveryinterestingwhenworkingwithserverinstancesinCitrus.Eachservercomponentautomaticallyhasaninboundmessagechannelwhereincomingmessagesarestoredtointernally.Soifyouneedtocleanupaserverthathasalreadystoredsomeincomingmessagesyoucandothiseasilybypurgingtheinternalmessagechannel.ThemessagechannelfollowsanamingconventionserverName.inboundwhereserverNameistheSpringbeannameoftheCitrusserverendpointcomponent.Ifyoupurgethisinternalchannelinabeforetestnatureyouaresurethatobsoletemessagesonaserverinstancegetpurgedbeforeeachtestisexecuted.

CitrusReferenceGuide

178Purgechannels

Page 179: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Purgingendpoints

Citrusworkswithmessageendpointswhensendingandreceivingmessages.Ingeneralendpointscanalsoqueuemessages.ThisisespeciallythecasewhenusingJMSmessageendpointsoranyserverendpointcomponentinCitrus.Theseareinmemorymessagequeuesholdingmessagesfortestcases.Thesemessagesmaybecomeobsoleteduringatestrun,especiallywhenatestcasethatwouldconsumethemessagesfails.Deletingallmessagesfromamessageendpointisthereforeausefultaskandisessentialinsuchscenariossothatupcomingtestcasesarenotinfluenced.Eachtestcaseshouldonlyreceivethosemessagesthatactuallyrefertothetestmodel.Thereforeitisagoodideatopurgeallmessageendpointdestinationsbetweenthetestcases.Obsoletemessagesthatgetstuckinamessageendpointdestinationforsomereasonarethenremovedsothatupcomingtestcasearenotbroken.

Followingactiondefinitionpurgesallmessagesfromalistofmessageendpoints:

XMLDSL

<testcasename="purgeEndpointTest"><actions><purge-endpoint><endpointname="someEndpointName"/><endpointname="anotherEndpointName"/></purge-endpoint>

<purge-endpoint><endpointref="someEndpoint"/><endpointref="anotherEndpoint"/></purge-endpoint></actions></testcase>

AsyoucanseethetestactionsupportsendpointnamesaswellasendpointreferencestoSpringbeaninstances.WhenusingendpointreferencesyourefertotheSpringbeannameinyourapplicationcontext.

TheJavaDSLworksquitesimilar-havealook:

JavaDSLdesigner

@Autowired@CitrusTestpublicvoidpurgeTest()

CitrusReferenceGuide

179Purgeendpoints

Page 180: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

purgeEndpoints().endpointNames("endpoint1","endpoint2","endpoint3").endpoint("endpoint4");

JavaDSLrunner

@Autowired@CitrusTestpublicvoidpurgeTest()purgeEndpoints(action->action.endpointNames("endpoint1","endpoint2","endpoint3").endpoint("endpoint4"));

WhenusingtheJavaDSLwecaninjectendpointobjectswithSpringbeancontainerIoC.Thenextexampleusessuchbeanreferencesforendpointsinapurgeaction.

JavaDSLdesigner

@Autowired@Qualifier("endpoint1")privateEndpointendpoint1;

@Autowired@Qualifier("endpoint2")privateEndpointendpoint2;

@Autowired@Qualifier("endpoint3")privateEndpointendpoint3;

@CitrusTestpublicvoidpurgeTest()purgeEndpoints().endpoints(endpoint1,endpoint2).endpoint(endpoint3);

JavaDSLrunner

@Autowired@Qualifier("endpoint1")privateEndpointendpoint1;

@Autowired@Qualifier("endpoint2")

CitrusReferenceGuide

180Purgeendpoints

Page 181: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

privateEndpointendpoint2;

@Autowired@Qualifier("endpoint3")privateEndpointendpoint3;

@CitrusTestpublicvoidpurgeTest()purgeEndpoints(action->action.endpoints(endpoint1,endpoint2).endpoint(endpoint3));

Messageselectorsenableyoutoselectivelyremovemessagesfromanendpoint.Allmessagesthatmeetthemessageselectorconditiongetdeletedandtheothermessagesremaininsidetheendpointdestination.ThemessageselectoriseitheranormalStringname-valuerepresentationoramapofkeyvaluepairs:

XMLDSL

<purge-endpoints><selector><value>operation='sayHello'</value></selector><endpointname="someEndpointName"/><endpointname="anotherEndpointName"/></purge-endpoints>

JavaDSLdesigner

@CitrusTestpublicvoidpurgeTest()purgeEndpoints().endpointNames("endpoint1","endpoint2","endpoint3").selector("operation='sayHello'");

JavaDSLrunner

@CitrusTestpublicvoidpurgeTest()purgeEndpoints(action->action.endpointNames("endpoint1","endpoint2","endpoint3").selector("operation='sayHello'"));

CitrusReferenceGuide

181Purgeendpoints

Page 182: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IntheexamplesaboveweuseaStringtorepresentthemessageselectorexpression.Ingeneralthemessageselectoroperatesonthemessageheader.SofollowingonfromthatweremoveallmessagesselectivelythathaveamessageheaderoperationwithitsvaluesayHello.

Purgingendpointsineachtestcaseeverytimeisquiteexhaustingbecauseeverytestcaseneedstodefineapurgingactionattheverybeginningofthetest.Amorestraightforwardapproachwouldbetointroducesomepurgingactionwhichisautomaticallyexecutedbeforeeachtest.FortunatelytheCitrustestsuiteoffersaverysimplewaytodothis.Itisdescribedintestsuite-before-test.

Whenusingthespecialactionsequencebeforetestcasesweareabletopurgeendpointdestinationseverytimeatestcaseexecutes.SeetheupcomingexampletofindouthowtheactionisdefinedintheSpringconfigurationapplicationcontext.

<citrus:before-testid="purgeBeforeTest"><citrus:actions><purge-endpoint><endpointname="fooEndpoint"/><endpointname="barEndpoint"/></purge-endpoint></citrus:actions></citrus:before-test>

Justusethisbefore-testbeanintheSpringbeanapplicationcontextandthepurgeendpointactionisactive.Obsoletemessagesthatarewaitingonthemessageendpointsforconsumptionarepurgedbeforethenexttestinlineisexecuted.

TipPurgingmessageendpointsbecomesalsoveryinterestingwhenworkingwithserverinstancesinCitrus.Eachservercomponentautomaticallyhasaninboundmessageendpointwhereincomingmessagesarestoredtointernally.Citruswillautomaticallyusethisincomingmessageendpointastargetforthepurgeactionsoyoucanjustusetheserverinstanceasyouknowitfromyourconfigurationinanypurgeaction.

CitrusReferenceGuide

182Purgeendpoints

Page 183: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Assertfailure

CitrustestactionsfailwithJavaexceptionsanderrormessages.Thisgivesyoutheopportunitytoexpectanactiontofailduringtestexecution.YoucansimpleassertaJavaexceptiontobethrownduringexecution.Seetheexampleforanassertactiondefinitioninatestcase:

XMLDSL

<testcasename="assertFailureTest"><actions><assertexception="com.consol.citrus.exceptions.CitrusRuntimeException"message="Unknownvariable$date"><when><echo><message>Currentdateis:$date</message></echo></when></assert></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidassertTest()assertException().exception(com.consol.citrus.exceptions.CitrusRuntimeException.class).message("Unknownvariable$date").when(echo("Currentdateis:$date"));

NoteNotethattheassertactionrequiresanexception.Incasenoexceptionisthrownbytheembeddedtestactiontheassertionandthetestcasewillfail!

Theassertactionalwayswrapsasingletestaction,whichisthenmonitoredforfailure.Incasethenestedtestactionfailswitherroryoucanvalidatetheerrorinitstypeanderrormessage(optional).Thefailurehastofittheexpectedoneexactlyotherwisetheassertionfailsitself.

ImportantImportanttonoticeisthefactthatassertedexceptionsdonotcausefailureofthetestcase.Asyouexceptthefailuretohappenthetestcontinueswithitsworkoncetheassertionisdonesuccessfully.

CitrusReferenceGuide

183Assert

Page 184: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

184Assert

Page 185: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Catchexceptions

InthepreviouschapterwehaveseenhowtoexpectfailuresinCitruswithassertaction.Nowtheassertactionisdesignedforsingleactionstobemonitoredandforfailurestobeexpectedinanycase.The'catch'actionincontrarycanholdseveralnestedtestactionsandexceptionfailureisoptional.

Thenestedactionsareerrorproofforthechosenexceptiontype.Thismeanspossibleexceptionsarecaughtandignored-thetestcasewillnotfailforthisexceptiontype.Butonlyforthisparticularexceptiontype!Otherexceptiontypesthatoccurduringexecutiondocausethetesttofailasusual.

XMLDSL

<testcasename="catchExceptionTest"><actions><catchexception="com.consol.citrus.exceptions.CitrusRuntimeException"><echo><message>Currentdateis:$date</message></echo></catch></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidcatchTest()catchException().exception(CitrusRuntimeException.class).when(echo("Currentdateis:$date"));

ImportantNotethatthereisnovalidationavailableinacatchblock.Socatchingexceptionsisjusttomakeatestmorestabletowardserrorsthatcanoccur.Thecaughtexceptiondoesnotcauseanyfailureinthetest.Thetestcasemaycontinuewithexecutionasiftherewasnotfailure.Alsonoticethatthecatchactionisalsohappywhennoexceptionatallisraised.Incontrarytothattheassertactionrequirestheexceptionandanassertactionisfailinginpositiveprocessing.

Catchingexceptionslikethismayonlyfittoveryerrorproneactionblockswherefailuresdonotharmthetestcasesuccess.Otherwiseafailureinatestactionshouldalwaysreflecttothewholetestcasetofailwitherrors.

CitrusReferenceGuide

185Catch

Page 186: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NoteJavadevelopersmightaskwhynotusetry-catchJavablockinstead?Theanswerissimpleyetveryimportanttounderstand.ThetestmethodiscalledbytheJavaDSLtestcasebuilderforbuildingtheCitrustest.Thiscanbereferredtoasthedesigntimeofthetest.Afterthebuildingtestmethodwasprocessedthetestgetsexecuted,whichcanbecalledtheruntimeofthetest.Thismeansthatatry-catchblockwithinthedesigntimemethodwillneverperformduringthetestrun.TheonlyreliablewaytoaddthecatchcapabilitytothetestaspartofthetestcaseruntimeistousetheCitrustestactionwhichgetsexecutedduringtestruntime.

CitrusReferenceGuide

186Catch

Page 187: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

RunningApacheAntbuildtargets

Theactionloadsabuild.xmlAntfileandexecutesoneormoretargetsintheAntproject.ThetargetisexecutedwithoptionalbuildpropertiespassedtotheAntrun.TheAntbuildoutputisloggedwithCitrusloggerandthetestcasesuccessisboundtotheAntbuildsuccess.ThismeansincasetheAntbuildfailsforsomereasonthetestcasewillalsofailwithbuildexceptionaccordingly.

SeethisbasicAntrunexampletoseehowitworkswithinyourtestcase:

XMLDSL

<testcasename="AntRunTest"><variables><variablename="today"value="citrus:currentDate()"/></variables><actions><antbuild-file="classpath:com/consol/citrus/actions/build.xml"><executetarget="sayHello"/><properties><propertyname="date"value="$today"/><propertyname="welcomeText"value="Hello!"/></properties></ant></actions></testcase>

JavaDSLdesigner

@CitrusTestpublicvoidantRunTest()variable("today","citrus:currentDate()");

antrun("classpath:com/consol/citrus/actions/build.xml").target("sayHello").property("date","$today").property("welcomeText","$Hello!");

JavaDSLrunner

@CitrusTestpublicvoidantRunTest()variable("today","citrus:currentDate()");

CitrusReferenceGuide

187Antrun

Page 188: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

antrun(action->action.buildFilePath("classpath:com/consol/citrus/actions/build.xml").target("sayHello").property("date","$today").property("welcomeText","$Hello!"));

Therespectivebuild.xmlAntfilemustprovidethetargettocall.Forexample:

<projectname="citrus-build"default="sayHello"><propertyname="welcomeText"value="WelcometoCitrus!"></property>

<targetname="sayHello"><echomessage="$welcomeText-Todayis$date"></echo></target>

<targetname="sayGoodbye"><echomessage="Goodbyeeverybody!"></echo></target></project>

AsyoucanseeyoucanpasscustombuildpropertiestotheAntbuildexecution.ExistingAntbuildpropertiesarereplacedandyoucanusethepropertiesinyourbuildfileasusual.

Youcanalsocallmultipletargetswithinonesinglebuildrunbyusingacommaseparatedlistoftargetnames:

XMLDSL

<testcasename="AntRunTest"><variables><variablename="today"value="citrus:currentDate()"/></variables><actions><antbuild-file="classpath:com/consol/citrus/actions/build.xml"><executetargets="sayHello,sayGoodbye"/><properties><propertyname="date"value="$today"/></properties></ant></actions></testcase>

JavaDSLdesigner

CitrusReferenceGuide

188Antrun

Page 189: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidantRunTest()variable("today","citrus:currentDate()");

antrun("classpath:com/consol/citrus/actions/build.xml").targets("sayHello","sayGoodbye").property("date","$today");

JavaDSLrunner

@CitrusTestpublicvoidantRunTest()variable("today","citrus:currentDate()");

antrun(action->action.buildFilePath("classpath:com/consol/citrus/actions/build.xml").targets("sayHello","sayGoodbye").property("date","$today"));

Thebuildpropertiescanliveinexternalfileresourceasanalternativetotheinlinepropertydefinitions.Youjusthavetousetherespectivefileresourcepathandallnestedpropertiesgetloadedasbuildproperties.

Inadditiontothatyoucanalsodefineacustombuildlistener.ThebuildlistenermustimplementtheAntAPIinterfaceorg.apache.tools.ant.BuildListener.DuringtheAntbuildrunthebuildlisteneriscalledwithseveralcallbackmethods(e.g.buildStarted(),buildFinished(),targetStarted(),targetFinished(),...).ThisishowyoucanaddadditionallogictotheAntbuildrunfromCitrus.Acustombuildlistenercouldmanagethefailstateofyourtestcase,inparticularbyraisingsomeexceptionforcingthetestcasetofailaccordingly.

XMLDSL

<testcasename="AntRunTest"><actions><antbuild-file="classpath:com/consol/citrus/actions/build.xml"build-listener="customBuildListener"><executetarget="sayHello"/><propertiesfile="classpath:com/consol/citrus/actions/build.properties"/></ant></actions></testcase>

CitrusReferenceGuide

189Antrun

Page 190: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSLdesigner

@AutowiredprivateBuildListenercustomBuildListener;

@CitrusTestpublicvoidantRunTest()antrun("classpath:com/consol/citrus/actions/build.xml").target("sayHello").propertyFile("classpath:com/consol/citrus/actions/build.properties").listener(customBuildListener);

JavaDSLrunner

@AutowiredprivateBuildListenercustomBuildListener;

@CitrusTestpublicvoidantRunTest()antrun(action->action.buildFilePath("classpath:com/consol/citrus/actions/build.xml").target("sayHello").propertyFile("classpath:com/consol/citrus/actions/build.properties").listener(customBuildListener));

ThecustomBuildListenerusedintheexampleaboveshouldreferenceaSpringbeanintheCitrusapplicationcontext.Thebeanimplementstheinterfaceorg.apache.tools.ant.BuildListenerandcontrolstheAntbuildrun.

CitrusReferenceGuide

190Antrun

Page 191: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Start/Stopserverinstances

Citrusisworkingwithservercomponentsthatarestartedandstoppedwithinatestrun.ThiscanbeaHttpserverorsomeSMTPmailserverforinstance.UsuallytheCitrusservercomponentsareautomaticallystartedwhenCitrusisstartingandrespectivelystoppedwhenCitrusisshuttingdown.Sometimesitmightbehelpfultoexplicitlystartandstopaserverinstancewithinyourtestcase.Hereyoucanusespecialstartandstoptestactionsinsideyourtest.Thisisagoodwaytotestdowntimescenariosofinterfacepartnerswithrespectiveerrorhandlingwhenconnectionstoserversarelost

Letmeexplainwithasimplesampletestcase:

XMLDSL

<testcasename="sleepTest"><actions><startserver="myMailServer"/>

<sleep/>

<stopserver="myMailServer"/></actions></testcase>

ThestartandstopservertestactionreceiveaservernamewhichreferencesaSpringbeancomponentoftypecom.consol.citrus.server.ServerinyourbasicSpringapplicationcontext.Theserverinstanceisstartedorstoppedwithinthetestcase.Asyoucanseeinthenextlistingwecanalsostartandstopmultipleserverinstanceswithinasingletestaction.

<testcasename="sleepTest"><actions><start><servers><servername="myMailServer"/><servername="myFtpServer"/></servers></start>

<sleep/>

<stop><servers><servername="myMailServer"/>

CitrusReferenceGuide

191Manageserver

Page 192: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<servername="myFtpServer"/></servers></stop></actions></testcase>

WhenusingtheJavaDSLthebestwaytoreferenceaserverinstanceistoautowiretheSpringbeanviadependencyinjection.TheSpringframeworktakescaseoninjectingtheproperSpringbeancomponentdefinedintheSPringapplicationcontext.ThiswayyoucaneasilystartandstopserverinstanceswithinJavaDSLtestcases.

JavaDSLdesignerandrunner

@Autowired@Qualifier("myFtpServer")privateFtpServermyFtpServer;

@CitrusTestpublicvoidstartStopServerTest()start(myFtpServer);

sleep();

stop(myFtpServer);

NoteStartingandstoppingserverinstancesisasynchronoustestaction.Thismeansthatyourtestcaseiswaitingfortheservertostartbeforeothertestactionstakeplace.Startuptimesandshutdownofserverinstancesmaydelayyourtestaccordingly.

AsyoucanseestartingandstoppingCitrusserverinstancesisveryeasy.Youcanalsowriteyourownserverimplementationsbyimplementingtheinterfacecom.consol.citrus.server.Server.Allcustomserverimplementationscanthenbestartedandstoppedduringatestcase.

CitrusReferenceGuide

192Manageserver

Page 193: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

StopTimer

Theactioncanbeusedforstoppingeitheraspecifictimer(containers-timer)oralltimersrunningwithinatest.Thisactionisusefulwhentimersarestartedinthebackground(usingparallelorfork=true)andyouwishtostopthesetimersattheendofthetest.Someexamplesofusingthisactionareprovidedbelow:

XMLDSL

<testcasename="timerTest"><actions><timerid="forkedTimer"fork="true"><sleepmilliseconds="50"/></timer>

<timerfork="true"><sleepmilliseconds="50"/></timer>

<timerrepeatCount="5"><sleepmilliseconds="50"/></timer>

<stop-timertimerId="forkedTimer"/></actions><finally><stop-timer/></finally></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidtimerTest()

timer().timerId("forkedTimer").fork(true).actions(sleep(50L));

timer().fork(true).actions(sleep(50L));

CitrusReferenceGuide

193Stoptimer

Page 194: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

timer().repeatCount(5).actions(sleep(50L));

stopTimer("forkedTimer")

doFinally().actions(stopTimer());

Intheaboveexample3timersarestarted,thefirst2inthebackgroundandthethirdinthetestexecutionthread.Timer#3hasarepeatCountsetto5soitwillterminateautomaticallyafter5runs.Timer#1and#2howeverhavenorepeatCountsetsotheywillexecuteuntiltheyaretoldtostop.

Timer#1isstoppedexplicitlyusingthefirststopTimeraction.HerethestopTimeractionincludesthenameofthetimertostop.Thisisconvenientwhenyouwishtoterminateaspecifictimer.HoweversincenotimerIdwassetfortimer#2,youcanterminatethis(andallothertimers)usingthe'stopTimer'actionwithnoexplicittimerIdset.

CitrusReferenceGuide

194Stoptimer

Page 195: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Includingcustomtestactions

Nowwehavealookattheopportunitytoaddcustomtestactionstothetestcaseflow.Letusstartthissectionwithanexample:

XMLDSL

<testcasename="ActionReferenceTest"><actions><actionreference="cleanUpDatabase"/><actionreference="mySpecialAction"/></actions></testcase>

ThegenericelementreferencesSpringbeansthatimplementtheJavainterfacecom.consol.citrus.TestAction.ThisisaveryfastwaytoaddyourownactionimplementationstoaCitrustestcase.ThiswayyoucaneasilyimplementyourownactionsinJavaandincludethemintothetestcase.

Intheexampleabovethecalledactionsarespecialdatabasecleanupimplementations.TheactionsaredefinedasSpringbeansintheCitrusconfigurationandgetreferencedbytheirbeannameorid.

<beanid="cleanUpDatabase"class="my.domain.citrus.actions.SpecialDatabaseCleanupAction"><propertyname="dataSource"ref="testDataSource"/></bean>

TheSpringapplicationcontextholdsyourcustombeanimplementations.YoucansetpropertiesandusethefullSpringpowerwhileimplementingyourcustomtestactioninJava.LetushavealookonhowsuchaJavaclassmaylooklike.

importcom.consol.citrus.actions.AbstractTestAction;importcom.consol.citrus.context.TestContext;

publicclassSpecialDatabaseCleanupActionextendsAbstractTestAction

@AutowiredprivateDataSourcedataSource;

@OverridepublicvoiddoExecute(TestContextcontext)JdbcTemplatejdbcTemplate=newJdbcTemplate(dataSource);

CitrusReferenceGuide

195Genericaction

Page 196: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

jdbcTemplate.execute("...");

AllyouneedtodoinyourJavaclassistoimplementtheCitruscom.consol.citrus.TestActioninterface.Theabstractclasscom.consol.citrus.actions.AbstractTestActionmayhelpyoutostartwithyourcustomtestactionimplementationasitprovidesbasicmethodimplementationssoyoujusthavetoimplementthedoExecute()method.

WhenusingtheJavatestcaseDSLyouarealsoquitecomfortablewithincludingyourcustomtestactions.

JavaDSLdesignerandrunner

@AutowiredprivateSpecialDatabaseCleanupActioncleanUpDatabaseAction;

@CitrusTestpublicvoidgenericActionTest()echo("Nowlet'sincludeourspecialtestaction");

action(cleanUpDatabaseAction);

echo("That'sit!");

Usinganonymousclassimplementationsisalsopossible.

JavaDSLdesignerandrunner

@CitrusTestpublicvoidgenericActionTest()echo("Nowlet'scallourspecialtestactionanonymously");

action(newAbstractTestAction()publicvoiddoExecute(TestContextcontext)//dosomething);

echo("That'sit!");

CitrusReferenceGuide

196Genericaction

Page 197: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

197Genericaction

Page 198: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TemplatesTemplatesgroupactionsequencestoalogicalunit.Youcanthinkoftemplatesasreusablecomponentsthatareusedinseveraltests.Themaintenanceismuchmoreeffectivebecausethetemplatesarereferencedseveraltimes.

Thetemplatealwayshasauniquename.Insideatestcasewecallthetemplatebythisuniquename.Havealookatafirstexample:

<templatename="doCreateVariables"><create-variables><variablename="var"value="123456789"/></create-variables>

<call-templatename="doTraceVariables"/></template>

<templatename="doTraceVariables"><echo><message>Currenttimeis:$time</message></echo>

<trace-variables/></template>

Thecodeexampleabovedescribestwotemplatedefinitions.Templatesholdasequenceoftestactionsorcallothertemplatesthemselvesasseenintheexampleabove.

NoteTheactioncallsothertemplatesbytheirname.ThecalledtemplatenotnecessarilyhastobelocatedinthesametestcaseXMLfile.ThetemplatemightbedefinedinaseparateXMLfileotherthanthetestcaseitself:

XMLDSL

<testcasename="templateTest"><variables><variablename="myTime"value="citrus:currentDate()"/></variables><actions><call-templatename="doCreateVariables"/>

<call-templatename="doTraceVariables"><parametername="time"value="$myTime">

CitrusReferenceGuide

198Templates

Page 199: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</call-template></actions></testcase>

JavaDSLdesigner

@CitrusTestpublicvoidtemplateTest()variable("myTime","citrus:currentDate()");

applyTemplate("doCreateVariables");

applyTemplate("doTraceVariables").parameter("time","$myTime");

JavaDSLrunner

@CitrusTestpublicvoidtemplateTest()variable("myTime","citrus:currentDate()");

applyTemplate(template->template.name("doCreateVariables"));

applyTemplate(template->template.name("doTraceVariables").parameter("time","$myTime"));

Thereisanopenquestionwhendealingwithtemplatesthataredefinedsomewhereelseoutsidethetestcase.Howtohandlevariables?Atemplatesmayusedifferentvariablenamesthenthetestandviceversa.Nodoubtthetemplatewillfailassoonasspecialvariableswithrespectivevaluesarenotpresent.Unknownvariablescausethetemplateandthewholetesttofailwitherrors.

Soafirstapproachwouldbetoharmonizevariableusageacrosstemplatesandtestcases,sothattemplatesandtestcasesdousethesamevariablenaming.Butthisapproachmightleadtohighcalibrationeffort.Thereforetemplatessupportparameterstosolvethisproblem.Whenatemplateiscalledthecallingactorisabletosetsomeparameters.Letusdiscussanexampleforthisissue.

Thetemplate"doDateCoversion"inthenextsampleusesthevariable$date.Thecallingtestcasecansetthisvariableasaparameterwithoutactuallydeclaringthevariableinthetestitself:

CitrusReferenceGuide

199Templates

Page 200: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<call-templatename="doDateCoversion"><parametername="date"value="$sampleDate"></call-template>

ThevariablesampleDateisalreadypresentinthetestcaseandgetstranslatedintothedateparameter.Followingfromthatthetemplateworksfinealthoughtestandtemplatedoworkondifferentvariablenamings.

Withtemplateparametersyouareabletosolvethecalibrationeffortwhenworkingwithtemplatesandvariables.Itisalwaysagoodideatochecktheusedvariables/parametersinsideatemplatewhencallingit.Theremightbeavariablethatisnotdeclaredyetinsideyourtest.Soyouneedtodefinethisvalueasaparameter.

TemplateparametersmaycontainmorecomplexvalueslikeXMLfragments.Thecall-templateactionoffersfollowingCDATAvariationfordefiningcomplexparametervalues:

<call-templatename="printXMLPayload"><parametername="payload"><value><![CDATA[<HelloRequestxmlns="http://www.consol.de/schemas/samples/sayHello.xsd"><Text>HelloSouth$var</Text></HelloRequest>]]></value></parameter></call-template>

ImportantWhenatemplateworksonvariablevaluesandparameterschangestothesevariableswillautomaticallyaffectthevariablesinthewholetest.Soifyouchangeavariable'svalueinsideatemplateandthevariableisdefinedinsidethetestcasethechangeswillaffectthevariableinaglobalcontext.Wehavetobecarefulwiththiswhenexecutingatemplateseveraltimesinatest,especiallyincombinationwithparallelcontainers(seecontainers-parallel).

<parallel><call-templatename="print"><parametername="param1"value="1"/><parametername="param2"value="HelloEurope"/></call-template><call-templatename="print"><parametername="param1"value="2"/><parametername="param2"value="HelloAsia"/></call-template>

CitrusReferenceGuide

200Templates

Page 201: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<call-templatename="print"><parametername="param1"value="3"/><parametername="param2"value="HelloAfrica"/></call-template></parallel>

Inthelistingaboveatemplateprintiscalledseveraltimesinaparallelcontainer.Theparametervalueswillbehandledinaglobalcontext,soitisquitelikelytohappenthatthetemplateinstancesinfluenceeachotherduringexecution.Wemightgetsuchprintmessages:

2.HelloEurope2.HelloAfrica3.HelloAfrica

Indexparametersdonotfitandthemessage'HelloAsia'iscompletelygone.Thisisbecausetemplatesoverwriteparameterstoeachotherastheyareexecutedinparallelatthesametime.Toavoidthisbehaviorweneedtotellthetemplatethatitshouldhandleparametersaswellasvariablesinalocalcontext.Thiswillenforcethateachtemplateinstanceisworkingonadedicatedlocalcontext.Seetheglobal-contextattributethatissettofalseinthisexample:

<templatename="print"global-context="false"><echo><message>$param1.$param2</message></echo></template>

Afterthattemplateinstanceswon'tinfluenceeachotheranymore.Butnoticethatvariablechangesinsidethetemplatethendonotaffectthetestcaseneither.

CitrusReferenceGuide

201Templates

Page 202: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestbehaviorsTestbehaviorscombineactionsequencestoalogicalunit.ThebehaviordefinesasetoftestactionsthatcanbeappliedtoaJavaDSLtestcase.Followingfromthatyoucansaythatbehaviorsarereusabletestactiontemplates.Themaintenanceismuchmoreeffectivewhenyoureusebasictestactionsinmanytestcases.

ThebehaviorisaseparateJavaDSLclasswithasingleapplymethodthatconfiguresthetestactions.Havealookatthisfirstexample:

JavaDSL

publicclassFooBehaviorextendsAbstractTestBehaviorpublicvoidapply()variable("foo","test");

echo("fooBehavior");

publicclassBarBehaviorextendsAbstractTestBehaviorpublicvoidapply()variable("bar","test");

echo("barBehavior");

AsyoucanseethebehaviorclassisabletousetheCitrusJavaDSLasusual.Eachbehaviorisabletodefinetestvariablesandactions.Inatestcaseyoucanapplythebehaviorsasfollows:

JavaDSL

@CitrusTestpublicvoidbehaviorTest()variable("myTime","citrus:currentDate()");

FooBehaviorfooBehavior=newFooBehavior();applyBehavior(fooBehavior);

applyBehavior(newBarBehavior());

applyBehavior(fooBehavior);

CitrusReferenceGuide

202Testbehaviors

Page 203: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Whendealingwithbehaviorstestactionsaredefinedsomewhereoutsidethetestcase.Howdowehandletestvariables?Abehaviormayusedifferentvariablenamesthenthetestandviceversa.Nodoubtthebehaviorwillfailassoonasspecialvariableswithrespectivevaluesarenotpresent.Unknownvariablescausethebehaviorandthewholetesttofailwitherrors.

Soagoodapproachwouldbetoharmonizevariableusageacrossbehaviorsandtestcases,sothattemplatesandtestcasesdousethesamevariablenaming.Thebehaviorautomaticallyknowsallvariablesinthetestcase.Andalltestvariablescreatedinsidethebehaviorarevisibletothetestcaseafterapplying.

ImportantWhenabehaviorchangesvariablesthiswillautomaticallyaffectthevariablesinthewholetest.Soifyouchangeavariable'svalueinsideabehaviorandthevariableisdefinedinsidethetestcasethechangeswillaffectthevariableinaglobaltestcontext.Thismeanswehavetobecarefulwhenexecutingabehaviorseveraltimesinatest,especiallyincombinationwithparallelcontainers(seecontainers-parallel).

Behaviortypes

ThetestcaseinJavaisabletofolloweitherdesignerorrunnerstrategies.Thismeanswealsohavetwodifferentbehaviortypesfordesignerandrunnerrespectively.Thebehaviorsarelocatedinseparatepackages

com.consol.citrus.dsl.design.AbstractTestBehaviorcom.consol.citrus.dsl.runner.AbstractTestBehavior

Decidewhichbasebehavioryouwanttoextendfromaccordingtoyourtestcasenature.

CitrusReferenceGuide

203Testbehaviors

Page 204: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ContainersSimilartotemplatesacontainerelementholdsonetomanytestactions.Incontrasttothetemplatethecontainerappearsdirectlyinsidethetestcaseactionchain,meaningthatthecontainerisnotreferencedbymorethanonetestcase.

Containersexecutetheembeddedtestactionsinspecificlogic.Thiscanbeanexecutioniniterationforinstance.Combinedifferentcontainerswitheachotherandyouwillbeabletogenerateverypowerfulhierarchicalstructuresinordertocreateacomplexexecutionlogic.Inthefollowingsectionssomepredefinedcontainersaredescribed.

CitrusReferenceGuide

204Containers

Page 205: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Sequential

Thesequentialcontainerexecutestheembeddedtestactionsinstrictsequence.Readersnowmightsearchforthedifferencetothenormalactionchainthatisspecifiedinsidethetestcase.Theactualpowerofsequentialcontainersdoesshowonlyincombinationwithothercontainerslikeiterationsandparallels.Wewillseethislaterwhenhandlingthesecontainers.

Fornowthesequentialcontainerseemsnotverysensational-onemightsayboring-becauseitsimplygroupsapairoftestactionstosequentialexecution.

XMLDSL

<testcasename="sequentialTest"><actions><sequential><trace-time/><sleep/><echo><message>HalloTestFramework</message></echo><trace-time/></sequential></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidsequentialTest()sequential().actions(stopTime(),sleep(1.0),echo("HelloCitrus"),stopTime());

CitrusReferenceGuide

205Sequential

Page 206: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Conditional

Nowwedealwithconditionalexecutionsoftestactions.Nestedactionsinsideaconditionalcontainerareexecutedonlyincaseabooleandexpressionevaluatestotrue.Otherwisethecontainerexecutionisnotperformedatall.

Seesomeexampletofindouthowitworkswiththeconditionalexpressionstring.

XMLDSL

<testcasename="conditionalTest"><variables><variablename="index"value="5"/><variablename="shouldSleep"value="true"/></variables>

<actions><conditionalexpression="$index=5"><sleepseconds="10"/></conditional>

<conditionalexpression="$shouldSleep"><sleepseconds="10"/></conditional>

<conditionalexpression="@assertThat('$shouldSleep','anyOf(is(true),isEmptyString())')@"<sleepseconds="10"/></conditional></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidconditionalTest()variable("index",5);variable("shouldSleep",true);

conditional().when("$index=5")).actions(sleep(10000L));

conditional().when("$shouldSleep")).actions(sleep(10000L)

CitrusReferenceGuide

206Conditional

Page 207: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

);

conditional().when("$shouldSleep",anyOf(is("true"),isEmptyString())).actions(sleep(10000L));

Thenestedsleepactionisexecutedincasethevariable$indexisequaltothevalue'5'.Thisconditionalexecutionoftestactionsisusefulwhendealingwithdifferenttestenvironmentssuchasdifferentoperatingsystemsforinstance.Theconditionalcontaineralsosupportsexpressionsthatevaluatetothecharactersequence"true"or"false"asshowninthe$shouldSleepexample.

ThelastconditionalcontainerintheexampleabovemakesuseofHamcrestmatchers.Thematcherevaluatestotrueoffalseandbasedonthatthecontaineractionsareexecutedorskipped.TheHamcrestmatchersareverypowerfulwhenitcomestoevaluationofmultipleconditionsatatime.

CitrusReferenceGuide

207Conditional

Page 208: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Parallel

Parallelcontainersexecutetheembeddedtestactionsconcurrenttoeachother.EveryactioninthiscontainerwillbeexecutedinaseparateJavaThread.Followingexampleshouldclarifytheusage:

XMLDSL

<testcasename="parallelTest"><actions><parallel><sleep/>

<sequential><sleep/><echo><message>1</message></echo></sequential>

<echo><message>2</message></echo>

<echo><message>3</message></echo>

<iteratecondition="ilt=5"index="i"><echo><message>10</message></echo></iterate></parallel></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidparalletTest()parallel().actions(sleep(),sequential().actions(sleep(),echo("1")

CitrusReferenceGuide

208Parallel

Page 209: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

),echo("2"),echo("3"),iterate().condition("ilt=5").index("i")).actions(echo("10")));

Sothenormaltestactionprocessingwouldbetoexecuteoneactionafteranother.Asthefirstactionisasleepoffiveseconds,thewholetestprocessingwouldstopandwaitfor5seconds.Thingsaredifferentinsidetheparallelcontainer.Herethedescendingtestactionswillnotwaitbutexecuteatthesametime.

NoteNotethatcontainerscaneasilywrapothercontainers.Theexampleshowsasimplecombinationofsequentialandparallelcontainersthatwillarchiveacomplexexecutionlogic.Actionsinsidethesequentialcontainerwillexecuteoneafteranother.Butactionsinparallelwillbeexecutedatthesametime.

CitrusReferenceGuide

209Parallel

Page 210: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Iterate

Iterationsareverypowerfulelementswhendescribingcomplexlogic.Thecontainerexecutestheembeddedactionsseveraltimes.Thecontainerwillcontinuewithloopingaslongasthedefinedbreakingconditionstringevaluatestotrue.Incasetheconditionevaluatestofalsetheiterationwillbreakanfinishexecution.

XMLDSL

<testcasename="iterateTest"><actions><iterateindex="i"condition="ilt5"><echo><message>indexis:$i</message></echo></iterate></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoiditerateTest()iterate().condition("ilt5").index("i")).actions(echo("indexis:$i"));

Theattribute"index"automaticallydefinesanewvariablethatholdstheactualloopindexstartingat"1".Thisindexvariableisavailableasanormalvariableinsidetheiteratecontainer.Thereforeitispossibletoprintouttheactualloopindexintheechoactionasshownintheaboveexample.

Theconditionstringismandatoryanddescribestheactualendoftheloop.Initeratecontainerstheloopwillbreakincasetheconditionevaluatestofalse.

TheconditionstringcanbeanyBooleanexpressionandsupportsseveraloperators:

lt(lowerthan)

lt=(lowerthanequals)

gt(greaterthan)

CitrusReferenceGuide

210Iterate

Page 211: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

gt=(greaterthanequals)

=(equals)

and(logicalcombiningoftwoBooleanvalues)

or(logicalcombiningoftwoBooleanvalues)

()(brackets)

ImportantItisveryimportanttonoticethattheconditionisevaluatedbeforetheveryfirstiterationtakesplace.Theloopthereforecanbeexecuted0-ntimesaccordingtotheconditionvalue.

Nowthebooleanexpressionevaluationasdescribedaboveislimitedtoverybasicoperationsuchaslowerthan,greaterthanandsoon.WealsocanuseHamcrestmatchersinconditionsthatarewaymorepowerfulthanthat.

XMLDSL

<testcasename="iterateTest"><actions><iterateindex="i"condition="@assertThat(lessThan(5))@"><echo><message>indexis:$i</message></echo></iterate></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoiditerateTest()iterate().condition(lessThan(5)).index("i")).actions(echo("indexis:$i"));

IntheexampleaboveweuseHamcrestmatchersascondition.YoucancombineHamcrestmatchersandcreateverypowerfulconditionevaluationshere.

CitrusReferenceGuide

211Iterate

Page 212: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

212Iterate

Page 213: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Repeatuntiltrue

Quitesimilartothepreviouslydescribediteratecontainerthisrepeatingcontainerwillexecuteitsactionsinaloopaccordingtoanendingcondition.TheconditiondescribesaBooleanexpressionusingtheoperatorsasdescribedinthepreviouschapter.

NoteTheloopcontinuesitsworkuntiltheprovidedconditionevaluatestotrue.Itisveryimportanttonoticethattherepeatloopwillexecutetheactionsbeforeevaluatingthecondition.Thismeanstheactionsgetexecuted1-ntimes.

XMLDSL

<testcasename="iterateTest"><actions><repeat-until-trueindex="i"condition="(i=3)or(i=5)"><echo><message>indexis:$i</message></echo></repeat-until-true></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidrepeatTest()repeat().until("(igt5)or(i=3)").index("i")).actions(echo("indexis:$i"));

Asyoucanseetherepeatcontainerisonlyexecutedwhentheiteratingconditionexpressionevaluatestofalse.Bythetimetheconditionistrueexecutionisdiscontinued.Youcanusebasiclogicaloperatorssuchasand,orandsoon.

AmorepowerfulwayisgivenbyHamcrestmatchersthataredirectlysupportedinconditionexpressions.

XMLDSL

<testcasename="iterateTest"><actions>

CitrusReferenceGuide

213Repeat

Page 214: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<repeat-until-trueindex="i"condition="@assertThat(anyOf(is(3),is(5))@"><echo><message>indexis:$i</message></echo></repeat-until-true></actions></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidrepeatTest()repeat().until(anyOf(is(3),is(5)).index("i")).actions(echo("indexis:$i"));

TheHamcrestmatcherusagesimplifiesthereadingalot.Anditempowersyoutocombinemorecomplexconditionexpressions.SoIpersonallypreferthissyntax.

CitrusReferenceGuide

214Repeat

Page 215: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Repeatonerroruntiltrue

Thenextloopingcontaineriscalledrepeat-on-error-until-true.Thiscontainerrepeatsagroupofactionsincaseoneembeddedactionfailedwitherror.Incaseofanerrorinsidethecontainertheloopwilltrytoexecuteallembeddedactionsagaininordertoseekforoverallsuccess.Theexecutioncontinuesuntilallembeddedactionswereprocessedsuccessfullyortheendingconditionevaluatestotrueandtheerror-loopwillleadtofinalfailure.

XMLDSL

<testcasename="iterateTest"><actions><repeat-onerror-until-trueindex="i"condition="i=5"><echo><message>indexis:$i</message></echo><fail/></repeat-onerror-until-true></actions></testcase>

JavaDSLdesigner

@CitrusTestpublicvoidrepeatOnErrorTest()repeatOnError(echo("indexis:$i"),fail("Forcelooptofail!")).until("i=5").index("i");

JavaDSLrunner

@CitrusTestpublicvoidrepeatOnErrorTest()repeatOnError().until("i=5").index("i")).actions(echo("indexis:$i"),fail("Forcelooptofail!"));

CitrusReferenceGuide

215RepeatOnError

Page 216: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Inthecodeexampletheerror-loopcontinuesfourtimesastheactiondefinitelyfailsthetest.DuringthefifthiterationThecondition"i=5"evaluatestotrueandtheloopbreaksitsprocessingleadingtoafinalfailureasthetestactionswerenotsuccessful.

NoteTheoverallsuccessofthetestcasedependsontheerrorsituationinsidetherepeat-onerror-until-truecontainer.Incasetheloopbreaksbecauseoffailingactionsandtheloopwilldiscontinueitsworkthewholetestcaseisfailingtoo.Theerrorloopprocessingissuccessfulincaseallembeddedactionswerenotraisinganyerrorsduringaniteration.

Therepeat-on-errorcontaineralsooffersanautomaticsleepmechanism.Thisauto-sleeppropertywillforcethecontainertowaitagivenamountoftimebeforeexecutingthenextiteration.Weusedthismechanismalotwhenvalidatingdatabaseentries.Let'ssaywewanttochecktheexistenceofanorderentryinthedatabase.Unfortunatelythesystemundertestisnotverywellperformingandmayneedsometimetostoretheneworder.Thisamountoftimeisnotpredictable,especiallywhendealingwithdifferenthardwareonourtestenvironments(localtestingvs.servertesting).Followingfromthatourtestcasemayfailunpredictableonlybecauseofruntimeconditions.

Wecanavoidunstabletestcasesthatarebasedontheseruntimeconditionswiththeauto-sleepfunctionality.

XMLDSL

<repeat-onerror-until-trueauto-sleep="1000"condition="i=5"index="i"><echo><sqldatasource="testDataSource"><statement>SELECTCOUNT(1)ASCNT_ORDERSFROMORDERSWHERECUSTOMER_ID='$customerId'</statement><validatecolumn="CNT_ORDERS"value="1"/></sql></echo></repeat-onerror-until-true>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidrepeatOnErrorTest()repeatOnError().until("i=5").index("i").autoSleep(1000)).actions(query(action->action.dataSource(testDataSource)

CitrusReferenceGuide

216RepeatOnError

Page 217: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.statement("SELECTCOUNT(1)ASCNT_ORDERSFROMORDERSWHERECUSTOMER_ID='$customerId'".validate("CNT_ORDERS","1")));

Wesurroundedthedatabasecheckwitharepeat-onerrorcontainerhavingtheauto-sleeppropertysetto1000milliseconds.Therepeatcontainerwilltrytocheckthedatabaseuptofivetimeswithanautomaticsleepof1secondbeforeeveryiteration.Thisgivesthesystemundertestuptofivesecondstimetostorethenewentrytothedatabase.Thetestcaseisverystableandjustfitstothehardwareenvironment.Onslowtestenvironmentsthetestmayneedseveraliterationstosuccessfullyreadthedatabaseentry.Onveryfastenvironmentsthetestmaysucceedrightonthefirsttry.

ImportantWechangedautosleeptimefromsecondstomillisecondswithCitrus2.0release.SoifyouarecomingfrompreviousCitrusversionsbesuretonowusepropermillisecondvalues.

Sofastenvironmentsarenotsloweddownbystaticsleepoperationsandslowerenvironmentsarestillabletoexecutethistestcasewithhighstability.

CitrusReferenceGuide

217RepeatOnError

Page 218: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Timer

Timersareveryusefulcontainerswhenyouwishtoexecuteacollectionoftestactionsseveraltimesatregularintervals.Thetimercomponentgeneratesaneventwhichinturntriggerstheexecutionofthenestedtestactionsassociatedwithtimer.ThiscanbeusefulinanumberoftestscenariosforexamplewhenCitrusneedstosimulateaheartbeatorifyouaredebuggingatestandyouwisttoquerythecontentsofthedatabase,tomentionjustafew.Thefollowingcodesampleshoulddemonstratethepowerandflexibilityoftimers:

XMLDSL

<testcasename="timerTest"><actions><timerid="forkedTimer"interval="100"fork="true"><echo><message>I'mgoingtoruninthebackgroundandletsomeothertestactionsrun(nestedactionrun$forkedTimer-indextimes)</echo><sleepmilliseconds="50"/></timer>

<timerrepeatCount="3"interval="100"delay="50"><sleepmilliseconds="50"/><echo><message>I'mgoingtorepeatthismessage3timesbeforethenexttestactionsareexecuted</echo></timer>

<echo><message>Testalmostcomplete.Makesurealltimersrunninginthebackgroundarestopped</echo></actions><finally><stop-timertimerId="forkedTimer"/></finally></testcase>

JavaDSLdesignerandrunner

@CitrusTestpublicvoidtimerTest()

timer().timerId("forkedTimer")

CitrusReferenceGuide

218Timer

Page 219: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.interval(100L).fork(true).actions(echo("I'mgoingtoruninthebackgroundandletsomeothertestactionsrun(nestedactionrun$forkedTimer-indextimes)"sleep(50L));

timer().repeatCount(3).interval(100L).delay(50L).actions(sleep(50L),echo("I'mgoingtorepeatthismessage3timesbeforethenexttestactionsareexecuted");

echo("Testalmostcomplete.Makesurealltimersrunninginthebackgroundarestopped");

doFinally().actions(stopTimer("forkedTimer"));

Intheaboveexamplethefirsttimer(timerId=forkedTimer)isstartedinthebackground.Bydefaulttimersareruninthecurrentthreadofexecutionbuttostartitinthebackgroundjustuse"fork=true".Every100millisecondsthistimeremitsaneventwhichwillresultinthenestedactionsbeingexecuted.Thenested'echo'actionoutputsthenumberoftimesthistimerhasalreadybeenexecuted.Itdoesthiswiththehelpofan'index'variable,inthisexample$forkedTimer-index,whichisnamedaccordingtothetimeridwiththesuffix'-index'.Nolimitissetonthenumberoftimesthistimershouldrunsoitwillkeeponrunninguntileitheranestedtestactionfailsoritisinstructedtostop(moreonthisbelow).

Thesecondtimerisconfiguredtorun3timeswithadelayof100millisecondsbetweeneachiteration.Usingtheattribute'delay'wecangetthetimerpausefor50millisecondsbeforerunningthenestedactionsforthefirsttime.Thetimerisconfiguredtoruninthecurrentthreadofexecutionsothelasttestaction,the'echo',hastowaitforthistimertocompletebeforeitisexecuted.

Sohowdowetelltheforkedtimertostoprunning?Ifweforgettodothisthetimerwilljustexecuteindefinitely.Tohelpusoutherewecanusethe'stop-timer'action.Byaddingthistothefinallyblockweensurethatthetimerwillbestopped,evenifsome

CitrusReferenceGuide

219Timer

Page 220: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

nestedtestactionfails.Wecouldhaveeasilyaddeditasanestedtestaction,totheforkedTimerforexample,butifsomeothertestactionfailedbeforethestop-timerwascalled,thetimerwouldneverstop.

NoteYoucanalsoconfiguretimerstoruninthebackgroundusingthe'parallel'container,ratherthansettingtheattribute'fork'totrue.Usingparallelallowsmorefine-grainedcontrolofthetestandhastheaddedadvantagethatallerrorsgeneratedfromanestertimeractionarevisibletothetestexecuter.Ifanerroroccurswithinthetimerthentheteststatusissettofailed.Usingfork=trueanerrorcausesthetimertostopexecuting,buttheteststatusisnotinfluencedbythiserror.

CitrusReferenceGuide

220Timer

Page 221: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Customcontainers

IncaseyouhaveacustomactioncontainerimplementationyoumightalsowanttouseitinJavaDSL.TheactioncontainersarehandledwithspecialcareintheJavaDSLbecausetheyhavenestedactions.SowhenyoucallatestactioncontainerintheJavaDSLyoualwayshavesomethinglikethis:

JavaDSLdesignerandrunner

@CitrusTestpublicvoidcontainerTest()echo("Thisechoisoutsideoftheactioncontainer");

sequential().actions(echo("Inside"),echo("Insideoncemore"),echo("Andagain:Inside!"));

echo("Thisechoisoutsideoftheactioncontainer");

NowthethreenestedactionsareaddedtotheactionsequentialcontainerratherthantothetestcaseitselfalthoughweareusingthesameactionJavaDSLmethodsasoutsidethecontainer.ThismechanismisonlyworkingbecauseCitrusishandlingtestactioncontainerswithspecialcare.

Acustomtestactioncontainerimplementationcouldlooklikethis:

publicclassReverseActionContainerextendsAbstractActionContainer@OverridepublicvoiddoExecute(TestContextcontext)for(inti=getActions().size();i>0;i--)getActions().get(i-1).execute(context);

Thecontainerlogicisverysimple:Thecontainerexecutesthenestedactionsinreverseorder.AsalreadymentionedCitrusneedstotakespecialcareonallactioncontainerswhenexecutingaJavaDSLtest.Thisiswhyyoushouldnotexecuteacustomtestcontainerimplementationonyourown.

CitrusReferenceGuide

221Custom

Page 222: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidcontainerTest()ReverseActionContainerreverseContainer=newReverseActionContainer();reverseContainer.addTestAction(newEchoAction().setMessage("Foo"));reverseContainer.addTestAction(newEchoAction().setMessage("Bar"));run(reverseContainer);

TheabovecustomcontainerexecutionisgoingtofailwithinternalerrorastheCitrusJavaDSLwasnotabletorecognisetheactioncontainerasitshouldbe.AlsotheEchoActioninstancecreationisnotverycomfortable.InsteadyoucanuseaspecialcontainerJavaDSLsyntaxalsowithyourcustomcontainerimplementation:

@CitrusTestpublicvoidcontainerTest()container(newReverseActionContainer()).actions(echo("Foo"),echo("Bar"));

Thecustomcontainerimplementationnowworksfinewiththeautomaticallynestedechoactions.AndweareabletousetheusualJavaDSLsyntacticsugarfortestactionslikeecho.

Inanextstepweaddacustomsuperclassforallourtestclasseswhichprovidesahelpermethodforthecustomcontainerimplementationinordertohaveaevenmorecomfortablesyntax.

JavaDSLdesignerandrunner

publicclassCustomCitrusBaseTestextendsTestNGCitrusTestDesigner

publicAbstractTestContainerBuilder<ReverseActionContainer>reverse()returncontainer(newReverseActionContainer());

Nowallsubclassescanusethenewreversemethodforcallingthecustomcontainerimplementation.

@CitrusTestpublicvoidcontainerTest()

CitrusReferenceGuide

222Custom

Page 223: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

reverse().actions(echo("Foo"),echo("Bar"));

Nice!ThisishowweshouldintegratecustomizedtestactioncontainerstotheCitrusJavaDSL.

CitrusReferenceGuide

223Custom

Page 224: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FinallysectionThischapterdealswithaspecialsectioninsidethetestcasethatisexecutedevenincaseerrorsdidoccurduringthetest.LetssayyouhavestartedaJettywebserverinstanceatthebeginningofthetestcaseandyouneedtoshutdowntheserverwhenthetesthasfinisheditswork.Orasasecondexampleimaginethatyouhavepreparedsomedatainsidethedatabaseatthebeginningofyourtestandyouwanttomakesurethatthedataiscleanedupattheendofthetestcase.

Inbothsituationswemightrunintosomeproblemswhenthetestfailed.Wefacetheproblemthatthewholetestcasewillterminateimmediatelyincaseoferrors.Cleanuptasksattheendofthetestactionchainmaynotbeexecutedcorrectly.

Dirtystatesinsidethedatabaseorstillrunningserverinstancesthenmightcauseproblemsforfollowingtestcases.Toavoidthisproblemsyoushouldusethefinallyblockofthetestcase.Thesectioncontainsactionsthatareexecutedevenincasethetestfails.Usingthisstrategythedatabasecleaningtasksmentionedbeforewillfindexecutionineverycase(successorfailure).

Thefollowingexampleshowshowtousethefinallysectionattheendofatest:

XMLDSL

<testcasename="finallyTest"><variables><variablename="orderId"value="citrus:randomNumber(5)"/><variablename="date"value="citrus:currentDate('dd.MM.yyyy')"/></variables><actions><sqldatasource="testDataSource"><statement>INSERTINTOORDERSVALUES($orderId,1,1,'$date')</statement></sql>

<echo><message>ORDERcreationtime:$date</message></echo></actions><finally><sqldatasource="testDataSource"><statement>

CitrusReferenceGuide

224Finally

Page 225: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DELETEFROMORDERSWHEREORDER_ID='$orderId'</statement></sql></finally></testcase>

IntheexamplethefirstactioncreatesanentryinthedatabaseusinganINSERTstatement.Tobesurethattheentryinthedatabaseisdeletedafterthetest,thefinallysectioncontainstherespectiveDELETEstatementthatisalwaysexecutedregardlessthetestcasestate(successfulorfailed).

OfcourseyoucanalsousethefinallyblockintheJavatestcaseDSL.Findfollowingexampletoseehowitworks:

JavaDSLdesigner

@CitrusTestpublicvoidfinallySectionTest()variable("orderId","citrus:randomNumber(5)");variable("date","citrus:currentDate('dd.MM.yyyy')");

sql(dataSource).statement("INSERTINTOORDERSVALUES($orderId,1,1,'$date')");

echo("ORDERcreationtime:citrus:currentDate('dd.MM.yyyy')");

doFinally(sql(dataSource).statement("DELETEFROMORDERSWHEREORDER_ID='$orderId'"));

JavaDSLrunner

@CitrusTestpublicvoidfinallySectionTest()variable("orderId","citrus:randomNumber(5)");variable("date","citrus:currentDate('dd.MM.yyyy')");

sql(action->action.dataSource(dataSource).statement("INSERTINTOORDERSVALUES($orderId,1,1,'$date')"));

echo("ORDERcreationtime:citrus:currentDate('dd.MM.yyyy')");

doFinally().actions(sql(action->action.dataSource(dataSource).statement("DELETEFROMORDERSWHEREORDER_ID='$orderId'");

CitrusReferenceGuide

225Finally

Page 226: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NoteJavadevelopersmightaskwhynotusetry-finallyJavablockinstead?Theanswerissimpleyetveryimportanttounderstand.The@CitrusTestannotatedmethodiscalledatdesigntimeofthetestcase.Themethodbuildsthetestcaseafterwardsthetestisexecutedatruntime.Thismeansthatatry-finallyblockwithinthe@CitrusTestannotatedmethodwillneverperformduringthetestrunbutatdesigntimebeforethetestgetsexecuted.ThisiswhywehavetoaddthefinallysectionaspartofthetestcasewithdoFinally().

CitrusReferenceGuide

226Finally

Page 227: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JMSsupportCitrusprovidessupportforsendingandreceivingJMSmessages.Wehavetoseparatebetweensynchronousandasynchronouscommunication.SointhischapterweexplainhowtosetupJMSmessageendpointsforsynchronousandasynchronousoutboundandinboundcommunication

NoteTheJMScomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-jms</artifactId><version>2.7</version></dependency>

Citrusprovidesa"citrus-jms"configurationnamespaceandschemadefinitionforJMSrelatedcomponentsandfeatures.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusJMSconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-jms="http://www.citrusframework.org/schema/jms/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/jms/confighttp://www.citrusframework.org/schema/jms/config/citrus-jms-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

JMSendpoints

CitrusReferenceGuide

227Jms

Page 228: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

BydefaultCitrusJMSendpointsareasynchronous.Soletusfirstofalldealwithasynchronousmessagingwhichmeansthatwewillnotwaitforanyresponsemessageaftersendingorreceivingamessage.

ThetestcaseitselfshouldnotknowaboutJMStransportdetailslikequeuenamesorconnectioncredentials.ThisinformationisstoredintheendpointcomponentconfigurationthatlivesinthebasicSpringconfigurationfileinCitrus.SoletushavealookatasimpleJMSmessageendpointconfigurationinCitrus.

<citrus-jms:endpointid="helloServiceQueueEndpoint"destination-name="Citrus.HelloService.Request.Queue"timeout="10000"/>

TheendpointcomponentreceivesanuniqueidandaJMSdestinationname.Thiscanbeaqueueortopicdestination.WewilldealwithJMStopicslateron.FornowthetimeoutsettingcompletesourfirstJMSendpointcomponentdefinition.

TheendpointneedsaJMSconnectionfactoryforconnectingtoaJMSmessagebroker.TheconnectionfactoryisalsoaddedasSpringbeantotheCitrusSpringapplicationcontext.

<beanid="connectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory"><propertyname="brokerURL"value="tcp://localhost:61616"/></bean>

TheJMSconnectionfactoryreceivestheJMSmessagebrokerURLandisabletoholdmanyotherconnectionspecificoptions.InthisexampleweusetheApacheActiveMQconnectionfactoryimplementationaswewanttousetheActiveMQmessagebroker.CitrusworksbydefaultwithabeanidconnectionFactory.AllCitrusJMScomponentwillautomaticallyrecognizethisconnectionfactory.

TipSpringmakesitveryeasytoconnecttootherJMSbrokerimplementationstoo(e.g.ApacheActiveMQ,TIBCOEnterpriseMessagingService,IBMWebsphereMQ).JustaddtherequiredconnectionfactoryimplementationasconnectionFactorybean.

NoteAlloftheCitrusJMSendpointcomponentswillautomaticallylookforabeannamedconnectionFactorybydefault.Youcanusetheconnection-factoryendpointattributeinordertouseanotherconnectionfactoryinstancewithdifferentbeannames.

<citrus-jms:endpointid="helloServiceQueueEndpoint"destination-name="Citrus.HelloService.Request.Queue"

CitrusReferenceGuide

228Jms

Page 229: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

connection-factory="myConnectionFacotry"/>

AsanalternativetothatyoumaywanttouseaspecialSpringjmstemplateimplementationascustombeaninyourendpoint.

<citrus-jms:endpointid="helloServiceQueueEndpoint"destination-name="Citrus.HelloService.Request.Queue"jms-template="myJmsTemplate"/>

Theendpointisnowreadytobeusedinsideatestcase.Insideatestcaseyoucansendorreceivemessagesusingthisendpoint.ThetestactionscanreferencetheJMSendpointusingitsidentifier.WhensendingamessagethemessageendpointcreatesaJMSmessageproducerandwillsimplypublishthemessagetothedefinedJMSdestination.Asthecommunicationisasynchronousbydefaultproducerdoesnotwaitforasynchronousresponse.

WhenreceivingamessageswiththisendpointtheendpointcreatesaJMSconsumerontheJMSdestination.Theendpointthenactsasamessagedrivenlistener.Thismeansthatthemessageconsumerconnectstothegivendestinationandwaitsformessagestoarrive.

NoteBesidesthedestination-nameattributeyoucanalsoprovideareferencetoadestinationimplementation.

<citrus-jms:endpointid="helloServiceQueueEndpoint"destination="helloServiceQueue"/>

<amq:queueid="helloServiceQueue"physicalName="Citrus.HelloService.Request.Queue"/>

ThedestinationattributereferencestoaJMSdestinationobjectintheSpringapplicationcontext.IntheexampleaboveweusedtheActiveMQqueuedestinationcomponent.ThedestinationreferencecanalsorefertoaJNDIlookupforinstance.

JMSsynchronousendpoints

WhenusingsynchronousmessageendpointsCitruswillmanageareplydestinationforreceivingasynchronousresponsemessageonthereplydestination.Thefollowingfigureillustratesthatwenowhavetwodestinationsinourcommunicationscenario.

CitrusReferenceGuide

229Jms

Page 230: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thesynchronousmessageendpointcomponentissimilartotheasynchronousbrotherthatwehavediscussedbefore.Theonlydifferenceisthattheendpointwillautomaticallymanageareplydestinationbehindthescenes.BydefaultCitrususestemporaryreplydestinationsthatgetautomaticallydeletedafterthecommunicationhandshakeisdone.AgainweneedtouseaJMSconnectionfactoryintheSpringXMLconfigurationasthecomponentneedtoconnecttoaJMSmessagebroker.

<citrus-jms:sync-endpointid="helloServiceSyncEndpoint"destination-name="Citrus.HelloService.InOut.Queue"timeout="10000"/>

Thesynchronouscomponentdefinesatargetdestinationwhichagainiseitheraqueueortopicdestination.Ifnothingelseisdefinedtheendpointwillcreatetemporaryreplydestinationsonitsown.Whentheendpointhassentamessageitwaitssynchronouslyfortheresponsemessagetoarriveonthereplydestination.Youcanreceivethisreplymessageinyourtestcasebyreferencingthissameendoointinareceivetestaction.Incasenoreplymessagearrivesintimeamessagetimeouterrorisraisedrespectively.

Seethefollowingexampletestcasewhichreferencesthesynchronousmessageendpointinitssendandreceivetestactioninordertosendoutamessageandwaitforthesynchronousresponse.

<testcasename="synchronousMessagingTest"><actions><sendendpoint="helloServiceSyncEndpoint"><message><data>[...]</data></message></send>

<receiveendpoint="helloServiceSyncEndpoint"><message><data>[...]</data></message>

CitrusReferenceGuide

230Jms

Page 231: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</receive></actions></testcase>

Weinitiatedthesynchronouscommunicationbysendingamessageonthesynchronousendpoint.Thesecondstepthenreceivesthesynchronousmessageonthetemporaryreplydestinationthatwasautomaticallycreatedforus.

Ifyouratherwanttodefineastaticreplydestinationyoucandoso,too.Thestaticreplydestinationisnotdeletedaftercommunicationhandshake.Youmayneedtoworkwithmessageselectorstheninordertopicktherightresponsemessagethatbelongstoaspecificcommunicationhandshake.Youcandefineastaticreplydestinationonthesynchronousendpointcomponentasfollows.

<citrus-jms:sync-endpointid="helloServiceSyncEndpoint"destination-name="Citrus.HelloService.InOut.Queue"reply-destination-name="Citrus.HelloService.Reply.Queue"timeout="10000"/>

Insteadofusingthereply-destination-namefeelfreetousethedestinationreferencewithreply-destinationattribute.AgainyoucanuseaJNDIlookupthentoreferenceadestinationobject.

ImportantBeawareofpermissionsthataremandatoryforcreatingtemporarydestinations.CitrustriestocreatetemporaryqueuesontheJMSmessagebroker.FollowingfromthattheCitrusJMSuserhastohavethepermissiontodoso.Besurethattheuserhasthesufficientrightswhenusingtemporaryreplydestinations.

Uptonowwehavesentamessageandwaitedforasynchronousresponseinthenextstep.Nowitisalsopossibletoswitchthedirectionsofsendandreceiveactions.ThenwehavethesituationwhereCitrusreceivesaJMSmessagefirstandthenCitrusisinchargeofprovidingapropersynchronousresponsemessagetotheinitialsender.

InthisscenariotheforeignmessageproducerhasstoredadynamicJMSreplyqueuedestinationtotheJMSheader.SoCitrushastosendthereplymessagetothisspecificreplydestination,whichisdynamicofcourse.Fortunatelytheheavyliftisdonewiththe

CitrusReferenceGuide

231Jms

Page 232: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JMSmessageendpointandwedonothavetochangeanythinginourconfiguration.Againwejustdefineasynchronousmessageendpointintheapplicationcontext.

<citrus-jms:sync-endpointid="helloServiceSyncEndpoint"destination-name="Citrus.HelloService.InOut.Queue"timeout="10000"/>

Nowtheonlythingthatchangeshereisthatwefirstreceiveamessageinourtestcaseonthisendpoint.Thesecondstepisasendmessageactionthatreferencesthissameendpointandwearedone.Citrusautomaticallymanagesthereplydestinationsforus.

<testcasename="synchronousMessagingTest"><actions><receiveendpoint="helloServiceSyncEndpoint"><message><data>[...]</data></message></receive>

<sendendpoint="helloServiceSyncEndpoint"><message><data>[...]</data></message></send></actions></testcase>

JMStopics

UptonowwehaveusedJMSqueuedestinationsonourendpoints.CitrusisalsoabletoconnecttoJMStopicdestinations.IncontrarytoJMSqueueswhichrepresentsthepoint-to-pointcommunicationJMStopicsusepublish-subscribemechanisminordertospreadmessagesoverJMS.AJMStopicproducerpublishesmessagestothetopic,whilethetopicacceptsmultiplemessagesubscriptionsanddeliversthemessagetoallsubscribers.

TheCitrusJMSendpointsoffertheattribute'pub-sub-domain'.OncethisattributeissettotrueCitruswilluseJMStopicsinsteadofqueuedestinations.Seethefollowingexamplewherethepublish-subscribeattributeissettotrueinJMSmessageendpoint

CitrusReferenceGuide

232Jms

Page 233: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

components.

<citrus-jms:endpointid="helloServiceQueueEndpoint"destination="helloServiceQueue"pub-sub-domain="true"/>

WhenusingJMStopicsyouwillbeabletosubscribeseveraltestactionstothetopicdestinationandreceiveamessagemultipletimesasallsubscriberswillreceivethemessage.

ImportantItisveryimportanttokeepinmindthatCitrusdoesnotdealwithdurablesubscribers.Thismeansthatmessagesthatweresentinadvancetothemessagesubscriptionarenotdeliveredtothemessageendpoint.SoracingconditionsmaycauseproblemswhenusingJMStopicendpointsinCitrus.BesuretoletCitrussubscribetothetopicbeforemessagesaresenttoit.Otherwiseyoumayloosesomemessagesthatweresentinadvancetothesubscription.

JMSmessageheaders

TheJMSspecificationdefinesasetofspecialmessageheaderentriesthatcangointoyourJMSmessage.TheseJMSheadersarestoreddifferentlyinaJMSmessageheaderthanothercustomheaderentriesdo.Thereforethesespecialheadervaluesshouldbesetinaspecialsyntaxthatwediscussinthenextparagraphs.

<header><elementname="citrus_jms_correlationId"value="$correlationId"/><elementname="citrus_jms_messageId"value="$messageId"/><elementname="citrus_jms_redelivered"value="$redelivered"/><elementname="citrus_jms_timestamp"value="$timestamp"/></header>

AsyouseeallJMSspecificmessageheadersusethecitrusjmsprefix.ThisprefixcomesfromSpringIntegrationmessageheadermappersthattakecareofsettingthoseheadersintheJMSmessageheaderproperly.

TypingofmessageheaderentriesmayalsobeofinterestinordertomeettheJMSstandardsoftypedmessageheaders.ForinstancethefollowingmessageheaderisoftypedoubleandisthereforetransferredviaJMSasadoublevalue.

<header><elementname="amount"value="19.75"type="double"/></header>

CitrusReferenceGuide

233Jms

Page 234: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SOAPoverJMS

WhensendingSOAPmessagesyouhavetodealwithproperenvelope,bodyandheaderconstruction.InCitrusyoucanaddaspecialmessageconverterthatperformstheheavyliftforyou.JustaddthemessageconvertertotheJMSendpointasshowninthenextprogramlisting:

<citrus-jms:endpointid="helloServiceSoapJmsEndpoint"destination-name="Citrus.HelloService.Request.Queue"message-converter="soapJmsMessageConverter"/>

<beanid="soapJmsMessageConverter"class="com.consol.citrus.jms.message.SoapJmsMessageConverter"

WiththismessageconverteryoucanskiptheSOAPenvelopecompletelyinyourtestcase.Youjustdealwiththemessagebodypayloadandtheheaderentries.Therestisdonebythemessageconverter.SoyougetproperSOAPmessagesontheproducerandconsumerside.

CitrusReferenceGuide

234Jms

Page 235: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

HTTPRESTsupportRESTAPIshavegainedmoreandmoresignificanceregardingclient-serverinterfaces.TheRESTclientisnothingbutaHTTPclientsendingHTTPrequestsusuallyinJSONdataformattoaHTTPserver.AsHTTPisasynchronousprotocolbynaturetheclientreceivestheserverresponsesynchronously.CitrusisabletoconnectwithHTTPservicesandtestRESTAPIsonbothclientandserversidewithapowerfulJSONmessagedatasupport.InthenextsectionsyouwilllearnhowtoinvokeHTTPservicesasaclientandhowtohandleRESTHTTPrequestsinatestcase.WedealwithsettingupaHTTPserverinordertoacceptclientrequestsandprovideproperHTTPresponseswithGET,PUT,DELETEorPOSTrequestmethod.

NoteThehttpcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldaddthemoduleasMavendependencytoyourprojectaccordingly.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-http</artifactId><version>2.7</version></dependency>

AsCitrusprovidesacustomizedHTTPconfigurationschemafortheSpringapplicationcontextconfigurationfileswehavetoaddnametothetoplevelbeanselement.Simplyincludethehttp-confignamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-http="http://www.citrusframework.org/schema/http/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/http/confighttp://www.citrusframework.org/schema/http/config/citrus-http-config.xsd">

[...]

</beans>

CitrusReferenceGuide

235Http

Page 236: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowwearereadytousethecustomizedCitrusHTTPconfigurationelementswiththecitrus-httpnamespaceprefix.

HTTPRESTclient

OntheclientsidewehaveasimpleHTTPmessageclientcomponentconnectingtotheserver.Therequest-urlattributedefinestheHTTPserverendpointURLtoconnectto.Asusualyoucanreferencethisclientinyourtestcaseinordertosendandreceivemessages.Citrusasclientwaitsfortheresponsemessagefromserver.Afterthattheresponsemessagegoesthroughthevalidationprocessasusual.LetusseehowaCitrusHTTPclientcomponentlookslike:

<citrus-http:clientid="helloHttpClient"request-url="http://localhost:8080/hello"request-method="GET"content-type="application/xml"timeout="60000"/>

Therequest-methoddefinestheHTTPmethodtouse.Inadditiontothatwecanspecifythecontent-typeoftherequestweareabouttosend.TheclientbuildstheHTTPrequestandsendsittotheHTTPserver.WhiletheclientiswaitingforthesynchronousHTTPresponsetoarriveweareabletopollseveraltimesfortheresponsemessageinourtestcase.Asusualaoucanusethesameclientendpointinyourtestcasetosendandreceivemessagessynchronously.Incasethereplymessagecomesintoolateaccordingtothetimeoutsettingsarespectivetimeouterrorisraised.

HttpdefinesseveralrequestmethodsthataclientcanusetoaccessHttpserverresources.IntheexampleclientaboveweareusingGETasdefaultrequestmethod.OfcourseyoucanoverwritethissettinginatestcaseactionbysettingtheHTTPrequestmethodinsidethesendingtestaction.TheHttpclientcomponentcanbeusedasnormalendpointinasendingtestaction.Usesomethinglikethisinyourtest:

XMLDSL

<sendendpoint="helloHttpClient"><message><payload><TestMessage><Text>HelloHttpServer</Text></TestMessage></payload></message>

CitrusReferenceGuide

236Http

Page 237: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<header><elementname="citrus_http_method"value="POST"/></header></send>

TipCitrususestheSpringRESTtemplatemechanismforsendingoutHTTPrequests.ThismeansyouhavegreatcustomizingopportunitieswithaspecialRESTtemplateconfiguration.YoucanthinkofbasicHTTPauthentication,readtimeoutsandspecialmessagefactoryimplementations.JustusethecustomRESTtemplateattributeinclientconfigurationlikethis:

<citrus-http:clientid="helloHttpClient"request-url="http://localhost:8080/hello"request-method="GET"content-type="text/plain"rest-template="customizedRestTemplate"/>

<!--Customizedresttemplate--><beanname="customizedRestTemplate"class="org.springframework.web.client.RestTemplate"><propertyname="messageConverters"><util:listid="converter"><beanclass="org.springframework.http.converter.StringHttpMessageConverter"><propertyname="supportedMediaTypes"><util:listid="types"><value>text/plain</value></util:list></property></bean></util:list></property><propertyname="errorHandler"><!--Customerrorhandler--></property><propertyname="requestFactory"><beanclass="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"><propertyname="readTimeout"value="9000"/></bean></property></bean>

UptonowwehaveusedanormalsendtestactiontosendHttprequestsasaclient.ThisiscompletelyvalidstrategyastheCitrusHttpclientisanormalendpoint.ButwemightwanttosetsomemoreHttpRESTspecificpropertiesandsettings.InordertosimplifytheHttpusageinatestcasewecanuseaspecialtestactionimplementation.TheCitrusHttpspecificactionsarelocatedinaseparateXMLnamespace.SowenneedtoaddthisnamespacetoourtestcaseXMLfirst.

CitrusReferenceGuide

237Http

Page 238: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:http="http://www.citrusframework.org/schema/http/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/http/testcasehttp://www.citrusframework.org/schema/http/testcase/citrus-http-testcase.xsd">

[...]

</beans>

ThetestcaseisnowreadytousethespecificHttptestactionsbyusingtheprefixhttp:.

XMLDSL

<http:send-requestclient="httpClient"><http:POSTpath="/customer"><http:headerscontent-type="application/xml"accept="application/xml,*/*"><http:headername="X-CustomHeaderId"value="$custom_header_id"/></http:headers><http:body><http:data><![CDATA[<customer><id>citrus:randomNumber()</id><name>testuser</name></customer>]]></http:data></http:body></http:POST></http:send-request>

TheactionaboveusesseveralHttpspecificsettingssuchastherequestmethodPOSTaswellasthecontent-typeandacceptheaders.AsusualthesendactionneedsatargetHttpclientendpointcomponent.Wecanspecifyarequestpathattributethataddedasrelativepathtothebaseuriusedontheclient.

WhenusingaGETrequestwecanspecifysomerequesturiparameters.

XMLDSL

<http:send-requestclient="httpClient"><http:GETpath="/customer/$custom_header_id"><http:paramscontent-type="application/xml"accept="application/xml,*/*">

CitrusReferenceGuide

238Http

Page 239: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<http:paramname="type"value="active"/></http:params></http:GET></http:send-request>

ThesendactionaboveusesaGETrequestontheendpointurihttp://localhost:8080/customer/1234?type=active.

OfcoursewhensendingHttpclientrequestswearealsointerestedinreceivingHttpresponsemessages.WewanttovalidatethesuccessresponsewithHttpstatuscode.

XMLDSL

<http:receive-responseclient="httpClient"><http:headersstatus="200"reason-phrase="OK"version="HTTP/1.1"><http:headername="X-CustomHeaderId"value="$custom_header_id"/></http:headers><http:body><http:data><![CDATA[<customerResponse><success>true</success></customerResponse>]]></http:data></http:body></http:receive-response>

Thereceive-responsetestactionalsousesaclientcomponent.Wecanexpectresponsestatuscodeinformationsuchasstatusandreason-phrase.OfcourseCitruswillraiseavalidationexceptionincaseHttpstatuscodesmismatch.

UptonowwehaveusedXMLDSLtestcases.TheJavaDSLinCitrusalsoworkswithspecificHttptestactions.Seefollowingexampleandfindouthowthisworks:

XMLDSL

@CitrusTestpublicvoidhttpActionTest()http().client("httpClient").send().post("/customer").payload("<customer>"+"<id>citrus:randomNumber()</id>"+"<name>testuser</name>"+"</customer>").header("X-CustomHeaderId","$custom_header_id")

CitrusReferenceGuide

239Http

Page 240: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.contentType("text/xml").accept("text/xml,*/*");

http().client("httpClient").receive().response(HttpStatus.OK).payload("<customerResponse>"+"<success>true</success>"+"</customerResponse>").header("X-CustomHeaderId","$custom_header_id").version("HTTP/1.1");

Thereisonemoresettingontheclienttobeawareof.BydefaulttheclientcomponentwilladdtheAccepthttpheaderandsetitsvaluetoalistofallsupportedencodingsonthehostoperatingsystem.Asthislistcangetverylongyoumaywanttonotsetthisdefaultacceptheader.ThesettingisdoneintheSpringRestTemplate:

<beanname="customizedRestTemplate"class="org.springframework.web.client.RestTemplate"><propertyname="messageConverters"><util:listid="converter"><beanclass="org.springframework.http.converter.StringHttpMessageConverter"><propertyname="writeAcceptCharset"value="false"/></bean></util:list></property></bean>

YouwouldhaveaddthiscustomRestTemplateconfigurationandsetittotheclientcomponentwithrest-templateproperty.ButfortunatelytheCitrusclientcomponentprovidesaseparatesettingdefault-accept-headerwhichisaBooleansetting.Bydefaultitissettotruesothedefaultacceptheaderisautomaticallyaddedtoallrequests.Ifyousetthisflagtofalsetheheaderisnotset:

<citrus-http:clientid="helloHttpClient"request-url="http://localhost:8080/hello"request-method="GET"content-type="text/plain"default-accept-header="false"/>

OfcourseyoucansettheAcceptheaderoneachsendoperationinordertotelltheserverwhatkindofcontenttypesaresupportedinresponsemessages.

CitrusReferenceGuide

240Http

Page 241: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowwecansendandreceivemessagesasHttpclientwithspecifictestactions.NowletsmoveontotheHttpserver.

HTTPclientinterceptors

Theclientcomponentisabletoaddcustominterceptorsthatparticipateintherequest/responseprocessing.Theinterceptorsneedtoimplementthecommoninterfaceorg.springframework.http.client.ClientHttpRequestInterceptor.

<citrus-http:clientid="helloHttpClient"request-url="http://localhost:8080/hello"request-method="GET"interceptors="clientInterceptors"/>

<util:listid="clientInterceptors"><beanclass="com.consol.citrus.http.interceptor.LoggingClientInterceptor"/></util:list>

ThesampleaboveaddstheCitrusloggingclientinterceptorthatlogsrequestsandresponsesexchangedwiththatclientcomponent.Youcanaddcustominterceptorimplementationshereinordertoparticipateintherequest/responsemessageprocessing.

HTTPRESTserver

TheHTTPclientwasquiteeasyandstraightforward.ReceivingHTTPmessagesisalittlebitmorecomplicatedbecauseCitrushastoprovideserverfunctionalitylisteningonalocalportforclientconnections.ThereforeCitrusoffersanembeddedHTTPserverwhichiscapableofhandlingincomingHTTPrequests.OnceaclientconnectionisacceptedtheHTTPservermustalsoprovideaproperHTTPresponsetotheclient.InthenextfewlinesyouwillseehowtosimulateserversideHTTPRESTservicewithCitrus.

<citrus-http:serverid="helloHttpServer"port="8080"auto-start="true"resource-base="src/it/resources"/>

CitrususesanembeddedJettyserverthatwillautomaticallystartwhentheSpringapplicationcontextisloaded(auto-start="true").Thebasicconnectorislisteningonport8080forrequests.Testcasescaninteractwiththisserverinstanceviamessage

CitrusReferenceGuide

241Http

Page 242: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

channelsbydefault.Theserverprovidesaninboundchannelthatholdsincomingrequestmessages.Thetestcasecanreceivethoserequestsfromthechannelwithanormalreceivetestaction.InasecondstepthetestcasecanprovideasynchronousresponsemessageasreplywhichwillbeautomaticallysentbacktotheHTTPclientasresponse.

Thefigureaboveshowsthebasicsetupwithinboundchannelandreplychannel.Youasatestershouldnotworryaboutthistomuch.Bydefaultyouasatesterjustusetheserverassynchronousendpointinyourtestcase.Thismeansthatyousimplyreceiveamessagefromtheserverandsendaresponseback.

<testcasename="httpServerTest"><actions><receiveendpoint="helloHttpServer"><message><data>[...]</data></message></receive>

<sendendpoint="helloHttpServer"><message><data>[...]</data></message></send></actions></testcase>

Asyoucanseewereferencetheserveridinbothreceiveandsendactions.TheCitrusserverinstancewillautomaticallysendtheresponsebacktothecallingHTTPclient.Inmostcasesthisisexactlywhatwewanttodo-sendbackaresponsemessagethatisspecifiedinsidethetest.TheHTTPservercomponentbydefaultusesachannelendpointadapterinordertoforwardallincomingrequeststoaninmemorymessagechannel.Thisisdonecompletelybehindthescenes.TheHttpservercomponentprovidessomemorecustomizationpossibilitieswhenitcomestoendpointadapter

CitrusReferenceGuide

242Http

Page 243: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

implementations.Thistopicisdiscussedinaseparatesectionendpoint-adapter.Uptonowwekeepitsimplebysynchronouslyreceivingandsendingmessagesinthetestcase.

TipThedefaultchannelendpointadapterautomaticallycreatesaninboundmessagechannelwhereincomingmessagesarestoredtointernally.Soifyouneedtocleanupaserverthathasalreadystoredsomeincomingmessagesyoucandothiseasilybypurgingtheinternalmessagechannel.ThemessagechannelfollowsanamingconventionserverName.inboundwhereserverNameistheSpringbeannameoftheCitrusserverendpointcomponent.Ifyoupurgethisinternalchannelinabeforetestnatureyouaresurethatobsoletemessagesonaserverinstancegetpurgedbeforeeachtestisexecuted.

Soletsgetbacktoourmissionofprovidingresponsemessagesasservertoconnectedclients.AsyoumightknowHttpRESTworkswithsomecharacteristicpropertieswhenitcomestosendandreceivemessages.ForinstanceaclientcansenddifferentrequestmethodsGET,POST,PUT,DELETE,HEADandsoon.TheCitrusservermayverifythismethodwhenreceivingclientrequests.ThereforewehaveintroducedspecialHttptestactionsforservercommunication.Havealookatasimpleexample:

<http:receive-requestserver="helloHttpServer"><http:POSTpath="/test"><http:headerscontent-type="application/xml"accept="application/xml,*/*"><http:headername="X-CustomHeaderId"value="$custom_header_id"/><http:headername="Authorization"value="Basicc29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA=="/></http:headers><http:body><http:data><![CDATA[<testRequestMessage><text>HelloHttpServer</text></testRequestMessage>]]></http:data></http:body></http:POST><http:extract><http:headername="X-MessageId"variable="message_id"/></http:extract></http:receive-request>

<http:send-responseserver="helloHttpServer"><http:headersstatus="200"reason-phrase="OK"version="HTTP/1.1"><http:headername="X-MessageId"value="$message_id"/><http:headername="X-CustomHeaderId"value="$custom_header_id"/><http:headername="Content-Type"value="application/xml"/>

CitrusReferenceGuide

243Http

Page 244: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</http:headers><http:body><http:data><![CDATA[<testResponseMessage><text>HelloCitrus</text></testResponseMessage>]]></http:data></http:body></http:send-response>

WereceiveaclientrequestandvalidatethattherequestmethodisPOSTonrequestpath/test.Nowwecanvalidatespecialmessageheaderssuchascontent-type.Inadditiontothatwecancheckcustomheadersandbasicauthorizationheaders.Asusualtheoptionalmessagebodyiscomparedtoanexpectedmessagetemplate.ThecustomX-MessageIdheaderissavedtoatestvariablemessage_idforlaterusageintheresponse.

TheresponsemessagedefinesHttptypicalentitiessuchasstatusandreason-phrase.Herethetestercansimulate404NOT_FOUNDerrorsorsimilarotherstatuscodesthatgetsendbacktotheclient.InourexampleeverythingisOKandwesendbackaresponsebodyandsomecustomheaderentries.

ThatisbasicallyhowCitrussimulatesHttpserveroperations.Wereceivetheclientrequestandvalidatetherequestproperties.ThenwesendbackaresponsewithaHttpstatuscode.

AsusualalltheseHttpspecificactionsarealsoavailableinJavaDSL.

@CitrusTestpublicvoidhttpServerActionTest()http().server("helloHttpServer").receive().post("/test").payload("<testRequestMessage>"+"<text<HelloHttpServer</text>"+"</testRequestMessage>").contentType("application/xml").accept("application/xml,*/*").header("X-CustomHeaderId","$custom_header_id").header("Authorization","Basicc29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA==").extractFromHeader("X-MessageId","message_id");

http().server("helloHttpServer")

CitrusReferenceGuide

244Http

Page 245: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.send().response(HttpStatus.OK).payload("<testResponseMessage>"+"<text<HelloCitrus</text>"+"</testResponseMessage>").version("HTTP/1.1").contentType("application/xml").header("X-CustomHeaderId","$custom_header_id").header("X-MessageId","$message_id");

ThisistheexactsameexampleinJavaDSL.Weselectserveractionsfirstandreceiveclientrequests.ThenwesendbackaresponsewithaHttpStatus.OKstatus.ThiscompletestheserveractionsonHttpmessagetransport.NowwecontinuewithsomemoreHttpspecificsettingsandfeatures.

HTTPheaders

WhendealingwithHTTPrequest/responsecommunicationwealwaysdealwithHTTPspecificheaders.TheHTTPprotocoldefinesagroupofheaderattributesthatbothclientandserverneedtobeabletohandle.YoucansetandvalidatetheseHTTPheadersinCitrusquiteeasy.LetushavealookataclientoperationinCitruswheresomeHTTPheadersareexplicitlysetbeforetherequestissentout.

<http:send-requestclient="httpClient"><http:POST><http:headers><http:headername="X-CustomHeaderId"value="$custom_header_id"/><http:headername="Content-Type"value="text/xml"/><http:headername="Accept"value="text/xml,*/*"/></http:headers><http:body><http:payload><testRequestMessage><text>HelloHttpServer</text></testRequestMessage></http:payload></http:body></http:POST></http:send-request>

Weareabletosetcustomheaders(X-CustomHeaderId)thatgodirectlyintotheHTTPheadersectionoftherequest.InadditiontothattesterscanexplicitlysetHTTPreservedheaderssuchasContent-Type.Fortunatelyyoudonothavetosetallheadersonyour

CitrusReferenceGuide

245Http

Page 246: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

own.CitruswillautomaticallysettherequiredHTTPheadersfortherequest.SowehavethefollowingHTTPrequestwhichissenttotheserver:

POST/testHTTP/1.1Accept:text/xml,*/*Content-Type:text/xmlX-CustomHeaderId:123456789Accept-Charset:macromanUser-Agent:JakartaCommons-HttpClient/3.1Host:localhost:8091Content-Length:175<testRequestMessage><text>HelloHttpServer</text></testRequestMessage>

OnserversidetestersareinterestedinvalidatingtheHTTPheaders.WithinCitrusreceiveactionyousimplydefinetheexpectedheaderentries.TheHTTPspecificheadersareautomaticallyavailableforvalidationasyoucanseeinthisexample:

<http:receive-requestserver="httpServer"><http:POST><http:headers><http:headername="X-CustomHeaderId"value="$custom_header_id"/><http:headername="Content-Type"value="text/xml"/><http:headername="Accept"value="text/xml,*/*"/></http:headers><http:body><http:payload><testRequestMessage><text>HelloHttpServer</text></testRequestMessage></http:payload></http:body></http:POST></http:receive-request>

ThetestchecksoncustomheadersandHTTPspecificheaderstomeettheexpectedvalues.

NowthatwehaveacceptedtheclientrequestandvalidatedthecontentsweareabletosendbackaproperHTTPresponsemessage.SamethingherewithHTTPspecificheaders.TheHTTPprotocoldefinesseveralheadersmarkingthesuccessorfailureoftheserveroperation.InthetestcaseyoucansetthoseheadersfortheresponsemessagewithconventionalCitrusheadernames.Seethefollowingexampletofindouthowthatworksforyou.

CitrusReferenceGuide

246Http

Page 247: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<http:send-responseserver="httpServer"><http:headersstatus="200"reason-phrase="OK"><http:headername="X-CustomHeaderId"value="$custom_header_id"/><http:headername="Content-Type"value="text/xml"/></http:headers><http:body><http:payload><testResponseMessage><text>HelloCitrusClient</text></testResponseMessage></http:payload></http:body></http:send-response>

Oncemorewesetthecustomheaderentry(X-CustomHeaderId)andaHTTPreservedheader(Content-Type)fortheresponsemessage.OntopofthisweareabletosettheresponsestatusfortheHTTPresponse.Weusethereservedheadernamesstatusinordertomarkthesuccessoftheserveroperation.WiththismechanismwecaneasilysimulatedifferentserverbehavioursuchasHTTPerrorresponsecodes(e.g.404-Notfound,500-Internalerror).Letushaveacloserlookatthegeneratedresponsemessage:

HTTP/1.1200OKContent-Type:text/xml;charset=UTF-8Accept-Charset:macromanContent-Length:205Server:Jetty(7.0.0.pre5)<testResponseMessage><text>HelloCitrusClient</text></testResponseMessage>

TipYoudonothavetosetthereasonphraseallthetime.ItissufficienttoonlysettheHTTPstatuscode.CitruswillautomaticallyaddtheproperreasonphraseforwellknownHTTPstatuscodes.

TheonlythingthatismissingrightnowisthevalidationofHTTPstatuscodeswhenreceivingtheserverresponseinaCitrustestcase.ItisveryeasyasyoucanusetheCitrusreservedheadernamesforvalidation,too.

<http:receive-responseclient="httpClient"><http:headersstatus="200"reason-phrase="OK"version="HTTP/1.1"><http:headername="X-CustomHeaderId"value="$custom_header_id"/></http:headers><http:body>

CitrusReferenceGuide

247Http

Page 248: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<http:payload><testResponseMessage><text>HelloTestFramework</text></testResponseMessage></http:payload></http:body></http:receive-response>

UptonowwehaveusedsomeofthebasicCitrusreservedHTTPheadernames(status,version,reason-phrase).InHTTPRESTfulservicessomeotherheadernamesareessentialforvalidation.Thesearerequestattributeslikequeryparameters,contextpathandrequestURI.TheCitrusserversideRESTmessagecontrollerwillautomaticallyaddallthisinformationtothemessageheaderforyou.Soallyouneedtodoisvalidatetheheaderentriesinyourtest.

ThenextexamplereceivesaHTTPGETmethodrequestonserverside.HeretheGETrequestdoesnothaveanymessagepayload,sothevalidationjustworksontheinformationgiveninthemessageheader.Weassumetheclienttocallhttp://localhost:8080/app/users?id=123456789.Asatesterweneedtovalidatetherequestmethod,requestURI,contextpathandthequeryparameters.

<http:receive-requestserver="httpServer"><http:GETpath="/app/users"context-path="/app"><http:params><http:paramname="id"value="123456789"/></http:params><http:headers><http:headername="Host"value="localhost:8080"/><http:headername="Content-Type"value="text/html"/><http:headername="Accept"value="text/xml,*/*"/></http:headers><http:body><http:data></http:data></http:body></http:GET></http:receive-request>

TipBeawareoftheslightdifferencesinrequestURIandcontextpath.Thecontextpathgivesyouthewebapplicationcontextpathwithintheservletcontainerforyourwebapplication.TherequestURIalwaysgivesyouthecompletepaththatwascalledforthisrequest.

CitrusReferenceGuide

248Http

Page 249: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AsyoucanseeweareabletovalidateallpartsoftheinitialrequestendpointURItheclientwascalling.ThiscompletestheHTTPheaderprocessingwithinCitrus.OnbothclientandserversideCitrusisabletosetandvalidateHTTPspecificheaderentrieswhichisessentialforsimulatingHTTPcommunication.

HTTPserverinterceptors

Theservercomponentisabletoaddcustominterceptorsthatparticipateintherequest/responseprocessing.Theinterceptorsneedtoimplementthecommoninterfaceorg.springframework.web.servlet.HandlerInterceptor.

<citrus-http:serverid="httpServer"port="8080"auto-start="true"interceptors="serverInterceptors"/>

<util:listid="serverInterceptors"><beanclass="com.consol.citrus.http.interceptor.LoggingHandlerInterceptor"/></util:list>

ThesampleaboveaddstheCitruslogginghandlerinterceptorthatlogsrequestsandresponsesexchangedwiththatservercomponent.Youcanaddcustominterceptorimplementationshereinordertoparticipateintherequest/responsemessageprocessing.

HTTPformurlencodeddata

HTMLformdatacanbesenttotheserverusingdifferentmethodsandcontenttypes.OneofthemisaPOSTmethodwithx-www-form-urlencodedbodycontent.Theformdataelementsaresenttotheserverusingkey-valuepairsPOSTdatawheretheformcontrolnameisthekeyandthecontroldataistheurlencodedvalue.

Formurlencodedformdatacontentcouldlooklikethis:

password=s%21cr%21t&username=foo

Ayoucanseetheformdataisautomaticallyencoded.Intheexampleabovewetransmittwoformcontrolspasswordandusernamewithrespectivevaluess$cr$tandfoo.IncasewewouldvalidatethisformdatainCitrusweareabletodothiswithplaintextmessagevalidation.

CitrusReferenceGuide

249Http

Page 250: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<receiveendpoint="httpServer"><messagetype="plaintext"><data><![CDATA[password=s%21cr%21t&username=$username]]></data></message><header><elementname="citrus_http_method"value="POST"/><elementname="citrus_http_request_uri"value="/form-test"/><elementname="Content-Type"value="application/x-www-form-urlencoded"/></header></receive>

Obviouslyvalidatingthesekey-valuepaircharactersequencescanbehardespeciallywhenhavingHTMLformswithlotsofformcontrols.ThisiswhyCitrusprovidesaspecialmessagevalidatorforx-www-form-urlencodedcontents.Firstofallwehavetoaddcitrus-httpmoduleasdependencytoourprojectifnotdonesoyet.AfterthatwecanaddthevalidatorimplementationtothelistofmessagevalidatorsusedinCitrus.

<citrus:message-validators><citrus:validatorclass="com.consol.citrus.http.validation.FormUrlEncodedMessageValidator"/></citrus:message-validators>

Nowweareabletoreceivetheurlencodedformdatamessageinatest.

<receiveendpoint="httpServer"><messagetype="x-www-form-urlencoded"><payload><form-dataxmlns="http://www.citrusframework.org/schema/http/message"><content-type>application/x-www-form-urlencoded</content-type><action>/form-test</action><controls><controlname="password"><value>$password</value></control><controlname="username"><value>$username</value></control></controls></form-data></payload></message><header><elementname="citrus_http_method"value="POST"/>

CitrusReferenceGuide

250Http

Page 251: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<elementname="citrus_http_request_uri"value="/form-test"/><elementname="Content-Type"value="application/x-www-form-urlencoded"/></header></receive>

Weuseaspecialmessagetypex-www-form-urlencodedsothenewmessagevalidatorwilltakeaction.TheformurlencodedmessagevalidatorisabletohandleaspecialXMLrepresentationoftheformdata.ThisenablestheverypowerfulXMLmessagevalidationcapabilitiesofCitrussuchasignoringelementsandusageoftestvariablesinline.

Eachformcontrolistranslatedtoacontrolelementwithrespectivenameandvalueproperties.Theformdataisvalidatedinamorecomfortablewayastheplaintextmessagevalidatorwouldbeabletooffer.

HTTPerrorhandling

SofarwehavereceivedresponsemessageswithHTTPstatuscode200OK.Howtodealwithservererrorslike404NotFoundor500Internalservererror?ThedefaultHTTPmessageclienterrorstrategyistopropagateservererrorresponsemessagestothereceiveactionforvalidation.WesimplycheckonHTTPstatuscodeandstatustextforerrorvalidation.

<http:send-requestclient="httpClient"><http:body><http:payload><testRequestMessage><text>HelloHttpServer</text></testRequestMessage></http:payload></http:body></http:send-request>

<http:receive-requestclient="httpClient"><http:body><http:data><![CDATA[]]></http:data></http:body><http:headersstatus="403"reason-phrase="FORBIDDEN"/></http:receive>

Themessagedatacanbeemptydependingontheserverlogicfortheseerrorsituations.Ifwereceiveadditionalerrorinformationasmessagepayloadjustaddvalidationassertionsasusual.

CitrusReferenceGuide

251Http

Page 252: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

InsteadofreceivingsuchemptymessageswithchecksonHTTPstatusheaderinformationwecanchangetheerrorstrategyinthemessagesendercomponentinordertoautomaticallyraiseexceptionsonresponsemessagesotherthan200OK.ThereforewegobacktotheHTTPmessagesenderconfigurationforchangingtheerrorstrategy.

<citrus-http:clientid="httpClient"request-url="http://localhost:8080/test"error-strategy="throwsException"/>

Nowweexpectanexceptiontobethrownbecauseoftheerrorresponse.Followingfromthatwehavetochangeourtestcase.InsteadofreceivingtheerrormessagewithreceiveactionweasserttheclientexceptionandcheckontheHTTPstatuscodeandstatustext.

<assertexception="org.springframework.web.client.HttpClientErrorException"message="403Forbidden"><when><http:send-requestclient="httpClient"><http:body><http:payload><testRequestMessage><text>HelloHttpServer</text></testRequestMessage></http:payload></http:body></http:send-request></when></assert>

BothwaysofhandlingHTTPerrormessagesonclientsidearevalidforexpectingtheservertoraiseHTTPerrorcodes.Choosethepreferredwayaccordingtoyourtestprojectrequirements.

HTTPclientbasicauthentication

Asclientyoumayhavetousebasicauthenticationinordertoaccessaresourceontheserver.Inmostcasesthiswillbeusername/passwordauthenticationwherethecredentialsaretransmittedintherequestheadersectionasbase64encoding.

TheeasiestapproachtosettheAuthorizationheaderforabasicauthenticationHTTPrequestwouldbetosetitonyourowninthesendactiondefinition.Ofcourseyouhavetousethecorrectbasicauthenticationheadersyntaxwithbase64encodingforthe

CitrusReferenceGuide

252Http

Page 253: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

username:passwordphrase.Seethissimpleexample.

<http:headers><http:headername="Authorization"value="Basicc29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA=="/></http:headers>

CitruswilladdthisheadertotheHTTPrequestsandtheserverwillreadtheAuthorizationusernameandpassword.Formoreconvenientbase64encodingyoucanalsouseaCitrusfunction,seefunctions-encode-base64

NowthereisamorecomfortablewaytosetthebasicauthenticationheaderinalltheCitrusrequests.AsCitrususesSpring'sRESTsupportwiththeRestTemplateandClientHttpRequestFactorythebasicauthenticationisalreadycoveredthereinamoregenericway.YousimplyhavetoconfigurethebasicauthenticationcredentialsontheRestTemplate'sClientHttpRequestFactory.Justseethefollowingexampleandlearnhowtodothat.

<citrus-http:clientid="httpClient"request-method="POST"request-url="http://localhost:8080/test"request-factory="basicAuthFactory"/>

<beanid="basicAuthFactory"class="com.consol.citrus.http.client.BasicAuthClientHttpRequestFactory"><propertyname="authScope"><beanclass="org.apache.http.auth.AuthScope"><constructor-argvalue="localhost"/><constructor-argvalue="8072"/><constructor-argvalue=""/><constructor-argvalue="basic"/></bean></property><propertyname="credentials"><beanclass="org.apache.http.auth.UsernamePasswordCredentials"><constructor-argvalue="someUsername"/><constructor-argvalue="somePassword"/></bean></property></bean>

Theadvantagesofthismethodisobvious.Nowallsendingtestactionsthatreferencetheclientcomponentwillautomaticallyaddthebasicauthenticationheader.

CitrusReferenceGuide

253Http

Page 254: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ImportantSinceCitrushasupgradedtoSpring3.1.xtheJakartacommonsHTTPclientisdeprecatedwithCitrusversion1.2.TheformerlyusedUserCredentialsClientHttpRequestFactoryisthereforealsodeprecatedandwillnotcontinuewithnextversions.PleaseupdateyourconfigurationifyouarecomingfromCitrus1.1orearlierversions.

TheaboveconfigurationresultsinHTTPclientrequestswithauthenticationheadersproperlysetforbasicauthentication.TheclientrequestfactorytakescareonaddingtheproperbasicauthenticationheadertoeachrequestthatissentwiththisCitrusmessagesender.Citrususespreemtiveauthentication.Themessagesenderonlysendsasinglerequesttotheserverwithallauthenticationinformationsetinthemessageheader.Therequestwhichdeterminestheauthenticationschemeontheserverisskipped.ThisiswhyyouhavetoaddsomeauthscopeintheclientrequestfactorysoCitruscansetupanauthenticationcachewithintheHTTPcontextinordertohavepreemtiveauthentication.

AsaresultofthebasicauthclientrequestfactorythefollowingexamplerequestthatiscreatedbytheCitrusHTTPclienthastheAuthorizationheaderset.ThisisdonenowautomaticallyforallrequestswiththisHTTPclient.

POST/testHTTP/1.1Accept:text/xml,*/*Content-Type:text/xmlAccept-Charset:iso-8859-1,us-ascii,utf-8Authorization:Basicc29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA==User-Agent:JakartaCommons-HttpClient/3.1Host:localhost:8080Content-Length:175<testRequestMessage><text>HelloHttpServer</text></testRequestMessage>

HTTPserverbasicauthentication

Citrusasaservercanalsosetbasicauthenticationsoclientsneedtoauthenticateproperlywhenaccessingserverresources.

<citrus-http:serverid="basicAuthHttpServer"port="8090"auto-start="true"resource-base="src/it/resources"security-handler="basicSecurityHandler"/>

CitrusReferenceGuide

254Http

Page 255: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<beanid="securityHandler"class="com.consol.citrus.http.security.SecurityHandlerFactory"><propertyname="users"><list><beanclass="com.consol.citrus.http.security.User"><propertyname="name"value="citrus"/><propertyname="password"value="secret"/><propertyname="roles"value="CitrusRole"/></bean></list></property><propertyname="constraints"><map><entrykey="/foo/*"><beanclass="com.consol.citrus.http.security.BasicAuthConstraint"><constructor-argvalue="CitrusRole"/></bean></entry></map></property></bean>

Wehavesetasecurityhandlerontheserverwebcontainerwithaconstraintonallresourceswith/foo/*.Followingfromthattheserverrequiresbasicauthenticationfortheseresources.Thegrantedusersandrolesarespecifiedwithinthesecurityhandlerbeandefinition.ConnectingclientshavetosetthebasicauthHTTPheaderproperlyusingthecorrectuserandroleforaccessingtheCitrusservernow.

Youcancustomizethesecurityhandlerforyourveryspecificneeds(e.g.loadusersandroleswithJDBCfromadatabase).Justhavealookatthecodebaseandinspectthesettingsandpropertiesofferedbythesecurityhandlerinterface.

TipThismechanismisnotrestrictedtobasicauthenticationonly.Withothersettingsyoucanalsosetupdigestorform-basedauthenticationconstraintsveryeasy.

HTTPGzipcompression

Gzipisaverypopularcompressionmechanismforoptimizingthemessagetransportationforlargecontent.TheCitrushttpclientandservercomponentssupportgzipcompressionoutofthebox.Thismeansthatyouonlyneedtosetthespecificencodingheadersinyourhttprequest/responsemessage.

Accept-Encoding=gzipSettingforclientswhenrequestinggzipcompressedresponsecontent.TheHttpservermustsupportgzipcompressiontheninordertoprovidetheresponseaszippedbytestream.TheCitrushttpservercomponentautomaticallyrecognizesthisheaderinarequestandappliesgzipcompressionto

CitrusReferenceGuide

255Http

Page 256: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

theresponse.Content-Encoding=gzipWhenahttpserversendscompressedmessagecontenttotheclientthisheaderissettogzipinordertomarkthecompression.TheHttpclientmustsupportgzipcompressiontheninordertounzipthemessagecontent.TheCitrushttpclientcomponentautomaticallyrecognizesthisheaderinaresponseandappliesgzipunziplogicbeforepassingthemessagetothetestcase.

TheCitrusclientandserverautomaticallytakecareongzipcompressionwhenthoseheadersareset.Inthetestcaseyoudonotneedtoziporunzipthecontentthenasitisautomaticallydonebefore.

ThismeansthatyoucanrequestgzippedcontentfromaserverwithjustaddingthemessageheaderAccept-Encodinginyourhttprequestoperation.

<echo><message>SendHttpclientrequestforgzipcompresseddata</message></echo>

<http:send-requestclient="gzipClient"><http:POST><http:headerscontent-type="text/html"><http:headername="Accept-Encoding"value="gzip"/><http:headername="Accept"value="text/plain"/></http:headers></http:POST></http:send-request>

<echo><message>Receivetextautomaticallygzipunzipped</message></echo>

<http:receive-responseclient="gzipClient"><http:headersstatus="200"reason-phrase="OK"><http:headername="Content-Type"value="text/plain"/></http:headers><http:bodytype="plaintext"><http:data>$text</http:data></http:body></http:receive-response>

OntheserversideifwereceiveamessageandtheresponseshouldbecompressedwithGzipwejusthavetosettheContent-Encodingheaderintheresponseoperation.

<echo><message>Receivegzipcompressedasbase64encodedtext</message></echo>

CitrusReferenceGuide

256Http

Page 257: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<http:receive-requestserver="echoHttpServer"><http:POSTpath="/echo"><http:headers><http:headername="Content-Type"value="text/html"/><http:headername="Accept-Encoding"value="gzip"/><http:headername="Accept"value="text/plain"/></http:headers></http:POST></http:receive-request>

<echo><message>SendHttpservergzipcompressedresponse</message></echo>

<http:send-responseserver="echoHttpServer"><http:headersstatus="200"reason-phrase="OK"><http:headername="Content-Encoding"value="gzip"/><http:headername="Content-Type"value="text/plain"/></http:headers><http:body><http:data>$text</http:data></http:body></http:send-response>

SotheCitrusserverwillautomaticallyaddgzipcompressiontotheresponseforus.

Ofcourseyoucanalsosendgzippedcontentasaclient.ThenyouwouldjustsettheContent-Encodingheadertogzipinyourrequest.Theclientwillautomaticallyapplycompressionforyou.

HTTPservletcontextcustomization

TheCitrusHTTPserverusesSpringapplicationcontextloadingonstartup.ForhighcustomizationsyoucanprovideacustomservletcontextfilewhichholdsallcustomconfigurationsasSpringbeansfortheserver.HereisasampleservletcontextwithsomebasicSpringMVCcomponentsandthecentralHttpMessageControllerwhichisresponsibleforhandlingincomingrequests(GET,PUT,DELETE,POST,etc.).

<beanid="citrusHandlerMapping"class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"

<beanid="citrusMethodHandlerAdapter"class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"<propertyname="messageConverters"><util:listid="converters"><beanclass="org.springframework.http.converter.StringHttpMessageConverter"><propertyname="supportedMediaTypes"><util:list>

CitrusReferenceGuide

257Http

Page 258: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<value>text/xml</value></util:list></property></bean></util:list></property></bean>

<beanid="citrusHttpMessageController"class="com.consol.citrus.http.controller.HttpMessageController"<propertyname="endpointAdapter"><beanclass="com.consol.citrus.endpoint.adapter.EmptyResponseEndpointAdapter"/></property></bean>

ThebeansaboveareresponsibleforproperHTTPserverconfiguration.Ingeneralyoudonotneedtoadjustthosebeans,butwehavethepossibilitytodosowhichgivesusagreatcustomizationandextensionpoints.TheimportantpartistheendpointadapterdefinitioninsidetheHttpMessageController.Onceaclientrequestwasacceptedtheadapterisresponsibleforgeneratingaproperresponsetotheclient.

YoucanaddthecustomservletcontextasfileresourcetotheCitrusHTTPservercomponent.Justusethecontext-config-locationattributeasfollows:

<citrus-http:serverid="helloHttpServer"port="8080"auto-start="true"context-config-location="classpath:com/consol/citrus/http/custom-servlet-context.xml"resource-base="src/it/resources"/>

CitrusReferenceGuide

258Http

Page 259: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WebSocketsupportTheWebSocketmessageprotocolbuildsontopofHttpstandardandbringsbidirectionalcommunicationtotheHttpclient-serverworld.CitrusisabletosendandreceivemessageswithWebSocketconnectionsasclientandserver.TheHttpserverimplementationisnowabletodefinemultipleWebSocketendpoints.ThenewCitrusWebSocketclientisabletopublishandconsumermessagesviabidirectionalWebSocketprotocol.

ThenewWebSocketsupportislocatedinthemodulecitrus-websocket.ThereforeweneedtoaddthismoduletoourprojectasdependencywhenweareabouttousetheWebSocketfeaturesinCitrus.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-websocket</artifactId><version>2.7</version></dependency>

AsCitrusprovidesacustomizedWebSocketconfigurationschemafortheSpringapplicationcontextconfigurationfileswehavetoaddnametothetoplevelbeanselement.Simplyincludethewebsocket-confignamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-websocket="http://www.citrusframework.org/schema/websocket/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/websocket/confighttp://www.citrusframework.org/schema/websocket/config/citrus-websocket-config.xsd"

[...]

</beans>

CitrusReferenceGuide

259HttpWebsockets

Page 260: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowourprojectisreadytousetheCitrusWebSocketsupport.FirstofallletussendamessageviaWebSocketconnectiontosomeserver.

WebSocketclient

OntheclientsideCitrusoffersaclientcomponentthatgoesdirectlytotheSpringbeanapplicationcontext.Theclientneedsaserverendpointuri.ThisisaWebSocketprotocolendpointuri.

<citrus-websocket:clientid="helloWebSocketClient"url="http://localhost:8080/hello"timeout="5000"/>

Theurldefinestheendpointtosendmessagesto.TheserverhastobeaWebSocketreadywebserverthatsupportsHttpconnectionupgradeforWebSocketprotocols.WebSocketbyitsnatureisanasynchronousbidirectionalprotocol.Thismeansthattheconnectionbetweenclientandserverremainsopenandbothserverandclientcansendandreceivemessages.SowhentheCitrusclientiswaitingforamessageweneedatimeoutthatstopstheasynchronouswaiting.Thereceivingtestactionandthetestcasewillfailwhensuchatimeoutisraised.

TheWebSocketclientwillautomaticallyopenaconnectiontotheserverandaskforaconnectionupgradetoWebSocketprotocol.Thishandshakeisdoneoncewhentheconnectiontotheserverisestablished.Afterthattheclientcanpushmessagestotheserverandontheothersidetheservercanpushmessagestotheclient.Nowletsfirstpushsomemessagestotheserver:

<sendendpoint="helloWebSocketClient"><message><payload><TestMessage><Text>HelloWebSocketServer</Text></TestMessage></payload></message></send>

Theconnectionhandshakeandtheconnectionupgradeisdoneautomaticallybytheclient.Afterthatthemessageispushedtotheserver.AsWebSocketisabidirectionalprotocolwecanalsoreceivemessagesontheWebSocketclient.Thesemessagesarepushedfromservertoallconnectedclients.

CitrusReferenceGuide

260HttpWebsockets

Page 261: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<receiveendpoint="helloWebSocketClient"><message><payload><TestMessage><Text>HelloWebSocketClient</Text></TestMessage></payload></message></receive>

Wejustusetheverysameclientendpointcomponentinamessagereceiveaction.Theclientwillwaitformessagesfromtheserverandoncereceivedperformthewellknownmessagevalidation.HereweexpectsomeXMLmessagepayload.ThiscompletestheclientsideasweareabletopushandconsumermessagesviaWebSocketconnections.

TipUptonowwehaveusedstaticWebSocketendpointURIsinourclientcomponentconfigurations.ThiscanbedonewithamorepowerfuldynamicendpointURIinWebSocketclient.SimilartotheendpointresolvingmechanisminSOAPyoucandynamicallysetthecalledendpointuriattestruntimethroughmessageheadervalues.BydefaultCitruswillcheckaspecificheaderentryfordynamicendpointURIwhichissimplydefinedforeachmessagesendingactioninsidethetest.

ThedynamicEndpointResolverbeanmustimplementtheEndpointUriResolverinterfaceinordertoresolvedynamicendpointurivalues.Citrusoffersadefaultimplementation,theDynamicEndpointUriResolver,whichusesaspecificmessageheaderforsettingdynamicendpointuri.Themessageheaderneedstospecifytheheadercitrus_endpoint_uriwithavalidrequesturi.

<header><elementname="citrus_endpoint_uri"value="ws://localhost:8080/customers/$customerId"</header>

Thespecificsendactionabovewillsenditsmessagetothedynamicendpoint(ws://localhost:8080/customers/$customerId)whichissetintheheadercitrus_endpoint_uri.

WebSocketserverendpoints

CitrusReferenceGuide

261HttpWebsockets

Page 262: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

OntheserversideCitrushasaHttpserverimplementationthatwecaneasilystartduringtestruntime.TheHttpserveracceptsconnectionsfromclientsandalsosupportsWebSocketupgradestrategies.ThismeansclientscanaskforaupgradetotheWebSocketstandard.InthishandshaketheserverwillupgradetheconnectiontoWebSocketandafterwardsclientandservercanexchangemessagesoverthisconnection.Thismeanstheconnectioniskeptaliveandmultiplemessagescanbeexchanged.LetsseehowWebSocketendpointsareaddedtoaHttpservercomponentinCitrus.

<citrus-websocket:serverid="helloHttpServer"port="8080"auto-start="true"resource-base="src/it/resources"><citrus-websocket:endpoints><citrus-websocket:endpointref="websocket1"/><citrus-websocket:endpointref="websocket2"/></citrus-websocket:endpoints></citrus-websocket:server>

<citrus-websocket:endpointid="websocket1"path="/test1"/><citrus-websocket:endpointid="websocket2"path="/test2"timeout="10000"/>

TheembeddedJettyWebSocketservercomponentinCitrusnowisabletodefinemultipleWebSocketendpoints.TheWebSocketendpointsmatchtoarequestpathontheserverandarereferencedbyauniqueid.EachWebSocketendpointcanfollowindividualtimeoutsettings.Inatestwecanusetheseendpointsdirectlytoreceivemessages.

<testcasename="httpWebSocketServerTest"><actions><receiveendpoint="websocket1"><message><data>[...]</data></message></receive>

<sendendpoint="websocket1"><message><data>[...]</data></message></send>

CitrusReferenceGuide

262HttpWebsockets

Page 263: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</actions></testcase>

Asyoucanseewereferencetheendpointidinbothreceiveandsendactions.EachWebSocketendpointholdsoneormoreopenconnectionstoitsclients.Eachmessagethatissentispushedtoallconnectedclients.EachclientcansendmessagestotheWebSocketendpoint.

TheWebSocketendpointcomponenthandlesconnectionhandshakesautomaticallyandcachesallopensessionsinmemory.Bydefaultallconnectedclientswillreceivethemessagespushedfromserver.Thisisdonecompletelybehindthescenes.TheCitrusserverisabletohandlemultipleWebSocketendpointswithdifferentclientsconnectedtoitatthesametime.ThisiswhywehavetochoosetheWebSocketendpointontheserverbyitsidentifierwhensendingandreceivingmessages.

WiththisWebSocketendpointswechangetheCitrusserverbehaviorsothatclientscanupgradetoWebSocketconnection.Nowwehaveabidirectionalconnectionwheretheservercanpushmessagestotheclientandviceversa.

WebSocketheaders

TheWebSocketstandarddefinessomedefaultheaderstouseduringconnectionupgrade.Theseheadersaremadeavailabletothetestcaseinbothdirections.CitruswillhandletheseheadervalueswithspecialcarewhenWebSocketsupportisactivatedonaserverorclient.NowWebSocketmessagescanalsobesplitintomultiplepieces.Eachmessagepartispushedseparatelytotheserverbutstillisconsideredtobeasinglemessagepayload.TheserverhastocollectandaggregateallmessagesuntilaspecialmessageheaderisLastissetinoneofthemessageparts.

TheCitrusWebSocketclientcanslicemessagesintoseveralparts.

<sendendpoint="webSocketClient"><messagetype="json"><data>["event":"client_message_1","timestamp":"citrus:currentDate()",</data></message><header><elementname="citrus_websocket_is_last"value="false"/>

CitrusReferenceGuide

263HttpWebsockets

Page 264: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</header></send>

<sleepmilliseconds="500"/>

<sendendpoint="webSocketClient"><messagetype="json"><data>"event":"client_message_2","timestamp":"citrus:currentDate()"]</data></message><header><elementname="citrus_websocket_is_last"value="true"/></header></send>

ThetestabovehastwoseparatesendoperationsbothsendingtoaWebSocketendpoint.Thefirstsendingactionsetstheheadercitrus_websocket_is_lasttofalsewhichindicatesthatthemessageisnotcompleteyet.The2ndsendactionpushestherestofthemessagetotheserverandsetthecitrus_websocket_is_lastheadertotrue.Nowtheserverisabletoaggregatethemessagepiecestoasinglemessagepayload.TheresultisavalidaJSONarraywithbotheventsinit.

["event":"client_message_1","timestamp":"2015-01-01","event":"client_message_2","timestamp":"2015-01-01"]

NowtheserverpartinCitrusisabletohandletheseslicedmessages,too.Theserverwillautomaticallyaggregatethosemessagepartsbeforepassingittothetestcaseforvalidation.

CitrusReferenceGuide

264HttpWebsockets

Page 265: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SOAPWebServicesSOAPWebServicesoverHTTPisawidelyusedcommunicationscenarioinmodernenterpriseapplications.ASOAPWebServiceclientispostingaSOAPrequestviaHTTPtoaserver.SOAPviaHTTPisasynchronousmessageprotocolbydefaultsotheclientiswaitingsynchronouslyfortheresponsemessage.CitrusprovidesbothSOAPclientandservercomponentsinordertomeetbothdirectionsofthisscenario.ThecomponentsusedareverysimilartotheHTTPcomponentsthatwerehavediscussedinthesectionsbefore.

NoteTheSOAPWebServicecomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldaddthemoduleasMavendependencytoyourprojectaccordingly.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-ws</artifactId><version>2.7</version></dependency>

InordertousetheSOAPWebServicesupportyouneedtoincludethespecificXMLconfigurationschemaprovidedbyCitrus.SeefollowingXMLdefinitiontofindouthowtoincludethecitrus-wsnamespace.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-ws="http://www.citrusframework.org/schema/ws/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/ws/confighttp://www.citrusframework.org/schema/ws/config/citrus-ws-config.xsd">

[...]

</beans>

CitrusReferenceGuide

265Soap

Page 266: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Nowyouarereadytousethecustomizedsoapconfigurationelements-allusingthecitrus-wsprefix-inyourSpringconfiguration.

SOAPclient

CitrusisabletoformaproperSOAPrequestinordertopassittotheserverviaHTTPandvalidatetherespectiveSOAPresponsemessage.LetusseehowamessageclientforSOAPlookslikeintheSpringconfiguration:

<citrus-ws:clientid="soapClient"request-url="http://localhost:8090/test"timeout="60000"/>

Theclientcomponentusestherequest-urlinordertoaccesstheserverresource.TheclientwillautomaticallybuildaproperSOAPrequestmessageincludingtheSOAPenvelope,SOAPheaderandthemessagepayloadasSOAPbody.ThismeansthatyouasatesterdonotcareaboutSOAPenvelopespecificlogicinthetestcase.TheclientendpointcomponentsavesthesynchronousSOAPresponsesothetestcasecanreceivethismessagewithanormalreceivetestaction.

IndetailyouasatesterjustsendandreceiveusingthesameclientendpointreferencejustasyouwoulddowithasynchronousJMSorchannelcommunication.IncasenoresponsemessageisavailableintimeaccordingtothetimeoutsettingsCitrusraisesatimeouterrorandthetestwillfail.

ImportantTheSOAPclientcomponentusesaSoapMessageFactoryimplementationinordertocreatetheSOAPmessages.ThisisaSpringbeanaddedtotheCitrusSpringapplicationcontext.Springoffersseveralreferenceimplementationsasmessagefactoriessoyoucanchooseoneofthem(e.g.forSOAP1.1or1.2implementations).

<!--DefaultSOAPMessageFactory(SOAP1.1)--><beanid="messageFactory"class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

<!--SOAP1.2MessageFactory--><beanid="soap12MessageFactory"class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"<propertyname="soapVersion"><util:constantstatic-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/></property></bean>

CitrusReferenceGuide

266Soap

Page 267: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

BydefaultCitruswillsearchforabeanwithid'messageFactory'.IncaseyouintendtousedifferentidentifiersyouneedtotelltheSOAPclientcomponentwhichmessagefactorytouse:

<citrus-ws:clientid="soapClient"request-url="http://localhost:8090/test"message-factory="soap12MessageFactory"/>

TipUptonowwehaveusedastaticendpointrequesturlfortheSOAPmessagesender.Besidesthatwecanusedynamicendpointuriinconfiguration.Wejustuseanendpointuriresolverinsteadofthestaticrequesturllikethis:

<citrus-ws:clientid="soapClient"endpoint-resolver="dynamicEndpointResolver"message-factory="soap12MessageFactory"/>

<beanid="dynamicEndpointResolver"class="com.consol.citrus.endpoint.resolver.DynamicEndpointUriResolver"/>

ThedynamicEndpointResolverbeanmustimplementtheEndpointUriResolverinterfaceinordertoresolvedynamicendpointurivalues.Citrusoffersadefaultimplementation,theDynamicEndpointUriResolver,whichusesaspecificmessageheaderforsettingthedynamicendpointuriforeachmessage.Themessageheaderneedstospecifytheheadercitrus_endpoint_uriwithavalidrequesturi.Justlikethis:

<header><elementname="citrus_endpoint_uri"value="http://localhost:$port/$context"/></header>

Asyoucanseeyoucanusedynamictestvariablestheninordertobuildtherequesturitouse.TheSOAPclientevaluatestheendpointuriheaderandsendsthemessagetothisserverresource.Youcanuseadifferenturivaluethenindifferenttestcasesandsendactions.

SOAPclientinterceptors

Theclientcomponentisabletoaddcustominterceptorsthatparticipateintherequest/responseprocessing.Theinterceptorsneedtoimplementthecommoninterfaceorg.springframework.ws.client.support.interceptor.ClientInterceptor.

CitrusReferenceGuide

267Soap

Page 268: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<citrus-ws:clientid="secureSoapClient"request-url="http://localhost:8080/services/ws/todolist"interceptors="clientInterceptors"/>

<util:listid="clientInterceptors"><beanclass="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor"><propertyname="securementActions"value="TimestampUsernameToken"/><propertyname="securementUsername"value="admin"/><propertyname="securementPassword"value="secret"/></bean><beanclass="com.consol.citrus.ws.interceptor.LoggingClientInterceptor"/></util:list>

ThesampleaboveaddsWss4JWsSecurityinterceptorsinordertoaddsecurityconstraintstotherequestmessages.

NoteWhencustomizingtheinterceptorchainalldefaultinterceptors(likelogginginterceptor)arelost.Youneedtoaddtheseinterceptorsexlicitlyasshownwiththecom.consol.citrus.ws.interceptor.LoggingClientInterceptorwhichisabletologrequest/responsemessagesduringcommunication.

SOAPserver

Everyclientneedaservertotalkto.WhenreceivingSOAPmessageswerequireawebserverinstancelisteningonaport.CitrusisusinganembeddedJettyserverinstanceincombinationwiththeSpringWebServiceAPIinordertoacceptSOAPrequestcallsasaserver.SeehowtheCitrusSOAPserverisconfiguredintheSpringconfiguration.

<citrus-ws:serverid="helloSoapServer"port="8080"auto-start="true"resource-base="src/it/resources"/>

Theservercomponentisabletostartautomaticallywhenapplicationstartsup.Intheexampleabovetheserverislisteningforrequestsonport8080.ThissetupusesthestandardconnectorconfigurationfortheJettyserver.FordetailedcustomizationtheCitrusJettyserverconfigurationalsosupportsexplicitconnectorconfigurations(@connectorand@connectorsattributes).FormoreinformationpleaseseetheJettyconnectordocumentation.

Testcasesinteractwiththisserverinstanceviamessagechannelsbydefault.Theservercomponentprovidesaninboundchannelthatholdsincomingrequestmessages.Thetestcasecanreceivethoserequestsfromthechannelwithanormalreceivetest

CitrusReferenceGuide

268Soap

Page 269: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

action.InasecondstepthetestcasecanprovideasynchronousresponsemessageasreplywhichwillbeautomaticallysentbacktothecallingSOAPclientasresponse.

Thefigureaboveshowsthebasicsetupwithinboundchannelandreplychannel.Youasatestershouldnotworryaboutthistomuch.Bydefaultyouasatesterjustusetheserverassynchronousendpointinyourtestcase.Thismeansthatyousimplyreceiveamessagefromtheserverandsendaresponseback.

<testcasename="soapServerTest"><actions><receiveendpoint="helloSoapServer"><message><data>[...]</data></message></receive>

<sendendpoint="helloSoapServer"><message><data>[...]</data></message></send></actions></testcase>

Asyoucanseewereferencetheserveridinbothreceiveandsendactions.TheCitrusserverinstancewillautomaticallysendtheresponsebacktothecallingclient.InmostcasesthisiswhatyouneedtosimulateaSOAPserverinstanceinCitrus.Ofcoursewehavesomemorecustomizationpossibilitiesthatwewillgooverlateron.ThiscustomizationsareoptionalsoyoucanalsoskipthenextdescriptiononendpointadaptersifyouarehappywithjustwhatyouhavelearnedabouttheSOAPservercomponentinCitrus.

JustliketheHTTPservercomponenttheSOAPservercomponentbydefaultusesthechannelendpointadapterinordertoforwardallincomingrequeststoaninmemorymessagechannel.Thisisdonecompletelybehindthescenes.TheCitrusconfiguration

CitrusReferenceGuide

269Soap

Page 270: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

hasbecomealoteasierheresoyoudonothavetoconfigurethisbydefault.Whennothingelseissetthetestcasedoesnotworryaboutthatsettingsontheserverandjustusestheserveridreferenceassynchronousendpoint.

TipThedefaultchannelendpointadapterautomaticallycreatesaninboundmessagechannelwhereincomingmessagesarestoredtointernally.Soifyouneedtocleanupaserverthathasalreadystoredsomeincomingmessagesyoucandothiseasilybypurgingtheinternalmessagechannel.ThemessagechannelfollowsanamingconventionserverName.inboundwhereserverNameistheSpringbeannameoftheCitrusserverendpointcomponent.Ifyoupurgethisinternalchannelinabeforetestnatureyouaresurethatobsoletemessagesonaserverinstancegetpurgedbeforeeachtestisexecuted.

HoweverwedonotwanttoloosethegreatextendabilityandcustomizingcapabilitiesoftheCitrusservercomponent.ThisiswhyyoucanoptionallydefinetheendpointadapterimplementationusedbytheCitrusSOAPserver.Weprovideseveralmessageendpointadapterimplementationsfordifferentsimulationstrategies.WiththeseendpointadaptersyoushouldbeabletogenerateproperSOAPresponsemessagesfortheclientinvariousways.Beforewehaveacloserlookatthedifferentadapterimplementationswewanttoshowhowyoucansetacustomendpointadapterontheservercomponent.

<citrus-ws:serverid="helloSoapServer"port="8080"auto-start="true"endpoint-adapter="emptyResponseEndpointAdapter"resource-base="src/it/resources"/>

<citrus:empty-response-adapterid="emptyResponseEndpointAdapter"/>

WiththisendpointadapterconfigurationabovewechangetheCitrusserverbehaviorfromscratch.NowtheserverautomaticallysendsbackanemptySOAPresponsemessageeverytime.SettingacustomendpointadapterimplementationwithcustomlogiciseasyasdefiningacustomendpointadapterSpringbeanandreferenceitintheserverattribute.Youcanreadmoreaboutendpointadaptersinendpoint-adapter.

SOAPsendandreceive

Citrusprovidestestactionsforsendingandreceivingmessagesofallkind.Differentmessagecontentanddifferentmessagetransportsareavailabletothesesendandreceiveactions.WhenusingSOAPmessagetransportwemightneedtosetspecial

CitrusReferenceGuide

270Soap

Page 271: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

informationonthatmessages.ThesearespecialSOAPheaders,SOAPfaultsandsoon.SowehavecreatedaspecialSOAPnamespaceforallyourSOAPrelatedsendandreceiveoperationsinaXMLDSLtest:

<spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:spring="http://www.springframework.org/schema/beans"xmlns:ws="http://www.citrusframework.org/schema/ws/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsdhttp://www.citrusframework.org/schema/ws/testcasehttp://www.citrusframework.org/schema/ws/testcase/citrus-ws-testcase.xsd">

Onceyouhaveaddedthewsnamespacefromabovetoyourtestcaseyouarereadytousespecialsendandreceiveoperationsinthetest.

XMLDSL

<ws:sendendpoint="soapClient"soap-action="MySoapService/sayHello"><message>[...]</message></ws:send>

<ws:receiveendpoint="soapServer"soap-action="MySoapService/sayHello"><message>[...]</message></ws:receive>

Thespecialnamespacecontainsfollowingelements:

send:SpecialsendoperationforsendingoutSOAPmessagecontent.receive:SpecialreceiveoperationforvalidatingSOAPmessagecontent.send-fault:SpecialsendoperationforsendingoutSOAPfaultmessagecontent.assert-fault:SpecialassertionoperationforexpectingaSOAPfaultmessageasresponse.

ThespecialSOAPrelatedsendandreceiveactionscancoexistwithnormalCitrusactions.Infactyoucanmixthoseactiontypesasyouwantinsideofatestcase.AlltestactionsthatworkwithSOAPmessagecontentonclientandserversideshouldusethisspecialnamespace.

CitrusReferenceGuide

271Soap

Page 272: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

InJavaDSLwehavesomethingsimilartothat.TheJavaDSLprovidesspecialSOAPrelatedfeatureswhencallingthesoap()method.WithafluentAPIyouareabletothensendandreceiveSOAPmessagecontentasclientandserver.

JavaDSL

@CitrusTestpublicvoidsoapTest()

soap().client("soapClient").send().soapAction("MySoapService/sayHello").payload("...");

soap().client("soapClient").receive().payload("...");

InthefollowingsectionstheSOAPrelatedcapabilitiesarediscussedinmoredetail.

SOAPheaders

SOAPdefinesseveralheadervariationsthatwediscussinthefollowingsections.FirstofallwedealwiththespecialSOAPactionheader.IncaseweneedtosetthisSOAPactionheaderwesimplyneedtousethespecialsoap-actionattributeinourtest.ThespecialheaderkeyincombinationwithaunderlyingSOAPclientendpointcomponentconstructstheSOAPactionintheSOAPmessage.

XMLDSL

<ws:sendendpoint="soapClient"soap-action="MySoapService/sayHello"><message>[...]</message></ws:send>

<ws:receiveendpoint="soapServer"soap-action="MySoapService/sayHello"><message>[...]</message></ws:receive>

JavaDSL

CitrusReferenceGuide

272Soap

Page 273: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidsoapActionTest()

soap().client("soapClient").send().soapAction("MySoapService/sayHello").payload("...");

soap().server("soapClient").receive().soapAction("MySoapService/sayHello").payload("...");

TheSOAPactionheaderisaddedtothemessagebeforesendingandvalidatedwhenusedinareceiveoperation.

NoteThesoap-actionattributeisdefinedinthespecialSOAPnamespaceinCitrus.WerecommendtousethisnamespaceforallyoursendandreceiveoperationsthatdealwithSOAPmessagecontent.HoweveryoucanalsosetthespecialSOAPactionheaderwhennotusingthespecialSOAPnamespace:Justsetthisheaderinyourtestaction:

<header><elementname="citrus_soap_action"value="sayHello"/></header>

SecondlyaSOAPmessageisabletocontaincustomizedSOAPheaders.Thesearekey-valuepairswherethekeyisaqualifiedname(QName)andthevalueanormalStringvalue.

<header><elementname="http://www.consol.de/sayHelloh1:Operation"value="sayHello"/><elementname="http://www.consol.de/sayHelloh1:Request"value="HelloRequest"/></header>

ThekeyisdefinedasqualifiedQNamecharactersequencewhichhasamandatoryXMLnamespaceandaprefixalongwithaheadername.LastnotleastaSOAPheadercancontainwholeXMLfragmentvalues.ThenextexampleshowshowtosettheseXMLfragmentsasSOAPheaderinCitrus:

<header><data><![CDATA[<Userxmlns="http://www.consol.de/schemas/sayHello">

CitrusReferenceGuide

273Soap

Page 274: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<UserId>123456789</UserId><Handshake>S123456789</Handshake></User>]]></data></header>

YoucanalsouseexternalfileresourcestosetthisSOAPheaderXMLfragmentasshowninthislastexamplecode:

<header><resourcefile="classpath:request-soap-header.xml"/></header>

ThiscompletestheSOAPheaderpossibilitiesforsendingSOAPmessageswithCitrus.OfcourseyoucanalsousethesevariantsinSOAPmessageheadervalidation.YoudefineexpectedSOAPheaders,SOAPactionandXMLfragmentsandCitruswillmatchincomingrequesttothat.Justusecitrus_soap_actionheaderkeyinyourreceivingmessageactionandyouvalidatethisSOAPheaderaccordingly.

WhenvalidatingSOAPheaderXMLfragmentsyouneedtodefinethewholeXMLheaderfragmentasexpectedheaderdatalikethis:

<receiveendpoint="soapMessageEndpoint"><message><data><![CDATA[<ResponseMessagexmlns="http://citrusframework.org/schema"><resultCode>OK</resultCode></ResponseMessage>]]></data></message><header><data><![CDATA[<SOAP-ENV:Headerxmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><customHeaderxmlns="http://citrusframework.org/headerschema"><correlationId>$correlationId</correlationId><applicationId>$applicationId</applicationId><trackingId>$trackingId</trackingId><serviceId>$serviceId</serviceId><interfaceVersion>1.0</interfaceVersion><timestamp>@ignore@</timestamp></customHeader>

CitrusReferenceGuide

274Soap

Page 275: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</SOAP-ENV:Header>]]></data><elementname="citrus_soap_action"value="doResponse"/></header></receive>

AsyoucanseetheSOAPXMLheadervalidationcancombineheaderelementandXMLfragmentvalidation.ThisisalsolikelytobeusedwhendealingwithWS-Securitymessageheaders.

SOAPHTTPmimeheaders

BesidestheSOAPspecificheaderelementstheHTTPmimeheaders(e.g.Content-Type,Content-Length,Authorization)mightbecandidatesforvalidation,too.WhenusingHTTPastransportlayertheSOAPmessagemaydefinethosemimeheaders.Thetesterisabletosendandvalidatetheseheadersinsidethetestcase,althoughtheseHTTPheadersarelocatedoutsideoftheSOAPenvelope.LetusfirstofallspeakaboutvalidatingtheHTTPmimeheaders.Thisfeatureisnotenabledbydefault.WehaveenablethisinourSOAPserverconfiguration.

<citrus-ws:serverid="helloSoapServer"port="8080"auto-start="true"handle-mime-headers="true"resource-base="src/it/resources"/>

WiththisconfigurationCitruswillhandleallavailablemimeheadersandpassthosetothetestcasefornormalheadervalidation.

<ws:receiveendpoint="helloSoapServer"><message><payload><SoapMessageRequestxmlns="http://www.consol.de/schemas/sample.xsd"><Operation>Validatemimeheaders</Operation></SoapMessageRequest></payload></message><header><elementname="Content-Type"value="text/xml;charset=utf-8"/></header></ws:receive>

CitrusReferenceGuide

275Soap

Page 276: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThevalidationoftheseHTTPmimeheadersisasusualnowthatwehaveenabledthemimeheaderhandlingintheSOAPserver.ThetransportHTTPheadersareavailableintheheaderjustlikethenormalSOAPheaderelementsdo.Soyoucanvalidatetheheadersasusual.

SomuchforreceivingandvalidatingHTTPmimemessageheaderswithSOAPcommunication.Nowwewanttosendspecialmimeheadersonclientside.Weoverwriteoraddmimeheaderstooursendingaction.Wemarksomeheaderswithfollowingprefix"citrushttp".ThistellstheSOAPclienttoaddtheseheaderstotheHTTPheadersectionoutsidetheSOAPenvelope.KeepinmindthatheaderelementswithoutthisprefixgorightintotheSOAPheadersectionbydefault.

<ws:sendendpoint="soapClient">[...]<header><elementname="citrus_http_operation"value="foo"/></header>[...]</ws:send>

ThelistingabovedefinesaHTTPmimeheaderoperation.TheheaderprefixcitrushttpiscutoffbeforetheheadergoesintotheHTTPheadersection.Withthisfeaturewecandecidewhereexactlyourheaderinformationislocatedinourresultingclientmessage.

SOAPEnvelopehandling

BydefaultCitruswillremovetheSOAPenvelopeinmessageconverter.FollowingfromthattheCitrustestcaseisindependentfromSOAPmessageformatsandisnotbotheredwithhandlingofSOAPenvelopeatall.ThisisgreatinmostcasesbutsometimesitmightbemandatorytoalsoseethewholeSOAPenvelopeinsidethetestcasereceiveaction.ThereforeyoucankeeptheSOAPenvelopeforincomingmessagesbyconfigurationontheSOAPserverside.

<citrus-ws:serverid="helloSoapServer"port="8080"auto-start="true"keep-soap-envelope="true"/>

WiththisconfigurationCitruswillhandleallavailablemimeheadersandpassthosetothetestcasefornormalheadervalidation.

CitrusReferenceGuide

276Soap

Page 277: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ws:receiveendpoint="helloSoapServer"><message><payload><SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><SoapMessageRequestxmlns="http://www.consol.de/schemas/sample.xsd"><Operation>Validatemimeheaders</Operation></SoapMessageRequest></SOAP-ENV:Body></SOAP-ENV:Envelope></payload></message></ws:receive>

SonowyouareabletovalidatethewholeSOAPenvelopeasis.Thismightbeofinterestinveryspecialcases.AsmentionedbydefaulttheCitrusserverwillautomaticallyremovetheSOAPenvelopeandtranslatetheSOAPbodytothemessagepayloadforstraightforwardvalidationinsidethetestcases.

SOAPserverinterceptors

TheCitrusSOAPserversupportstheconceptofinterceptorsinordertoaddcustomlogictotherequest/responseprocessingsteps.Theinterceptorsneedtoimplementacommoninterface:org.springframework.ws.server.EndpointInterceptor.Weareabletocustomizetheinterceptorchainontheservercomponentasfollows:

<citrus-ws:serverid="secureSoapServer"port="8080"auto-start="true"interceptors="serverInterceptors"/>

<util:listid="serverInterceptors"><beanclass="com.consol.citrus.ws.interceptor.SoapMustUnderstandEndpointInterceptor"><propertyname="acceptedHeaders"><list><value>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsdSecurity</list></property></bean><beanclass="com.consol.citrus.ws.interceptor.LoggingEndpointInterceptor"/><beanclass="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor"><propertyname="validationActions"value="TimestampUsernameToken"/><propertyname="validationCallbackHandler"><beanid="passwordCallbackHandler"class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler"<propertyname="usersMap">

CitrusReferenceGuide

277Soap

Page 278: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<map><entrykey="admin"value="secret"/></map></property></bean></property></bean></util:list>

ThecustominterceptorsareusedtoenableWsSecurityfeaturesonthesoapservercomponentviaWss4J.

NoteWhencustomizingtheinterceptorchainofthesoapservercomponentalldefaultinterceptors(likelogginginterceptors)arelost.Youcanseethatwehadtoaddthecom.consol.citrus.ws.interceptor.LoggingEndpointInterceptorexplicitlyinordertologrequest/responsemessagesfortheservercommunication.

SOAP1.2

BydefaultCitruscomponentsuseSOAP1.1version.FortunatelySOAP1.2issupportedsameway.AswealreadymentionedbeforetheCitrusSOAPcomponentsdouseaSOAPmessagefactoryforcreatingmessagesinSOAPformat.

<!--SOAP1.1MessageFactory--><beanid="soapMessageFactory"class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"<propertyname="soapVersion"><util:constantstatic-field="org.springframework.ws.soap.SoapVersion.SOAP_11"/></property></bean>

<!--SOAP1.2MessageFactory--><beanid="soap12MessageFactory"class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"<propertyname="soapVersion"><util:constantstatic-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/></property></bean>

AsyoucanseetheSOAPmessagefactorycaneithercreateSOAP1.1orSOAP1.2messages.ThisishowCitruscancreatebothSOAP1.1andSOAP1.2messages.Ofcourseyoucanhavemultiplemessagefactoriesconfiguredinyourproject.JustsetthemessagefactoryonaWebServiceclientorservercomponentinordertodefinewhichversionshouldbeused.

CitrusReferenceGuide

278Soap

Page 279: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<citrus-ws:clientid="soap12Client"request-url="http://localhost:8080/echo"message-factory="soap12MessageFactory"timeout="1000"/>

<citrus-ws:serverid="soap12Server"port="8080"auto-start="true"root-parent-context="true"message-factory="soap12MessageFactory"/>

BydefaultCitruscomponentsdoconnectwithamessagefactorycalledmessageFactorynomatterwhatSOAPversionthisfactoryisusing.

SOAPfaults

SOAPfaultsdescribeafailedcommunicationinSOAPWebServicesworld.CitrusisabletosendandreceiveSOAPfaultmessages.OnserversideCitruscansimulateSOAPfaultswithfault-code,fault-reason,fault-actorandfault-detail.OnclientsideCitrusisabletohandleandvalidateSOAPfaultsinresponsemessages.ThenextsectiondescribeshowtodealwithSOAPfaultsinCitrus.

SendSOAPfaults

AsCitrussimulatesSOAPserverendpointsyoualsoneedtothinkaboutsendingaSOAPfaulttothecallingclient.IncaseCitrusreceivesaSOAPrequestasaserveryoucanrespondwithaproperSOAPfaultifnecessary.

Pleasekeepinmindthatweusethecitrus-wsextensionforsendingSOAPfaultsinourtestcase,asshowninthisverysimpleexample:

XMLDSL

<ws:send-faultendpoint="helloSoapServer"><ws:fault><ws:fault-code>http://www.citrusframework.org/faultscitrus:TEC-1000</ws:fault-code><ws:fault-string>Invalidrequest</ws:fault-string><ws:fault-actor>SERVER</ws:fault-actor><ws:fault-detail><![CDATA[<FaultDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><MessageId>$messageId</MessageId><CorrelationId>$correlationId</CorrelationId><ErrorCode>TEC-1000</ErrorCode>

CitrusReferenceGuide

279Soap

Page 280: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<Text>Invalidrequest</Text></FaultDetail>]]></ws:fault-detail></ws:fault><ws:header><ws:elementname="citrus_soap_action"value="sayHello"/></ws:header></ws:send-fault>

TheexamplegeneratesasimpleSOAPfaultthatissentbacktothecallingclient.Thefault-actorandthefault-detailelementsareoptional.SamewiththesoapactiondeclaredinthespecialCitrusheadercitrus_soap_action.Inthesampleabovethefault-detaildataisplacedinlineasXMLdata.Asanalternativetothatyoucanalsosetthefault-detailviaexternalfileresource.JustusethefileattributeasfaultdetailinsteadoftheinlineCDATAdefinition.

XMLDSL

<ws:send-faultendpoint="helloSoapServer"><ws:fault><ws:fault-code>http://www.citrusframework.org/faultscitrus:TEC-1000</ws:fault-code><ws:fault-string>Invalidrequest</ws:fault-string><ws:fault-actor>SERVER</ws:fault-actor><ws:fault-detailfile="classpath:myFaultDetail.xml"/></ws:fault><ws:header><ws:elementname="citrus_soap_action"value="sayHello"/></ws:header></ws:send-fault>

ThegeneratedSOAPfaultlookslikefollows:

HTTP/1.1500InternalServerErrorAccept:text/xml,text/html,image/gif,image/jpeg,*;q=.2,*/*;q=.2SOAPAction:"sayHello"Content-Type:text/xml;charset=utf-8Content-Length:680Server:Jetty(7.0.0.pre5)

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><SOAP-ENV:Fault>

CitrusReferenceGuide

280Soap

Page 281: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<faultcodexmlns:citrus="http://www.citrusframework.org/faults">citrus:TEC-1000</<faultstringxml:lang="en">Invalidrequest</faultstring><detail><FaultDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><MessageId>9277832563</MessageId><CorrelationId>4346806225</CorrelationId><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

ImportantNoticethatthesendactionusesaspecialXMLnamespace(ws:send).ThiswsnamespacebelongstotheCitrusWebServiceextensionandaddsSOAPspecificfeaturestothenormalsendaction.Whenyouusesuchwsextensionsyouneedtodefinetheadditionalnamespaceinyourtestcase.Thisisusuallydoneintheroot<spring:beans>elementwherewesimplydeclarethecitrus-wsspecificnamespacelikefollows.```xml

###ReceiveSOAPfaults

IncaseyoureceiveSOAPresponsemessagesasaclientendpointyoumayneedtohandleandvalidateSOAPfaultsinerrorsituations.CitruscanvalidateSOAPfaultswithfault-code,fault-actor,fault-stringandfault-detailvalues.

AsaclientwesendoutarequestandreceiveaSOAPfaultasresponse.BydefaulttheclientsendingactioninCitrusthrowsaspecificexceptionwhentheSOAPresponseisaSOAPfaultelement.Thisexceptioniscalled***SoapFaultClientException***comingfromtheSpringAPI.YouasatestercanassertthiskindofexceptioninatestcaseinordertoexpecttheSOAPerror.

**XMLDSL**

```xml<assertclass="org.springframework.ws.soap.client.SoapFaultClientException"><sendendpoint="soapClient"><message><payload><SoapFaultForcingRequestxmlns="http://www.consol.de/schemas/soap"><Message>Thisisinvalid</Message></SoapFaultForcingRequest></payload></message></send></assert>

CitrusReferenceGuide

281Soap

Page 282: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheSOAPmessagesendingactionissurroundedbyasimpleassertaction.TheassertedexceptionclassistheSoapFaultClientExceptionthatwehavementionedbefore.Thismeansthatthetestexpectstheexceptiontobethrownduringthecommunication.Incasetheexceptionismissingthetestisfails.

SofarwehaveusedtheCitruscorecapabilitiesofassertinganexception.ThisbasicassertiontestactionisnotabletoofferdirectaccesstotheSOAPfault-codeandfault-stringvaluesforvalidation.ThebasicassertactionsimplyhasnoaccesstotheactualSOAPfaultelements.Fortunatelywecanusethecitrus-wsnamespaceagainwhichoffersaspecialassertactionimplementationespeciallydesignedforSOAPfaultsinthiscase.

XMLDSL

<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest">fault-actor="SERVER"><ws:when><sendendpoint="soapClient"><message><payload><SoapFaultForcingRequestxmlns="http://www.consol.de/schemas/soap"><Message>Thisisinvalid</Message></SoapFaultForcingRequest></payload></message></send></ws:when></ws:assert-fault>

ThespecialassertactionoffersseveralattributestovalidatetheexpectedSOAPfault.Namelytheseare"fault-code","fault-string"and"fault-actor".Thefault-codeisdefinedasaQNamestringandismandatoryforthevalidation.Thefaultassertionalsosupportstestvariablereplacementasusual(e.g.fault-code="http://www.citrusframework.org/faults$myFaultCode").

ThetimeyouuseSOAPfaultvalidationyouneedtotellCitrushowtovalidatetheSOAPfaults.CitrusneedsaninstanceofaSoapFaultValitatorthatweneedtoaddtotheSpringapplicationcontext.BydefaultCitrusissearchingforabeanwiththeid'soapFaultValidator'.

<beanid="soapFaultValidator"class="com.consol.citrus.ws.validation.SimpleSoapAttachmentValidator"

CitrusReferenceGuide

282Soap

Page 283: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusoffersseveralreferenceimplementationsfortheseSOAPfaultvalidators.Theseare:

com.consol.citrus.ws.validation.SimpleSoapAttachmentValidatorcom.consol.citrus.ws.validation.SimpleSoapFaultValidatorcom.consol.citrus.ws.validation.XmlSoapFaultValidator

PleaseseetheAPIdocumentationfordetailsontheavailablereferenceimplementations.OfcourseyoucanalsodefineyourownSOAPvalidatorlogic(wouldbegreatifyoucouldshareyourideas!).Inthetestcaseyoucanexplicitlychoosethevalidatortouse:

XMLDSL

<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest"fault-validator="mySpecialSoapFaultValidator">[...]</ws:assert-fault>

ImportantAnotherimportantthingtonoticewhenassertingSOAPfaultsisthefact,thatCitrusneedstohaveaSoapMessageFactoryavailableintheSpringapplicationcontext.IfyoudealwithSOAPmessagingingeneralyouwillalreadyhavesuchabeaninthecontext.

<beanid="messageFactory"class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

ChooseoneofSpring'sreferenceimplementationsorsomeotherimplementationasSOAPmessagefactory.Citruswillsearchforabeanwithid'messageFactory'bydefault.IncaseyouhaveotherbeanswithdifferentidentifierspleasechoosethemessageFactoryinthetestcaseassertaction:

XMLDSL

<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest"message-factory="mySpecialMessageFactory">[...]</ws:assert-fault>

CitrusReferenceGuide

283Soap

Page 284: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ImportantNoticethewsspecificnamespacethatbelongstotheCitrusWebServiceextensions.Asthews:assertactionusesSOAPspecificfeaturesweneedtorefertothecitrus-wsnamespace.Youcanfindthenamespacedeclarationintherootelementinyourtestcase.```xml

CitrusisalsoabletovalidateSOAPfaultdetails.Seethefollowingexampleforunderstandinghowtodoit:

**XMLDSL**

```xml<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest"><ws:fault-detail><![CDATA[<FaultDetailxmlns="http://www.consol.de/schemas/soap"><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail>]]></ws:fault-detail><ws:when><sendendpoint="soapClient"><message><payload><SoapFaultForcingRequestxmlns="http://www.consol.de/schemas/soap"><Message>Thisisinvalid</Message></SoapFaultForcingRequest></payload></message></send></ws:when></ws:assert-fault>

TheexpectedSOAPfaultdetailcontentissimplyaddedtothews:assertaction.TheSoapFaultValidatorimplementationdefinedintheSpringapplicationcontextisresponsibleforcheckingtheSOAPfaultdetailwithvalidationalgorithm.Thevalidatorimplementationchecksthedetailcontenttomeettheexpectedtemplate.CitrusprovidessomedefaultSoapFaultValidatorimplementations.SupportedalgorithmsarepureStringcomparison(com.consol.citrus.ws.validation.SimpleSoapFaultValidator)aswellasXMLtreewalk-through(com.consol.citrus.ws.validation.XmlSoapFaultValidator).

CitrusReferenceGuide

284Soap

Page 285: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WhenusingtheXMLvalidationalgorithmyouhavethecompletepowerasknownfromnormalmessagevalidationinreceiveactions.Thisincludesschemavalidationorignoringelementsforinstance.Onthefault-detailelementyouareabletoaddsomevalidationsettingssuchasschema-validation=enabled/disabled,customschema-repositoryandsoon.

XMLDSL

<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest"><ws:fault-detailschema-validation="false"><![CDATA[<FaultDetailxmlns="http://www.consol.de/schemas/soap"><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail>]]></ws:fault-detail><ws:when><sendendpoint="soapClient">[...]</send></ws:when></ws:assert-fault>

PleaseseealsotheCitrusAPIdocumentationforavailablevalidatorimplementationsandvalidationalgorithms.

SofarwehaveusedassertactionwrapperinordertocatchSOAPfaultexceptionsandvalidatetheSOAPfaultcontent.NowwehaveanalternativewayofhandlingSOAPfaultsinCitrus.WithexceptionsthesendactionabortsandwedonothaveareceiveactionfortheSOAPfault.ThismightbeinadequateifweneedtovalidatetheSOAPmessagecontent(SOAPHeaderandSOAPBody)comingwiththeSOAPfault.Thereforethewebservicemessagesendercomponentoffersseveralfaultstrategyoptions.InthefollowingwediscussthepropagationofSOAPfaultasmessagestothereceiveactionaswewoulddowithnormalSOAPmessages.

<citrus-ws:clientid="soapClient"request-url="http://localhost:8090/test"fault-strategy="propagateError"/>

CitrusReferenceGuide

285Soap

Page 286: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WehaveconfiguredafaultstrategypropagateErrorsothemessagesenderwillnotraiseclientexceptionsbutinformthereceiveactionwithSOAPfaultmessagecontents.Bydefaultthefaultstrategyraisesclientexceptions(fault-strategy=throwsException).

Sonowthatwedonotraiseexceptionswecanleaveouttheassertactionwrapperinourtest.InsteadwesimplyuseareceiveactionandvalidatetheSOAPfaultlikethis.

<sendendpoint="soapClient"><message><payload><SoapFaultForcingRequestxmlns="http://www.consol.de/schemas/sample.xsd"><Message>Thisisinvalid</Message></SoapFaultForcingRequest></payload></message></send>

<receiveendpoint="soapClient"timeout="5000"><message><payload><SOAP-ENV:Faultxmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><faultcodexmlns:CITRUS="http://citrus.org/soap">CITRUS:$soapFaultCode</faultcode<faultstringxml:lang="en">$soapFaultString</faultstring></SOAP-ENV:Fault></payload></message></receive>

SochoosethepreferredwayofhandlingSOAPfaultseitherbyassertingclientexceptionsorpropagatingfaultmessagestothereceiveactiononaSOAPclient.

MultipleSOAPfaultdetails

SOAPfaultmessagescanholdmultipleSOAPfaultdetailelements.IntheprevioussectionswehaveusedSOAPfaultdetailsinsendingandreceivingactionsassingleelement.InordertomeettheSOAPspecificationCitrusisalsoabletohandlemultipleSOAPfaultdetailelementsinamessage.Youjustusemultiplefault-detailelementsinyourtestactionlikethis:

<ws:send-faultendpoint="helloSoapServer"><ws:fault><ws:fault-code>http://www.citrusframework.org/faultscitrus:TEC-1000</ws:fault-code><ws:fault-string>Invalidrequest</ws:fault-string><ws:fault-actor>SERVER</ws:fault-actor>

CitrusReferenceGuide

286Soap

Page 287: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ws:fault-detail><![CDATA[<FaultDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><MessageId>$messageId</MessageId><CorrelationId>$correlationId</CorrelationId><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail>]]></ws:fault-detail><ws:fault-detail><![CDATA[<ErrorDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><ErrorCode>TEC-1000</ErrorCode></ErrorDetail>]]></ws:fault-detail></ws:fault><ws:header><ws:elementname="citrus_soap_action"value="sayHello"/></ws:header></ws:send-fault>

ThiswillresultinfollowingSOAPenvelopemessage:

HTTP/1.1500InternalServerErrorAccept:text/xml,text/html,image/gif,image/jpeg,*;q=.2,*/*;q=.2SOAPAction:"sayHello"Content-Type:text/xml;charset=utf-8Content-Length:680Server:Jetty(7.0.0.pre5)

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><SOAP-ENV:Fault><faultcodexmlns:citrus="http://www.citrusframework.org/faults">citrus:TEC-1000</<faultstringxml:lang="en">Invalidrequest</faultstring><detail><FaultDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><MessageId>9277832563</MessageId><CorrelationId>4346806225</CorrelationId><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail><ErrorDetailxmlns="http://www.consol.de/schemas/sayHello.xsd"><ErrorCode>TEC-1000</ErrorCode></ErrorDetail></detail>

CitrusReferenceGuide

287Soap

Page 288: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

OfcoursewecanalsoexpectseveralfaultdetailelementswhenreceivingaSOAPfault.

XMLDSL

<ws:assert-faultfault-code="http://www.citrusframework.org/faultsTEC-1001"fault-string="Invalidrequest"><ws:fault-detailschema-validation="false"><![CDATA[<FaultDetailxmlns="http://www.consol.de/schemas/soap"><ErrorCode>TEC-1000</ErrorCode><Text>Invalidrequest</Text></FaultDetail>]]></ws:fault-detail><ws:fault-detail><![CDATA[<ErrorDetailxmlns="http://www.consol.de/schemas/soap"><ErrorCode>TEC-1000</ErrorCode></ErrorDetail>]]></ws:fault-detail><ws:when><sendendpoint="soapClient">[...]</send></ws:when></ws:assert-fault>

Asyoucanseewecanindividuallyusevalidationsettingsforeachfaultdetail.Intheexampleabovewedisabledschemavalidationforthefirstfaultdetailelement.

SendHTTPerrorcodeswithSOAP

TheSOAPserverlogicinCitrusisabletosimulatepureHTTPerrorcodessuchas404"Notfound"or500"Internalservererror".ThegoodthingisthattheCitrusserverisabletoreceivearequestforpropervalidationinareceiveactionandthensimulateHTTPerrorsondemand.

CitrusReferenceGuide

288Soap

Page 289: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThemechanismonHTTPerrorcodesimulationisnotdifferenttotheusualSOAPrequest/responsehandlinginCitrus.Wereceivetherequestasusualandweprovidearesponse.TheHTTPerrorsituationissimulatedaccordingtothespecialHTTPheadercitrus_http_statusintheCitrusSOAPresponsedefinition.Incasethisheaderissettoavalueotherthan200OKtheCitrusSOAPserversendsanemptySOAPresponsewithHTTPerrorstatuscodesetaccordingly.

<receiveendpoint="helloSoapServer"><message><payload><Messagexmlns="http://consol.de/schemas/sample.xsd"><Text>HelloSOAPserver</Text></Message></payload></message></receive>

<sendendpoint="helloSoapServer"><message><data></data></message><header><elementname="citrus_http_status_code"value="500"/></header></send>

TheSOAPresponsemustbeemptyandtheHTTPstatuscodeissettoavalueotherthan200,like500.ThisresultsinaHTTPerrorsenttothecallingclientwitherror500"Internalservererror".

SOAPattachmentsupport

CitrusisabletoaddattachmentstoaSOAPrequestonclientandserverside.AsusualyoucanvalidatetheSOAPattachmentcontentonareceivedSOAPmessage.ThenextchaptersdescribehowtohandleSOAPattachmentsinCitrus.

SendSOAPattachments

AsclientCitrusisabletoaddattachmentstotheSOAPmessage.Ithinkitisbesttogostraightintoanexampleinordertounderstandhowitworks.

<ws:sendendpoint="soapClient"><message>

CitrusReferenceGuide

289Soap

Page 290: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<payload><SoapMessageWithAttachmentxmlns="http://consol.de/schemas/sample.xsd"><Operation>Readtheattachment</Operation></SoapMessageWithAttachment></payload></message><ws:attachmentcontent-id="MySoapAttachment"content-type="text/plain"><ws:resourcefile="classpath:com/consol/citrus/ws/soapAttachment.txt"/></ws:attachment></ws:send>

NoteInthepreviouschaptersyoumayhavealreadynoticedthecitrus-wsnamespacethatstandsfortheSOAPextensionsinCitrus.Pleaseincludethecitrus-wsnamespaceinyourtestcaseasdescribedearlierinthischaptersoyoucanusetheattachmentsupport.

ThespecialsendactionoftheSOAPextensionnamespaceisawareofSOAPattachments.Theattachmentcontentusuallyconsistsofacontent-idacontent-typeandtheactualcontentasplaintextorbinarycontent.InsidethetestcaseyoucanuseexternalfileresourcesorinlineCDATAsectionsfortheattachmentcontent.AsyouarefamiliarwithCitrusyoumayknowthisalreadyfromotheractions.

CitruswillconstructaSOAPmessagewiththeSOAPattachment.Currentlyonlyoneattachmentpermessageissupported.

ReceiveSOAPattachments

WhenCitruscallsSOAPWebServicesasaclientwemayreceiveSOAPresponseswithattachments.ThetestercanvalidatethosereceivedSOAPmessageswithattachmentcontentquiteeasy.Asusualletushavealookatanexamplefirst.

<ws:receiveendpoint="soapClient"><message><payload><SoapMessageWithAttachmentRequestxmlns="http://consol.de/schemas/sample.xsd"><Operation>Readtheattachment</Operation></SoapMessageWithAttachmentRequest></payload></message><ws:attachmentcontent-id="MySoapAttachment"content-type="text/plain"validator="mySoapAttachmentValidator"><ws:resourcefile="classpath:com/consol/citrus/ws/soapAttachment.txt"/></ws:attachment></ws:receive>

CitrusReferenceGuide

290Soap

Page 291: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AgainweusetheCitrusSOAPextensionnamespacewiththespecificreceiveactionthatisawareofSOAPattachmentvalidation.Thetestercanvalidatethecontent-id,thecontent-typeandtheattachmentcontent.InsteadofusingtheexternalfileresourceyoucouldalsodefineanexpectedattachmenttemplatedirectlyinthetestcaseasinlineCDATAsection.

NoteThews:attachmentelementspecifiesavalidatorinstance.Thisvalidatordetermineshowtovalidatetheattachmentcontent.SOAPattachmentsarenotlimitedtoXMLcontent.Plaintextcontentandbinarycontentispossible,too.SoeachSOAPattachmentvalidatingactioncanuseadifferentSoapAttachmentValidatorinstancewhichisresponsibleforvalidatingandcomparingreceivedattachmentstoexpectedtemplateattachments.IntheCitrusconfigurationthevalidatorissetasnormalSpringbeanwiththerespectiveidentifier.

<beanid="soapAttachmentValidator"class="com.consol.citrus.ws.validation.SimpleSoapAttachmentValidator"<beanid="mySoapAttachmentValidator"class="com.company.ws.validation.MySoapAttachmentValidator"

YoucandefineseveralvalidatorinstancesintheCitrusconfiguration.Thevalidatorwiththegeneralid"soapAttachmentValidator"isthedefaultvalidatorforallactionsthatdonotexplicitlysetavalidatorinstance.Citrusoffersasetofreferencevalidatorimplementations.TheSimpleSoapAttachmentValidatorwilluseasimpleplaintextcomparison.Ofcourseyouareabletoaddindividualvalidatorimplementations,too.

SOAPMTOMsupport

MTOM(MessageTransmissionOptimizationMechanism)enablesyoutosendandreceivelargeSOAPmessagecontentusingstreameddatahandlers.Thisoptimizestheresourceallocationonserverandclientsidewherenotalldataisloadedintomemorywhenmarshalling/unmarshallingthemessagepayloaddata.IndetailMTOMenabledmessagesdohaveaXOPpackageinsidethemessagepayloadreplacingtheactuallargecontentdata.Thecontentisthenstreamedaasseparateattachment.Serverandclientcanoperatewithadatahandlerprovidingaccesstothestreamedcontent.ThisisveryhelpfulwhenusinglargebinarycontentinsideaSOAPmessageforinstance.

CitrusisabletobothsendandreceiveMTOMenabledSOAPmessagesonclientandserver.Justusethemtom-enabledflagwhensendingaSOAPmessage:

<ws:sendendpoint="soapMtomClient"mtom-enabled="true"><message>

CitrusReferenceGuide

291Soap

Page 292: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<data><![CDATA[<image:addImagexmlns:image="http://www.citrusframework.org/imageService/"><image>cid:IMAGE</image></image:addImage>]]></data></message><ws:attachmentcontent-id="IMAGE"content-type="application/octet-stream"><ws:resourcefile="classpath:com/consol/citrus/hugeImageData.png"/></ws:attachment></ws:send>

AsyoucanseetheexampleabovesendsaSOAPmessagethatcontainsalargebinaryimagecontent.Theactualbinaryimagedataisreferencedwithacontentidmarkercid:IMAGEinsidethemessagepayload.Theactualimagecontentisaddedasattachmentwithaseparatefileresource.Importantisherethecontent-idwhichmatchestheidmarkerintheSOAPmessagepayload(IMAGE).

CitrusbuildsaproperSOAPMTOMenabledmessageautomaticallyaddingtheXOPpackageinsidethemessage.ThebinarydataissentasseparateSOAPattachmentaccordingly.TheresultingSOAPmessagelookslikethis:

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body><image:addImagexmlns:image="http://www.citrusframework.org/imageService/"><image><xop:Includexmlns:xop="http://www.w3.org/2004/08/xop/include"href="cid:IMAGE"/></image:addImage></SOAP-ENV:Body></SOAP-ENV:Envelope>

OntheserversideCitrusisalsoabletohandleMTOMenabledSOAPmessages.InaserverreceiveactionyoucanspecifytheMTOMSOAPattachmentcontentasfollows.

<ws:receiveendpoint="soapMtomServer"mtom-enabled="true"><messageschema-validation="false"><data><![CDATA[<image:addImagexmlns:image="http://www.citrusframework.org/imageService/"><image><xop:Includexmlns:xop="http://www.w3.org/2004/08/xop/include"href="cid:IMAGE"/></image></image:addImage>]]></data></message>

CitrusReferenceGuide

292Soap

Page 293: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ws:attachmentcontent-id="IMAGE"content-type="application/octet-stream"><ws:resourcefile="classpath:com/consol/citrus/hugeImageData.png"/></ws:attachment></ws:receive>

WedefinetheMTOMattachmentcontentasseparateSOAPattachment.Thecontent-idisreferencedsomewhereintheSOAPmessagepayloaddata.AtruntimeCitruswilladdtheXOPpackagedefinitionautomaticallyandperformvalidationonthemessageanditsstreamedMTOMattachmentdata.

NextthingthatwehavetotalkaboutisinlineMTOMdata.Thismeansthatthecontentshouldbeaddedaseitherbase64BinaryorhexBinaryencodedStringdatadirectlytothemessagecontent.Seethefollowingexamplethatusesthemtom-inlinesetting:

<ws:sendendpoint="soapMtomClient"mtom-enabled="true"><message><data><![CDATA[<image:addImagexmlns:image="http://www.citrusframework.org/imageService/"><image>cid:IMAGE</image><icon>cid:ICON</icon></image:addImage>]]></data></message><ws:attachmentcontent-id="IMAGE"content-type="application/octet-stream"mtom-inline="true"encoding-type="base64Binary"><ws:resourcefile="classpath:com/consol/citrus/image.png"/></ws:attachment><ws:attachmentcontent-id="ICON"content-type="application/octet-stream"mtom-inline="true"encoding-type="hexBinary"><ws:resourcefile="classpath:com/consol/citrus/icon.ico"/></ws:attachment></ws:send>

ThelistingabovedefinestwoinlineMTOMattachments.Thefirstattachmentcid:IMAGEusestheencodingtypebase64Binarywhichisthedefault.Thesecondattachmentcid:ICONuseshexBinaryencoding.Bothattachmentsareaddedasinlinedatabeforethemessageissent.ThefinalSOAPmessagelookslikefollows:

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body><image:addImagexmlns:image="http://www.citrusframework.org/imageService/"><image>VGhpcyBpcyBhIGJpbmFyeSBpbWFnZSBhdHRhY2htZW50IQpWYXJpYWJsZXMgJXt0ZXN0fSBzaG91bGQgbm90IGJlIHJlcGxhY2VkIQ==

CitrusReferenceGuide

293Soap

Page 294: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<icon>5468697320697320612062696E6172792069636F6E206174746163686D656E74210A5661726961626C657320257B746573747D2073686F756C64206E6F74206265207265706C6163656421</image:addImage></SOAP-ENV:Body></SOAP-ENV:Envelope>

Theimagecontentisabase64BinaryStringandtheiconaheyBinaryString.OfcoursethismechanismalsoissupportedinreceiveactionsontheserversidewheretheexpectedmessagecontentisaddedalsinlineMTOMdatabeforevalidationtakesplace.

SOAPclientbasicauthentication

AsaSOAPclientyoumayhavetousebasicauthenticationinordertoaccessaserverresource.BasicauthenticationviaHTTPstandsforusername/passwordauthenticationwherethecredentialsaretransmittedintheHTTPrequestheadersectionasbase64encodedentry.AsCitrususestheSpringWebServicestackwecanusethebasicauthenticationsupportthere.WesettheusercredentialsontheHttpClientmessagesenderwhichisusedinsidetheSpringWebServiceTemplate.

CitrusprovidesacomfortablewaytosettheHTTPmessagesenderwithbasicauthenticationcredentialsontheWebServiceTemplate.Justseethefollowingexampleandlearnhowtodothat.

<citrus-ws:clientid="soapClient"request-url="http://localhost:8090/test"message-sender="basicAuthClient"/>

<beanid="basicAuthClient"class="org.springframework.ws.transport.http.HttpComponentsMessageSender"<propertyname="authScope"><beanclass="org.apache.http.auth.AuthScope"><constructor-argvalue="localhost"/><constructor-argvalue="8090"/><constructor-argvalue=""/><constructor-argvalue="basic"/></bean></property><propertyname="credentials"><beanclass="org.apache.http.auth.UsernamePasswordCredentials"><constructor-argvalue="someUsername"/><constructor-argvalue="somePassword"/></bean></property></bean>

CitrusReferenceGuide

294Soap

Page 295: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheaboveconfigurationresultsinSOAPrequestswithauthenticationheadersproperlysetforbasicauthentication.ThespecialmessagesendertakescareonaddingtheproperbasicauthenticationheadertoeachrequestthatissentwiththisCitrusmessagesender.Bydefaultpreemtiveauthenticationisused.Themessagesenderonlysendsasinglerequesttotheserverwithallauthenticationinformationsetinthemessageheader.Therequestwhichdeterminestheauthenticationschemeontheserverisskipped.ThisiswhyyouhavetoaddsomeauthscopesoCitruscansetupanauthenticationcachewithintheHTTPcontextinordertohavepreemtiveauthentication.

TipYoucanalsoskipthemessagesenderconfigurationandsettheAuthorizationheaderoneachrequestinyoursendactiondefinitiononyourown.BeawareofsettingtheheaderasHTTPmimeheaderusingthecorrectprefixandtakecareonusingthecorrectbasicauthenticationwithbase64encodingfortheusername:passwordphrase.

<header><elementname="citrus_http_Authorization"value="Basicc29tZVVzZXJuYW1lOnNvbWVQYXNzd29yZA=="</header>

Forbase64encodingyoucanalsouseaCitrusfunction,seefunctions-encode-base64

SOAPserverbasicauthentication

WhenprovidingSOAPWebServiceserverfunctionalityCitruscanalsosetbasicauthenticationsoallclientsneedtoauthenticateproperlywhenaccessingtheserverresource.

<citrus-ws:serverid="simpleSoapServer"port="8080"auto-start="true"resource-base="src/it/resources"security-handler="basicSecurityHandler"/>

<beanid="securityHandler"class="com.consol.citrus.ws.security.SecurityHandlerFactory"><propertyname="users"><list><beanclass="com.consol.citrus.ws.security.User"><propertyname="name"value="citrus"/><propertyname="password"value="secret"/><propertyname="roles"value="CitrusRole"/></bean></list></property><propertyname="constraints">

CitrusReferenceGuide

295Soap

Page 296: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<map><entrykey="/foo/*"><beanclass="com.consol.citrus.ws.security.BasicAuthConstraint"><constructor-argvalue="CitrusRole"/></bean></entry></map></property></bean>

Wehavesetasecurityhandlerontheserverwebcontainerwithaconstraintonallresourceswith/foo/*.Followingfromthattheserverrequiresbasicauthenticationfortheseresources.Thegrantedusersandrolesarespecifiedwithinthesecurityhandlerbeandefinition.ConnectingclientshavetosetthebasicauthHTTPheaderproperlyusingthecorrectuserandroleforaccessingtheCitrusservernow.

Youcancustomizethesecurityhandlerforyourveryspecificneeds(e.g.loadusersandroleswithJDBCfromadatabase).Justhavealookatthecodebaseandinspectthesettingsandpropertiesofferedbythesecurityhandlerinterface.

TipThismechanismisnotrestrictedtobasicauthenticationonly.Withothersettingsyoucanalsosetupdigestorform-basedauthenticationconstraintsveryeasy.

WS-Addressingsupport

ThewebservicestackoffersalotofdifferenttechnologiesandstandardswithinthecontextofSOAPWebServices.WespeakofWS-*specificationsinparticular.Oneofthesespecificationsdealswithaddressing.OnclientsideyoumayaddwsaheaderinformationtotherequestinordertogivetheserverinstructionshowtodealwithSOAPfaultsforinstance.

InCitrusWebServiceclientyoucanaddthoseheaderinformationusingthecommonconfigurationlikethis:

<citrus-ws:clientid="soapClient"request-url="http://localhost:8090/test"message-converter="wsAddressingMessageConverter"/>

<beanid="wsAddressingMessageConverter"class="com.consol.citrus.ws.message.converter.WsAddressingMessageConverter"<constructor-arg><beanid="wsAddressing200408"class="com.consol.citrus.ws.addressing.WsAddressingHeaders"<propertyname="version"value="VERSION200408"/><propertyname="action"value="http://citrus.sample/sayHello"/><propertyname="to"value="http://citrus.sample/server"/>

CitrusReferenceGuide

296Soap

Page 297: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<propertyname="from"><beanclass="org.springframework.ws.soap.addressing.core.EndpointReference"><constructor-argvalue="http://citrus.sample/client"/></bean></property><propertyname="replyTo"><beanclass="org.springframework.ws.soap.addressing.core.EndpointReference"><constructor-argvalue="http://citrus.sample/client"/></bean></property><propertyname="faultTo"><beanclass="org.springframework.ws.soap.addressing.core.EndpointReference"><constructor-argvalue="http://citrus.sample/fault/resolver"/></bean></property></bean></constructor-arg></bean>

TheWsAddressingheadervalueswillbeusedforallrequestmessagesthataresentwiththesoapclientcomponentsoapClient.YoucanoverwritetheWsAddressingheaderineachsendtestactioninyourtestthough.JustsetthespecialWsAddressingmessageheaderonyourrequest.Youcanusethefollowingmessageheadernamesinordertooverwritethedefaultaddressingheadersspecifiedinthemessageconverterconfiguration(alsoseetheclasscom.consol.citrus.ws.addressing.WsAddressingMessageHeaders).

citrus_soap_ws_addressing_messageIdaddressingmessageidasURIcitrus_soap_ws_addressing_fromaddressingfromendpointreferenceasURIcitrus_soap_ws_addressing_toaddressingtoURIcitrus_soap_ws_addressing_actionaddressingactionURIcitrus_soap_ws_addressing_replyToaddressingreplytoendpointreferenceasURIcitrus_soap_ws_addressing_faultToaddressingfaulttoendpointreferenceasURI

WhenusingthismessageheadersyouareabletoexplicitlyoverwritetheWsAddressingheaders.Testvariablesaresupportedofcoursewhenspecifyingthevalues.MostofthevaluesareparsedtoaURIvalueattheendsopleasemakesuretousecorrectURIStringrepresentations.

NoteTheWS-Addressingspecificationknowsseveralversions.Supportedversionare:

VERSION10(WS-Addressing1.0May2006)

CitrusReferenceGuide

297Soap

Page 298: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

VERSION200408(August2004editionoftheWS-Addressingspecification)

TheaddressingheadersfindaplaceintheSOAPmessageheaderwithrespectivenamespacesandvalues.ApossibleSOAPrequestwithWSaddressingheaderslookslikefollows:

<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Headerxmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:ToSOAP-ENV:mustUnderstand="1">http://citrus.sample/server</wsa:To><wsa:From><wsa:Address>http://citrus.sample/client</wsa:Address></wsa:From><wsa:ReplyTo><wsa:Address>http://citrus.sample/client</wsa:Address></wsa:ReplyTo><wsa:FaultTo><wsa:Address>http://citrus.sample/fault/resolver</wsa:Address></wsa:FaultTo><wsa:Action>http://citrus.sample/sayHello</wsa:Action><wsa:MessageID>urn:uuid:4c4d8af2-b402-4bc0-a2e3-ad33b910e394</wsa:MessageID></SOAP-ENV:Header><SOAP-ENV:Body><cit:HelloRequestxmlns:cit="http://citrus/sample/sayHello"><cit:Text>HelloCitrus!</cit:Text></cit:HelloRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>

ImportantBydefaultwhennotsetexplicitlyonthemessageheaderstheWsAddressingmessageidpropertyisautomaticallygeneratedforeachrequest.YoucansetthemessageidgenerationstrategyintheSpringapplicationcontextmessageconverterconfiguration:

<beanid="wsAddressingMessageConverter"class="com.consol.citrus.ws.message.converter.WsAddressingMessageConverter"<propertyname="messageIdStrategy"><beanclass="org.springframework.ws.soap.addressing.messageid.UuidMessageIdStrategy"/></property></bean>

BydefaultthestrategywillcreateanewJavaUUIDforeachrequest.Thestrategyalsousesacommonresourcenameprefixurn:uuid:.Youcanoverwritethemessageidanytimeforeachrequestexplicitlybysettingthemessageheadercitrus_soap_ws_addressing_messageIdwitharespectivevalueonthemessageinyourtest.

CitrusReferenceGuide

298Soap

Page 299: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SOAPclientforkmode

SOAPoverHTTPusessynchronouscommunicationbynature.ThismeansthatsendingaSOAPmessageinCitrusoverHTTPwillautomaticallyblockfurthertestactionsuntilthesynchronousHTTPresponsehasbeenreceived.Intestcasesthissynchronousblockingmightcauseproblemsforseveralreasons.AsimplereasonwouldbethatyouneedtodofurthertestactionsinparalleltothesynchronousHTTPSOAPcommunication(e.g.simulateanotherbackendsysteminthetestcase).

YoucanseparatetheSOAPsendactionfromtherestofthetestcasebyusingthe"fork"mode.TheSOAPclientwillautomaticallyopenanewJavaThreadforthesynchronouscommunicationandthetestisabletocontinuewithexecutionalthoughthesynchronousHTTPSOAPresponsehasnotarrivedyet.

<ws:sendendpoint="soapClient"fork="true"><message><payload><SoapRequestxmlns="http://www.consol.de/schemas/sample.xsd"><Operation>Readtheattachment</Operation></SoapRequest></payload></message></ws:send>

Withthe"fork"modeenabledthetestcontinueswithexecutionwhilethesendingactionwaitsforthesynchronousresponseinaseparateJavaThread.Youcouldreachthesamebehaviourwithacomplex/containerconstruct,butforkingthesendactionismuchmorestraightforward.

ImportantItishighlyrecommendedtouseaproper"timeout"settingontheSOAPreceiveactionwhenusingforkmode.Theforkedsendoperationmighttakesometimeandthecorrespondingreceiveactionmightrunintofailureastheresponsewashasnotbeenreceivedyet.Theresultwouldbeabrokentestbecauseofthemissingresponsemessage.Aproper"timeout"settingforthereceiveactionsolvesthisproblemastheactionwaitsforthistimeperiodandoccasionallyrepeatedlyasksfortheSOAPresponsemessage.Thefollowinglistingsetsthereceivetimeoutto10seconds,sotheactionwaitsfortheforkedsendactiontodelivertheSOAPresponseintime.

<ws:receiveendpoint="soapClient"timeout="10000"><message><payload><SoapResponsexmlns="http://www.consol.de/schemas/sample.xsd">

CitrusReferenceGuide

299Soap

Page 300: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<Operation>Didsomething</Operation><Success>true</Success></SoapResponse></payload></message></ws:receive>

SOAPservletcontextcustomization

ForhighlycustomizedSOAPservercomponentsinCitrusyoucandefineafullservletcontextconfigurationfile.HereyouhavethefullpowertoaddSpringendpointmappingsandcustomendpointimplementations.Youcansetthecustomservletcontextasexternalfileresourceontheservercomponent:

<citrus-ws:clientid="soapClient"context-config-location="classpath:citrus-ws-servlet.xml"message-factory="soap11MessageFactory"/>

Nowletushaveacloserlookatthecontext-config-locationattribute.ThisconfigurationdefinestheSpringapplicationcontextfileforendpoints,requestmappingsandotherSpringWSspecificinformation.PleaseseetheofficialSpringWSdocumentationfordetailsonthisSpringbasedconfiguration.Youcanalsojustcopythefollowingexampleapplicationcontextwhichshouldworkforyouingeneral.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">

<beanid="loggingInterceptor"class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"><description>Thisinterceptorlogsthemessagepayload.</description></bean>

<beanid="helloServicePayloadMapping"class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping"><propertyname="mappings"><props><propkey="http://www.consol.de/schemas/sayHelloHelloRequest">helloServiceEndpoint</prop>

CitrusReferenceGuide

300Soap

Page 301: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</props></property><propertyname="interceptors"><list><refbean="loggingInterceptor"/></list></property></bean>

<beanid="helloServiceEndpoint"class="com.consol.citrus.ws.server.WebServiceEndpoint"><propertyname="endpointAdapter"ref="staticResponseEndpointAdapter"/></bean>

<citrus:static-response-adapterid="staticResponseEndpointAdapter"><citrus:payload><![CDATA[<HelloResponsexmlns="http://www.consol.de/schemas/sayHello"><MessageId>123456789</MessageId><CorrelationId>CORR123456789</CorrelationId><User>WebServer</User><Text>HelloUser</Text></HelloResponse>]]></citrus:payload><citrus:header><citrus:elementname="http://www.consol.de/schemas/samples/sayHello.xsdns0:Operation"value="sayHelloResponse"/><citrus:elementname="http://www.consol.de/schemas/samples/sayHello.xsdns0:Request"value="HelloRequest"/><citrus:elementname="citrus_soap_action"value="sayHello"/></citrus:header></citrus:static-response-adapter></beans>

TheprogramlistingabovedescribesanormalSpringWSrequestmappingwithendpointconfigurations.Themappingisresponsibletoforwardincomingrequeststotheendpointwhichwillhandletherequestandprovideaproperresponsemessage.Firstofallweaddalogginginterceptortothecontextsoallincomingrequestsgetloggedtotheconsolefirst.Thenweuseapayloadmapping(PayloadRootQNameEndpointMapping)inordertomapallincoming'HelloRequest'SOAPmessagestothe'helloServiceEndpoint'.EndpointsareofessentialnatureinCitrusSOAPWebServicesimplementation.Theyareresponsibleforprocessingarequestinordertoprovideaproperresponsemessagethatissentbacktothecallingclient.Citrususestheendpointincombinationwithamessageendpointadapterimplementation.

CitrusReferenceGuide

301Soap

Page 302: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Theendpointworkstogetherwiththemessageendpointadapterthatisresponsibleforprovidingaresponsemessagefortheclient.ThevariousmessageendpointadapterimplementationsinCitruswerealreadydiscussedinendpoint-adapter.

Inthisexamplethe'helloServiceEndpoint'usesthe'static-response-adapter'whichisalwaysreturningastaticresponsemessage.Inmostcasesstaticresponseswillnotfitthetestscenarioandyouwillhavetorespondmoredynamically.

RegardlessofwhichmessageendpointadaptersetupyouareusinginyourtestcasetheendpointtransformstheresponseintoaproperSOAPmessage.Youcanaddasmanyrequestmappingsandendpointsasyouwanttotheservercontextconfiguration.SoyouareabletohandledifferentrequesttypeswithonesingleJettyserverinstance.

That'sitforconnectingwithSOAPWebServices!WesawhowtosendandreceiveSOAPmessageswithJettyandSpringWebServices.HavealookatthesamplescomingwithyourCitrusarchiveinordertolearnmoreabouttheSOAPmessagehandling.

CitrusReferenceGuide

302Soap

Page 303: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FTPsupportCitrusisabletostartalittleftpserveracceptingincomingclientrequests.AlsoCitrusisabletocallFTPcommandsasaclient.ThenextsectionsdealwithFTPconnectivity.

NoteTheFTPcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldaddthemoduleasMavendependencytoyourprojectaccordingly.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-ftp</artifactId><version>2.7</version></dependency>

AsCitrusprovidesacustomizedFTPconfigurationschemafortheSpringapplicationcontextconfigurationfileswehavetoaddnametothetoplevelbeanselement.Simplyincludetheftp-confignamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-ftp="http://www.citrusframework.org/schema/ftp/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/http/confighttp://www.citrusframework.org/schema/ftp/config/citrus-ftp-config.xsd">

[...]

</beans>

NowwearereadytousethecustomizedCitrusFTPconfigurationelementswiththecitrus-ftpnamespaceprefix.

FTPclient

CitrusReferenceGuide

303Ftp

Page 304: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WewanttouseCitrusfoconnecttodomeFTPserverasaclientsendingcommandssuchascreatingadirectoryorlistingallfiles.CitrusoffersaclientcomponentdoingexactlythisFTPclientconnection.

<citrus-ftp:clientid="ftpClient"host="localhost"port="22222"username="admin"password="admin"timeout="10000"/>

TheconfigurationabovedescribesaCitrusftpclientconnectedtoaftpserverwithftp://localhost:22222.Forauthenticationusernameandpasswordaredefinedaswellastheglobalconnectiontimeout.Theclientwillautomaticallysendusernameandpasswordforproperauthenticationtotheserverwhenopeninganewconnection.

Inatestcaseyouarenowabletousetheclienttopushcommandstotheserver.

<sendendpoint="ftpClient"fork="true"><message><data></data></message><header><elementname="citrus_ftp_command"value="PWD"/><elementname="citrus_ftp_arguments"value="test"/></header></send>

<receiveendpoint="ftpClient"><messagetype="plaintext"><data>PWD</data></message><header><elementname="citrus_ftp_command"value="PWD"/><elementname="citrus_ftp_arguments"value="test"/><elementname="citrus_ftp_reply_code"value="257"/><elementname="citrus_ftp_reply_string"value="@contains('iscurrentdirectory')@"/></header></receive>

Asyoucanseemostoftheftpcommunicationparametersarespecifiedasspecialheaderelementsinthemessage.CitrusautomaticallyconvertsthoseinformationtoproperFTPcommandsandresponsemessages.

FTPserver

CitrusReferenceGuide

304Ftp

Page 305: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowthatweareabletoaccessFTPasaclientwemightalsowanttosimulatetheserverside.ThereforeCitrusoffersaservercomponentthatislisteningonaportforincomingFTPconnections.Theserverhasadefaulthomedirectoryonthelocalfilesystemspecified.Butyoucanalsodefinehomedirectoriesperuser.Fornowletushavealookattheserverconfigurationcomponent:

<citrus-ftp:serverid="ftpServer">port="22222"auto-start="true"user-manager-properties="classpath:ftp.server.properties"/>

Theftpserverconfigurationisquitesimple.Theserverstartsautomaticallyandbindstoaport.Theuserconfigurationisreadfromauser-manager-propertyfile.Letushavealookatthecontentofthisusermanagementfile:

#Passwordis"admin"ftpserver.user.admin.userpassword=21232F297A57A5A743894A0E4A801FC3ftpserver.user.admin.homedirectory=target/ftp/user/adminftpserver.user.admin.enableflag=trueftpserver.user.admin.writepermission=trueftpserver.user.admin.maxloginnumber=0ftpserver.user.admin.maxloginperip=0ftpserver.user.admin.idletime=0ftpserver.user.admin.uploadrate=0ftpserver.user.admin.downloadrate=0

ftpserver.user.anonymous.userpassword=ftpserver.user.anonymous.homedirectory=target/ftp/user/anonymousftpserver.user.anonymous.enableflag=trueftpserver.user.anonymous.writepermission=falseftpserver.user.anonymous.maxloginnumber=20ftpserver.user.anonymous.maxloginperip=2ftpserver.user.anonymous.idletime=300ftpserver.user.anonymous.uploadrate=4800ftpserver.user.anonymous.downloadrate=4800

Asyoucanseeyouareabletodefineasmanyuserfortheftpserverasyoulike.Usernameandpassworddefinetheauthenticationontheserver.Inadditiontothatyouhaveplentyofconfigurationpossibilitiesperuser.CitrususestheApacheftpserverimplementation.SoformoredetailsonconfigurationcapabilitiespleaseconsulttheofficialApacheftpserverdocumentation.

CitrusReferenceGuide

305Ftp

Page 306: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Nowwewouldliketousetheserverinatestcase.Veryeasyyoujusthavetodefineareceivemessageactionwithinyourtestcasethatusestheserveridasendpointreference:

<echo><message>ReceiveuserloginonFTPserver</message></echo>

<receiveendpoint="ftpServer"><messagetype="plaintext"><data>USER</data></message><header><elementname="citrus_ftp_command"value="USER"/><elementname="citrus_ftp_arguments"value="admin"/></header></receive>

<sendendpoint="ftpServer"><messagetype="plaintext"><data>OK</data></message></send>

<echo><message>ReceiveuserpasswordonFTPserver</message></echo>

<receiveendpoint="ftpServer"><messagetype="plaintext"><data>PASS</data></message><header><elementname="citrus_ftp_command"value="PASS"/><elementname="citrus_ftp_arguments"value="admin"/></header></receive>

<sendendpoint="ftpServer"><messagetype="plaintext""><data>OK</data></message></send>

Thelistingaboveshowstwoincomingcommandsrepresentingauserlogin.Weindicatewithresendactionsthatwewouldlinktheservertorespondwithpositivefeedbackandtoacceptthelogin.Aswehaveafullyqualifiedftpserverrunningtheclientcanalso

CitrusReferenceGuide

306Ftp

Page 307: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

pushfilesreaddirectoriesandmore.Allincomingcommandscanbevalidatedinsideatestcase.

CitrusReferenceGuide

307Ftp

Page 308: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

MessagechannelsupportMessagechannelsrepresenttheinmemorymessagingsolutioninCitrus.Producerandconsumercomponentsarelinkedviachannelsexchangingmessagesinmemory.AsthistransportmechanismcomesfromSpringIntegrationAPI(http://www.springsource.org/spring-integration)andCitrusitselfusesalotofSpringAPIs,especiallythosefromSpringIntegrationyouareabletoconnecttoallSpringmessagingadaptersviatheseinmemorychannels.

CitrusoffersachannelcomponentsthatcanbeusedbothbyCitrusandSpringIntegration.TheconclusionisthatCitrussupportsthesendingandreceivingofmessagesbothtoandfromSpringIntegrationmessagechannelcomponents.ThisopensupalotofgreatpossibilitiestointeractwiththeSpringIntegrationtransportadaptersforFTP,TCP/IPandsoon.Inadditiontothatthemessagechannelsupportprovidesusagoodwaytoexchangemessagesinmemory.

CitrusprovidessupportforsendingandreceivingJMSmessages.Wehavetoseparatebetweensynchronousandasynchronouscommunication.SointhischapterweexplainhowtosetupJMSmessageendpointsforsynchronousandasynchronousoutboundandinboundcommunication

NoteThemessagechannelconfigurationcomponentsusethedefault"citrus"configurationnamespaceandschemadefinition.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-jms="http://www.citrusframework.org/schema/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

CitrusReferenceGuide

308Messagechannel

Page 309: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Channelendpoint

Citrusoffersachannelendpointcomponentthatisabletocreateproducerandconsumercomponents.Producerandconsumersendandreceivemessagesbothtoandfromachannelendpoint.BydefaulttheendpointisasynchronouswhenconfiguredintheCitrusapplicationcontext.Withthiscomponentyouareabletoaccessmessagechannelsdirectly:

<citrus:channel-endpointid="helloEndpoint"channel="helloChannel"/>

<si:channelid="helloChannel"/>

TheCitruschannelendpointreferencesaSpringIntegrationchanneldirectly.InsideyourtestcaseyoucanreferencetheCitrusendpointasusualtosendandreceivemessages.Wewillseethislaterinsomeexamplecodelistings.

NoteTheSpringIntegrationconfigurationcomponentsuseaspecificnamespacethathastobeincludedintoyourSpringapplicationcontext.Youcanusethefollowingtemplatewhichholdsallnecessarynamespacesandschemalocations:

<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:si="http://www.springframework.org/schema/integration"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.springframework.org/schema/integrationhttp://www.springframework.org/schema/integration/spring-integration.xsd"></beans>

TheCitruschannelendpointalsosupportsacustomizedmessagechanneltemplatethatwillactuallysendthemessages.Thecustomizedtemplatemightgiveyouaccesstospecialconfigurationpossibilities.Howeveritisoptional,soifnomessagechanneltemplateisdefinedintheconfigurationCitruswillcreateadefaulttemplate.

<citrus:channel-endpointid="helloEndpoint"channel="helloChannel"message-channel-template="myMessageChannelTemplate"/>

CitrusReferenceGuide

309Messagechannel

Page 310: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Themessagesenderisnowreadytopublishmessagestothedefinedchannel.Thecommunicationissupposedtobeasynchronous,sotheproducerisnotabletoprocessareplymessage.Wewilldealwithsynchronouscommunicationandreplymessageslaterinthischapter.Themessageproducerjustpublishesmessagestothechannelandisdone.Interactingwiththeendpointsinatestcaseisquiteeasy.Justreferencetheidoftheendpointinyoursendandreceivetestactions

<sendendpoint="helloEndpoint"><message><payload><v1:HelloRequestxmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloWorld!</v1:Text></v1:HelloRequest></payload></message></send>

<receiveendpoint="helloEndpoint"><message><payload><v1:HelloResponsexmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloCitrus!</v1:Text></v1:HelloResponse></payload></message></receive>

AsyoucanseeCitrusisalsoabletoreceivemessagesfromthesameSpringIntegrationmessagechanneldestination.Wejustreferencesthesamechannel-endpointinthereceiveaction.

Asusualthereceiverconnectstothemessagedestinationandwaitsformessagestoarrive.Theusercansetareceivetimeoutwhichissetto5000millisecondsbydefault.Incasenomessagewasreceivedinthistimeframethereceiverraisestimeouterrorsandthetestfails.

Synchronouschannelendpoints

Thesynchronouschannelproducerpublishesmessagesandwaitssynchronouslyfortheresponsetoarriveonsomereplychanneldestination.Thereplychannelnameissetinthemessage'sheaderattributessothecounterpartinthiscommunicationcansendits

CitrusReferenceGuide

310Messagechannel

Page 311: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

replytothatchannel.Thebasicconfigurationforasynchronouschannelendpointcomponentlookslikefollows:

<citrus:channel-sync-endpointid="helloSyncEndpoint"channel="helloChannel"reply-timeout="1000"polling-interval="1000"/>

Synchronousmessagechannelendpointsusuallydopollforsynchronousreplymessagesforprocessingthereplymessages.Thepollintervalisanoptionalsettinginordertomanagetheamountofreplymessagehandshakeattempts.Oncetheendpointwasabletoreceivethereplymessagesynchronouslythetestcasecanreceivethereply.Incaseallmessagehandshakeattemptsdofailbecausethereplymessageisnotavailableintimeweraisesometimeouterrorandthetestwillfail.

NoteBydefaultthechannelendpointusestemporaryreplychanneldestinations.Thetemporaryreplychannelsareonlyusedonceforasinglecommunicationhandshake.Afterthatthereplychannelisdeletedagain.Staticreplychannelsarenotsupportedasithasnotbeeninscopeyet.

Whensendingamessagetothisendpointinthefirstplacetheproducerwillwaitsynchronouslyfortheresponsemessagetoarriveonthereplydestination.Youcanreceivethereplymessageinyourtestcaseusingthesameendpointcomponent.Sowehavetwoactionsonthesameendpoint,firstsendthenreceive.

<sendendpoint="helloSyncEndpoint"><message><payload><v1:HelloRequestxmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloWorld!</v1:Text></v1:HelloRequest></payload></message></send>

<receiveendpoint="helloSyncEndpoint"><message><payload><v1:HelloResponsexmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloCitrus!</v1:Text></v1:HelloResponse></payload></message></receive>

CitrusReferenceGuide

311Messagechannel

Page 312: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Inthelastsectionwesawthatsynchronouscommunicationisbasedonreplymessagesontemporaryreplychannels.WesawthatCitrusisabletopublishmessagestochannelsandwaitforreplymessagestoarriveontemporaryreplychannels.Thissectiondealswiththesamesynchronouscommunicationoverreplymessages,butnowCitrushastosenddynamicreplymessagestotemporarychannels.

ThescenariowearetalkingaboutisthatCitrusreceivesamessageandweneedtoreplytoatemporaryreplychannelthatisstoredinthemessageheaderattributes.Wehandlethissynchronouscommunicationwiththesamesynchronouschannelendpointcomponent.Wheninitiatingthecommunicationbyreceivingamessagefromasynchronouschannelendpointyouareabletosendasynchronousresponseback.Againjustusethesameendpointreferenceinyourtestcase.Thehandlingoftemporaryreplydestinationsisdoneautomaticallybehindthescenes.Sowehaveagaintwoactionsinourtestcase,butthistimefirstreceivethensend.

<receiveendpoint="helloSyncEndpoint"><message><payload><v1:HelloRequestxmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloWorld!</v1:Text></v1:HelloRequest></payload></message></receive>

<sendendpoint="helloSyncEndpoint"><message><payload><v1:HelloResponsexmlns:v1="http://citrusframework.org/schemas/HelloService.xsd"><v1:Text>HelloCitrus!</v1:Text></v1:HelloResponse></payload></message></send>

Thesynchronousmessagechannelendpointwillhandleallreplychanneldestinationsandprovidethosebehindthescenes.

Messageselectorsonchannels

CitrusReferenceGuide

312Messagechannel

Page 313: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

UnfortunatelySpringIntegrationmessagechannelsdonotsupportmessageselectorsonheadervaluesasdescribedinmessage-selector.WithCitrusversion1.2wefoundawaytoalsoaddmessageselectorsupportonmessagechannels.Wehadtointroduceaspecialqueuemessagechannelimplementation.Sofirstofallweusethisnewmessagechannelimplementationinourconfiguration.

<citrus:channelid="orderChannel"capacity="5"/>

TheCitrusmessagechannelimplementationextendsthequeuechannelimplementationfromSpringIntegration.Sowecanaddacapacityattributeforthischannel.That'sit!Nowweusethemessagechannelthatsupportsmessageselection.Inourtestwedefinemessageselectorsonheadervaluesasdescribedinmessage-selectorandyouwillseethatitworks.

Inadditiontothatwehaveimplementedothermessagefilterpossibilitiesonmessagechannelsthatwediscussinthenextsections.

RootQNameMessageSelector

YoucanusetheXMLrootQNameofyourmessageasselectioncriteria.Let'sseehowthisworksinasmallexample:

WehavetwodifferentXMLmessagesonamessagechannelwaitingtobepickedupbyaconsumer.

<HelloMessagexmlns="http://citrusframework.org/schema">HelloCitrus</HelloMessage><GoodbyeMessagexmlns="http://citrusframework.org/schema">GoodbyeCitrus</GoodbyeMessage>

WewouldliketopickuptheGoodbyeMessageinourtestcase.TheHelloMessageshouldbeleftonthemessagechannelaswearenotinterestedinitrightnow.Wecandefinearootqnamemessageselectorinthereceiveactionlikethis:

<receiveendpoint="orderChannelEndpoint"><selector><elementname="root-qname"value="GoodbyeMessage"/></selector><message><payload><GoodbyeMessagexmlns="http://citrusframework.org/schema">GoodbyeCitrus</GoodbyeMessage</payload></message></receive>

CitrusReferenceGuide

313Messagechannel

Page 314: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheCitrusreceiverpicksuptheGoodbyeMessagefromthechannelselectedviatherootqnameoftheXMLmessagepayload.OfcourseyoucanalsocombinemessageheaderselectorsandrootqnameselectorsasshowninthisexamplebelowwhereamessageheadersequenceIdisaddedtotheselectionlogic.

<selector><elementname="root-qname"value="GoodbyeMessage"/><elementname="sequenceId"value="1234"/></selector>

AswedealwithXMLqnamevalues,wecanalsousenamespacesinourselectorrootqnameselection.

<selector><elementname="root-qname"value="http://citrusframework.org/schemaGoodbyeMessage"/></selector>

XPathEvaluatingMessageSelector

ItisalsopossibletoevaluatesomeXPathexpressiononthemessagepayloadinordertoselectamessagefromamessagechannel.TheXPathexpressionoutcomemustmatchanexpectedvalueandonlythenthemessageisconsumedformthechannel.

ThesyntaxfortheXPathexpressionistobedefinedastheelementnamelikethis:

<selector><elementname="xpath://Order/status"value="pending"/></selector>

Themessageselectorlooksforordermessageswithstatus="pending"inthemessagepayload.Thismeansthatfollowingmessageswouldgetaccepted/declinedbythemessageselector.

<Order><status>pending</status></Order>=ACCEPTED<Order><status>finished</status></Order>=NOTACCEPTED

OfcourseyoucanalsouseXMLnamespacesinyourXPathexpressionswhenselectingmessagesfromchannels.

CitrusReferenceGuide

314Messagechannel

Page 315: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<selector><elementname="xpath://ns1:Order/ns1:status"value="pending"/></selector>

Namespaceprefixesmustmatchtheincomingmessage-otherwisetheXPathexpressionwillnotworkasexpected.Inourexamplethemessageshouldlooklikethis:

<ns1:Orderxmlns:ns1="http://citrus.org/schema"><ns1:status>pending</ns1:status></ns1:Order>

KnowingthecorrectXMLnamespaceprefixisnotalwayseasy.IfyouarenotsurewhichnamespaceprefixtochooseCitrusshipswithadynamicnamespacereplacementforXPathexpressions.TheXPathexpressionlookslikethisandismostflexible:

<selector><elementname="xpath://http://citrus.org/schema:Order/http://citrus.org/schema:status"value="pending"/></selector>

ThiswillmatchallincomingmessagesregardlesstheXMLnamespaceprefixthatisused.

CitrusReferenceGuide

315Messagechannel

Page 316: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FilesupportInchaptermessage-channelwediscussedthenativeSpringIntegrationchannelsupportwhichenablesCitrustointeractwithallSpringIntegrationmessagingadapterimplementations.ThisisafantasticwaytoextendCitrusforadditionaltransports.ThisinteractionnowcomeshandywhenwritingandreadingfilesfromthefilesysteminCitrus.

Writefiles

WewanttousetheSpringIntegrationfileadapterforbothreadingandwritingfileswithalocaldirectory.Citruscaneasilyconnecttothisfileadapterimplementationwithitsmessagechannelsupport.CitrusmessagesenderandreceiverspeaktomessagechannelsthatareconnectedtotheSpringIntegrationfileadapters.

<citrus:channel-endpointid="fileEndpoint"channel="fileChannel"/>

<file:outbound-channel-adapterid="fileOutboundAdapter"channel="fileChannel"directory="file:$some.directory.property"/>

<si:channelid="fileChannel"/>

TheconfigurationabovedescribesaCitrusmessagechannelendpointconnectedtoaSpringIntegrationoutboundfileadapterthatwritesmessagestoastoragedirectory.WiththiscombinationyouareabletowritefilestoadirectoryinyourCitrustestcase.ThetestcaseusesthechannelendpointinitssendactionandtheendpointinteractswiththeSpringIntegrationfileadaptersosendingoutthefile.

NoteTheSpringIntegrationfileadapterconfigurationcomponentsaddanewnamespacetoourSpringapplicationcontext.Seethistemplatewhichholdsallnecessarynamespacesandschemalocations:

<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:si="http://www.springframework.org/schema/integration"xmlns:file="http://www.springframework.org/schema/integration/file"xsi:schemaLocation="http://www.springframework.org/schema/beans

CitrusReferenceGuide

316File

Page 317: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.springframework.org/schema/integrationhttp://www.springframework.org/schema/integration/spring-integration.xsdhttp://www.springframework.org/schema/integration/filehttp://www.springframework.org/schema/integration/file/spring-integration-file.xsd"></beans>

Readfiles

Thenextprogramlistingshowsapossibleinboundfilecommunication.SotheSpringIntegrationfileinboundadapterwillreadfilesfromastoragedirectoryandpublishthefilecontentstoamessagechannel.Citruscanthenreceivethosefilesasmessagesinatestcaseviathechannelendpointandvalidatethefilecontentsforinstance.

<file:inbound-channel-adapterid="fileInboundAdapter"channel="fileChannel"directory="file:$some.directory.property"><si:pollerfixed-rate="100"/></file:inbound-channel-adapter>

<si:channelid="fileChannel"><si:queuecapacity="25"/><si:interceptors><beanclass="org.springframework.integration.transformer.MessageTransformingChannelInterceptor"<constructor-arg><beanclass="org.springframework.integration.file.transformer.FileToStringTransformer"</constructor-arg></bean></si:interceptors></si:channel>

<citrus:channel-endpointid="fileEndpoint"channel="fileChannel"/>

ImportantThefileinboundadapterconstructsJavafileobjectsasthemessagepayloadbydefault.CitruscanonlyworkonStringmessagepayloads.SoweneedafiletransformerthatconvertsthefileobjectstoStringpayloadsrepresentingthefile'scontent.

ThisfileadapterexampleshowshoweasyCitruscanworkhandinhandwithSpringIntegrationadapterimplementations.ThemessagechannelsupportisafantasticwaytoextendthetransportandprotocolsupportinCitrusbyconnectingwiththeverygood

CitrusReferenceGuide

317File

Page 318: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SpringIntegrationadapterimplementations.HaveacloserlookattheSpringIntegrationprojectformoredetailsandotheradapterimplementationsthatyoucanusewithCitrusintegrationtesting.

CitrusReferenceGuide

318File

Page 319: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ApacheCamelsupportApacheCamelprojectimplementstheenterpriseintegrationpatternsforbuildingmediationandroutingrulesinyourenterpriseapplication.WiththeCitrusCamelsupportyouareabletodirectlyinteractwiththeApacheCamelcomponentsandroutedefinitions.YoucancallCamelroutesandreceivesynchronousresponsemessages.YoucanalsosimulatetheCamelrouteendpointwithreceivingmessagesandprovidingsimulatedresponsemessages.

NoteThecamelcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldaddthemoduleasMavendependencytoyourprojectaccordingly.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-camel</artifactId><version>2.7</version></dependency>

CitrusprovidesaspecialApacheCamelconfigurationschemathatisusedinourSpringconfigurationfiles.Youhavetoincludethecitrus-camelnamespaceinyourSpringconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-camel="http://www.citrusframework.org/schema/camel/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/camel/confighttp://www.citrusframework.org/schema/camel/config/citrus-camel-config.xsd">

[...]

</beans>

NowyouarereadytousetheCitrusApacheCamelconfigurationelementsusingthecitrus-camelnamespaceprefix.

ThenextsectionsexplaintheCitruscapabilitieswhileworkingwithApacheCamel.

CitrusReferenceGuide

319Camel

Page 320: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Camelendpoint

CamelandCitrusbothusetheendpointpatterninordertodefinemessagedestinations.Userscaninteractwiththeseendpointswhencreatingthemediationandroutinglogic.TheCitrusendpointcomponentforCamelinteractionisdefinedasfollowsinyourCitrusSpringconfiguration.

<citrus-camel:endpointid="directCamelEndpoint"endpoint-uri="direct:news"/>

RightnexttothatCitrusendpointweneedtheApacheCamelroutethatislocatedinsideacamelcontextcomponent.

<camelContextid="camelContext"xmlns="http://camel.apache.org/schema/spring"><routeid="newsRoute"><fromuri="direct:news"/><touri="log:com.consol.citrus.camel?level=INFO"/><touri="seda:news-feed"/></route></camelContext>

AsyoucanseetheCitruscamelendpointisabletointeractwiththeCamelroute.IntheexampleabovetheCamelcontextisplacedasSpringbeanCamelcontext.ThiswouldbetheeasiestsetuptouseCamelwithCitrusasyoucanaddtheCamelcontextstraighttotheSpringbeanapplicationcontext.OfcourseyoucanalsoimportyourCamelcontextandroutesfromotherSpringbeancontextfilesoryoucanstarttheCamelcontextrouteswithJavacode.

IntheexampletheApacheCamelrouteislisteningontherouteendpointuridirect:news.IncomingmessageswillbeloggedtotheconsoleusingalogCamelcomponent.AfterthatthemessageisforwardedtoasedaCamelcomponentwhichisasimplequeueinmemory.SowehaveasmallCamelroutinglogicwithtwodifferentmessagetransports.

TheCitrusendpointcaninteractwiththissampleroutedefinition.TheendpointconfigurationholdstheendpointuriinformationthattellsCitrushowtoaccesstheApacheCamelroutedestination.ThisendpointuricanbeanyCamelendpointurithatisusedinaCamelroute.Herewejustusethedirectendpointuridirect:newssothesampleCamelroutegetscalleddirectly.Inyourtestcaseyoucanusethisendpoint

CitrusReferenceGuide

320Camel

Page 321: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

componentreferencedbyitsidornameinordertosendandreceivemessagesontherouteaddressdirect:news.TheCamelroutelisteningonthisdirectaddresswillbeinvokedaccordingly.

TheApacheCamelroutessupportasynchronousandsynchronousmessagecommunicationpatterns.BydefaultCitrususesasynchronouscommunicationwithCamelroutes.ThismeansthattheCitrusproducersendstheexchangemessagetotherouteendpointuriandisfinishedimmediately.Thereisnosynchronousresponsetoawait.IncontrarytothatthesynchronousendpointwillsendandreceiveasynchronousmessageontheCameldestinationroute.Wewilldiscussthislateroninthischapter.FornowwehavealookonhowtousetheCitruscamelendpointinatestcaseinordertosendamessagetotheCamelroute:

<sendendpoint="directCamelEndpoint"><messagetype="plaintext"><payload>HellofromCitrus!</payload></message></send>

TheCitruscamelendpointcomponentcanalsobeusedinareceivemessageactioninyourtestcase.Inthissituationyouwouldreceiveamessagefromtherouteendpoint.ThisisespeciallydesignedforqueueingendpointroutessuchastheCamelsedacomponent.InourexampleCamelrouteabovethesedaCamelcomponentiscalledwiththeendpointuriseda:news-feed.ThismeansthattheCamelrouteissendingamessagetothesedacomponent.Citrusisabletoreceivethisroutemessagewithaendpointcomponentlikethis:

<citrus-camel:endpointid="sedaCamelEndpoint"endpoint-uri="seda:news-feed"/>

YoucanusetheCitruscamelendpointinyourtestcasereceiveactioninordertoconsumethemessageonthesedacomponent.

<receiveendpoint="sedaCamelEndpoint"><messagetype="plaintext"><payload>HellofromCitrus!</payload></message></receive>

CitrusReferenceGuide

321Camel

Page 322: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TipInsteadofdefiningastaticCitruscamelcomponentyoucouldalsousethedynamicendpointcomponentsinCitrus.Thiswouldenableyoutosendyourmessagedirectlyusingtheendpointuridirect:newsinyourtestcase.Readmoreaboutthisinendpoint-components.

CitrusisabletosendandreceivemessageswithCamelrouteendpointuri.ThisenablesyoutoinvokeaCamelroute.TheCamelcomponentsusedisdefinedbytheendpointuriasusual.WheninteractingwithCamelroutesyoumightneedtosendbacksomeresponsemessagesinordertosimulateboundaryapplications.Wewilldiscussthesynchronouscommunicationinthenextsection.

SynchronousCamelendpoint

ThesynchronousApacheCamelproducersendsamessagetosomerouteandwaitssynchronouslyfortheresponsetoarrive.InCamelthiscommunicationisrepresentedwiththeexchangepatternInOut.ThebasicconfigurationforasynchronousApacheCamelendpointcomponentlookslikefollows:

<citrus-camel:sync-endpointid="camelSyncEndpoint"endpoint-uri="direct:hello"timeout="1000"polling-interval="300"/>

Synchronousendpointspollforsynchronousreplymessagestoarrive.Thepollintervalisanoptionalsettinginordertomanagetheamountofreplymessagehandshakeattempts.Oncetheendpointwasabletoreceivethereplymessagesynchronouslythetestcasecanreceivethereply.Incasethereplymessageisnotavailableintimeweraisesometimeouterrorandthetestwillfail.

Inafirsttestscenariowewriteatestcasethesendsamessagetothesynchronousendpointandwaitsforthesynchronousreplymessagetoarrive.SowehavetwoactionsonthesameCitrusendpoint,firstsendthenreceive.

<sendendpoint="camelSyncEndpoint"><messagetype="plaintext"><payload>HellofromCitrus!</payload></message></send>

<receiveendpoint="camelSyncEndpoint"><messagetype="plaintext"><payload>ThisisthereplyfromApacheCamel!</payload>

CitrusReferenceGuide

322Camel

Page 323: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</message></receive>

Thenextvariationdealswiththesamesynchronouscommunication,butsendandreceiverolesareswitched.NowCitrusreceivesamessagefromaCamelrouteandhastoprovideareplymessage.WehandlethissynchronouscommunicationwiththesamesynchronousApacheCamelendpointcomponent.Onlydifferenceisthatweinitiallystartthecommunicationbyreceivingamessagefromtheendpoint.KnowingthisCitrusisabletosendasynchronousresponseback.Againjustusethesameendpointreferenceinyourtestcase.Sowehaveagaintwoactionsinourtestcase,butthistimefirstreceivethensend.

<receiveendpoint="camelSyncEndpoint"><messagetype="plaintext"><payload>HellofromApacheCamel!</payload></message></receive>

<sendendpoint="camelSyncEndpoint"><messagetype="plaintext"><payload>ThisisthereplyfromCitrus!</payload></message></send>

Thisisprettysimple.CitrustakescareonsettingtheApacheCamelexchangepatternInOutwhileusingsynchronouscommunications.TheCamelroutesdorespondandCitrusisabletoreceivethesynchronousmessagesaccordingly.WiththispatternyoucaninteractwithApacheCamelrouteswhereCitrussimulatessynchronousclientsandconsumers.

Camelexchangeheaders

ApacheCamelusesexchangeswhensendingandreceivingmessagestoandfromroutes.Theseexchangesholdspecificinformationonthecommunicationoutcome.Citrusautomaticallyconvertstheseexchangeinformationtospecialmessageheaderentries.Youcanvalidatethoseexchangeheaderstheneasilyinyourtestcase:

<receiveendpoint="sedaCamelEndpoint"><messagetype="plaintext"><payload>HellofromCamel!</payload></message><header>

CitrusReferenceGuide

323Camel

Page 324: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<elementname="citrus_camel_route_id"value="newsRoute"/><elementname="citrus_camel_exchange_id"value="ID-local-50532-1402653725341-0-3"/><elementname="citrus_camel_exchange_failed"value="false"/><elementname="citrus_camel_exchange_pattern"value="InOnly"/><elementname="CamelCorrelationId"value="ID-local-50532-1402653725341-0-1"/><elementname="CamelToEndpoint"value="seda://news-feed"/></header></receive>

BesidestheCamelspecificexchangeinformationtheCamelexchangedoesalsoholdsomecustomproperties.ThesepropertiessuchasCamelToEndpointorCamelCorrelationIdarealsoaddedautomaticallytotheCitrusmessageheadersocanexpecttheminareceivemessageaction.

Camelexceptionhandling

Letussupposefollowingroutedefinition:

<camelContextid="camelContext"xmlns="http://camel.apache.org/schema/spring"><routeid="newsRoute"><fromuri="direct:news"/><touri="log:com.consol.citrus.camel?level=INFO"/><touri="seda:news-feed"/><onException><exception>com.consol.citrus.exceptions.CitrusRuntimeException</exception><touri="seda:exceptions"/></onException></route></camelContext>

Theroutehasanexceptionhandlingblockdefinedthatiscalledassoonastheexchangeprocessingendsupinsomeerrororexception.WithCitrusyoucanalsosimulateaexchangeexceptionwhensendingbackasynchronousresponsetoacallingroute.

<sendendpoint="sedaCamelEndpoint"><messagetype="plaintext"><payload>Somethingwentwrong!</payload></message><header><elementname="citrus_camel_exchange_exception"value="com.consol.citrus.exceptions.CitrusRuntimeException"/><elementname="citrus_camel_exchange_exception_message"value="Somethingwentwrong!"/><elementname="citrus_camel_exchange_failed"value="true"/></header>

CitrusReferenceGuide

324Camel

Page 325: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</send>

Thismessageasresponsetotheseda:news-feedroutewouldcauseCameltoentertheexceptionhandlingintheroutedefinition.Theexceptionhandlingisactivatedandcallstheerrorhandlingrouteendpointseda:exceptions.OfcourseCitruswouldbeabletoreceivesuchanexceptionexchangevalidatingtheexceptionhandlingoutcome.

InsuchfailurescenariostheApacheCamelexchangeholdstheexceptioninformation(CamelExceptionCaught)suchascausingexceptionclassanderrormessage.TheseheadersarepresentinanerrorscenarioandcanbevalidatedinCitruswhenreceivingerrormessagesasfollows:

<receiveendpoint="errorCamelEndpoint"><messagetype="plaintext"><payload>Somethingwentwrong!</payload></message><header><elementname="citrus_camel_route_id"value="newsRoute"/><elementname="citrus_camel_exchange_failed"value="true"/><elementname="CamelExceptionCaught"value="com.consol.citrus.exceptions.CitrusRuntimeException:Somethingwentwrong!"/></header></receive>

ThiscompletesthebasicexceptionhandlinginCitruswhenusingtheApacheCamelendpoints.

Camelcontexthandling

IntheprevioussampleswehaveusedtheApacheCamelcontextasSpringbeancontextthatisautomaticallyloadedwhenCitrusstartsup.NowwhenusingasingleCamelcontextinstanceCitrusisabletoautomaticallypickthisCamelcontextforrouteinteraction.IfyouusemorethatoneCamelcontextyouhavetotelltheCitrusendpointcomponentwhichcontexttouse.Theendpointoffersanoptionalattributecalledcamel-context.

<citrus-camel:endpointid="directCamelEndpoint"camel-context="newsContext"endpoint-uri="direct:news"/>

<camelContextid="newsContext"xmlns="http://camel.apache.org/schema/spring">

CitrusReferenceGuide

325Camel

Page 326: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<routeid="newsRoute"><fromuri="direct:news"/><touri="log:com.consol.citrus.camel?level=INFO"/><touri="seda:news-feed"/></route></camelContext>

<camelContextid="helloContext"xmlns="http://camel.apache.org/schema/spring"><routeid="helloRoute"><fromuri="direct:hello"/><touri="log:com.consol.citrus.camel?level=INFO"/><touri="seda:hello"/></route></camelContext>

IntheexampleabpovewehavetwoCamelcontextinstancesloaded.Theendpointhastopickthecontexttousewiththeattributecamel-contextwhichresidestotheSpringbeanidoftheCamelcontext.

Camelrouteactions

SinceCitrus2.4weintroducedsomeCamelspecifictestactionsthatenableeasyinteractionwithCamelroutesandtheCamelcontext.ThetestactionsdofollowaspecificXMLnamespacesowehavetoaddthisnamespacetothetestcasewhenusingtheactions.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:camel="http://www.citrusframework.org/schema/camel/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/camel/testcasehttp://www.citrusframework.org/schema/camel/testcase/citrus-camel-testcase.xsd">

[...]

</beans>

Weaddedaspecialcamelnamespacewithprefixcamel:sonowwecanstarttoaddCameltestactionstothetestcase:

XMLDSL

<testcasename="CamelRouteIT">

CitrusReferenceGuide

326Camel

Page 327: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<actions><camel:create-routes><routeContextxmlns="http://camel.apache.org/schema/spring"><routeid="route_1"><fromuri="direct:test1"/><touri="mock:test1"/></route>

<routeid="route_2"><fromuri="direct:test2"/><touri="mock:test2"/></route></routeContext></camel:create-routes>

<camel:create-routescamel-context="camelContext"><routeContextxmlns="http://camel.apache.org/schema/spring"><route><fromuri="direct:test3"/><touri="mock:test3"/></route></routeContext></camel:create-routes></actions></testcase>

Intheexampleabovewehaveusedthecamel:create-routetestactionthatwillcreatenewCamelroutesatruntimeintheCamelcontext.ThetargetCamelcontextisspecifiedwiththeoptionalcamel-contextattribute.BydefaultCitruswillsearchforaCamelcontextavailableintheSpringbeanapplicationcontext.Removingroutesatruntimeisalsosupported.

XMLDSL

<testcasename="CamelRouteIT"><actions><camel:remove-routescamel-context="camelContext"><routeid="route_1"/><routeid="route_2"/><routeid="route_3"/></camel:remove-routes></actions></testcase>

NextoperationwewilldiscussisthestartandstopofexistingCamelroutes:

XMLDSL

CitrusReferenceGuide

327Camel

Page 328: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<testcasename="CamelRouteIT"><actions><camel:start-routescamel-context="camelContext"><routeid="route_1"/></camel:start-routes>

<camel:stop-routescamel-context="camelContext"><routeid="route_2"/><routeid="route_3"/></camel:stop-routes></actions></testcase>

StartingandstoppingCamelroutesatruntimeisimportantwhentemporarilyCitrusneedtoreceiveamessageonaCamelendpointURI.Wecanstoparoute,useaCitruscamelendpointinsteadforvalidationandstarttherouteafterthetestisdone.ThiswaywencanalsosimulateerrorsandfailurescenariosinaCamelrouteinteraction.

OfcourseallCamelrouteactionsarealsoavailableinJavaDSL.

JavaDSL

@AutowiredprivateCamelContextcamelContext;

@CitrusTestpublicvoidcamelRouteTest()camel().context(camelContext).create(newRouteBuilder(camelContext)@Overridepublicvoidconfigure()throwsExceptionfrom("direct:news").routeId("route_1").autoStartup(false).setHeader("headline",simple("ThisisBIGnews!")).to("mock:news");

from("direct:rumors").routeId("route_2").autoStartup(false).setHeader("headline",simple("Thisisjustarumor!")).to("mock:rumors"););

camel().context(camelContext).start("route_1","route_2");

camel().context(camelContext).stop("route_2");

camel().context(camelContext).remove("route_2");

CitrusReferenceGuide

328Camel

Page 329: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AsyoucanseewehaveaccesstotheCamelroutebuilderthatadds1-nnewCamelroutestothecontext.Afterthatwecanstart,stopandremovetherouteswithinthetestcase.

Camelcontrolbusactions

TheCamelcontrolbuscomponentisagoodwaytoaccessroutestatisticsandroutestatusinformationwithinaCamelcontext.Citrusprovidescontrolbustestactionstoeasilyaccessthecontrolbusoperationsatruntime.

XMLDSL

<testcasename="CamelControlBusIT"><actions><camel:control-bus><camel:routeid="route_1"action="start"/></camel:control-bus>

<camel:control-buscamel-context="camelContext"><camel:routeid="route_2"action="status"/><camel:result>Stopped</camel:result></camel:control-bus>

<camel:control-bus><camel:languagetype="simple">$camelContext.stop()</camel:language></camel:control-bus>

<camel:control-buscamel-context="camelContext"><camel:languagetype="simple">$camelContext.getRouteStatus('route_3')</camel:language<camel:result>Started</camel:result></camel:control-bus></actions></testcase>

Theexampletestcaseshowsthecontrolbusaccess.Camelprovidestwodifferentwaystospecifyoperationsandparameters.Thefirstoptionistheuseofanactionattribute.TheCamelrouteidhastobespecifiedasmandatoryattribute.Asaresultthecontrolbusactionwillbeexecutedonthetargetrouteduringtestruntime.ThiswaywecanalsostartandstopCamelroutesinaCamelcontext.

CitrusReferenceGuide

329Camel

Page 330: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Incaseancontrolbusoperationhasaresultsuchasthestatusactionwecanspecifyacontrolresultthatiscompared.Citruswillraisevalidationexceptionswhentheresultsdiffer.Thesecondoptionforexecutingacontrolbusactionisthelanguageexpression.WecanuseCamellanguageexpressionsontheCamelcontextforaccessingacontrolbusoperation.Alsoherewecandefineanoptionaloutcomeasexpectedresult.

TheJavaDSLalsosupportsthesecontrolbusoperationsasthenextexampleshows:

JavaDSL

@AutowiredprivateCamelContextcamelContext;

@CitrusTestpublicvoidcamelRouteTest()camel().controlBus().route("my_route","start");

camel().controlBus().language(SimpleBuilder.simple("$camelContext.getRouteStatus('my_route')")).result(ServiceStatus.Started);

TheJavaDSLworkswithCamellanguageexpressionbuildersaswellasServiceStatusenumvaluesasexpectedresult.

CitrusReferenceGuide

330Camel

Page 331: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Vert.xeventbussupportVert.xisanapplicationplatformfortheJVMthatprovidesanetworkeventbusforlightweightscalablemessagingsolutions.TheCitrusVert.xcomponentsdoparticipateonthateventbusmessagingasproducerorconsumer.WiththesecomponentsyoucanaccessVert.xinstancesavailableinyournetworkinordertotestthoseVert.xapplicationsinsomeintegrationtestscenario.

NoteTheVert.xcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldaddthemoduleasMavendependencytoyourprojectaccordingly.

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-vertx</artifactId><version>2.7</version></dependency>

CitrusprovidesaspecialVert.xconfigurationschemathatisusedinourSpringconfigurationfiles.Youhavetoincludethecitrus-vertxnamespaceinyourSpringconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-vertx="http://www.citrusframework.org/schema/vertx/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/vertx/confighttp://www.citrusframework.org/schema/vertx/config/citrus-vertx-config.xsd">

[...]

</beans>

NowyouarereadytousetheCitrusVert.xconfigurationelementsusingthecitrus-vertxnamespaceprefix.

ThenextsectionsdiscusssendingandreceivingoperationsontheVert.xeventbuswithCitrus.

CitrusReferenceGuide

331Vertx

Page 332: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Vert.xendpoint

AsusualCitrususesanendpointcomponentinordertospecifysomemessagedestinationtosendandreceivemessagestoandfrom.TheVert.xendpointcomponentisdefinedasfollowsinyourCitrusSpringconfiguration.

<citrus-vertx:endpointid="simpleVertxEndpoint"host="localhost"port="5001"pubSubDomain="false"address="news-feed"/>

<beanid="vertxInstanceFactory"class="com.consol.citrus.vertx.factory.CachingVertxInstanceFactory"

TheendpointholdssomegeneralinformationhowtoaccesstheVert.xeventbus.HostandportvaluesdefinetheVert.xHazelcastclusterhostnameandport.CitrusstartsanewVert.xinstanceusingthiscluster.SoallotherVert.xinstancesconnectedtothisclusterhostwillreceivetheeventbusmessagesfromCitrusduringthetest.Inyourtestcaseyoucanusethisendpointcomponentreferencedbyitsidornameinordertosendandreceivemessagesontheeventbusaddressnews-feed.InVert.xtheeventbusaddressdefinesthedestinationforeventconsumerstolistenon.Asalreadymentionedclusterhostnameandportareoptional,soCitruswilluselocalhostandanewrandomportontheclusterhostifnothingisspecified.

TheVert.xeventbussupportspublish-subscribeandpoint-to-pointmessagecommunicationpatterns.BydefaultthepubSubDomaininCitrusisfalsesotheeventbussenderwillinitiateapoint-to-pointcommunicationontheeventbusaddress.Thismeansthatonlyonesingleconsumerontheeventbusaddresswillreceivethemessage.Iftherearemoreconsumersontheaddressthefirsttocomewinsandreceivesthemessage.Incontrarytothatthepublish-subscribescenariowoulddeliverthemessagetoallavailableconsumersontheeventbusaddresssimultaneously.YoucanenablethepubSubDomainontheVert.xendpointcomponentforthiscommunicationpattern.

TheVert.xendpointneedsainstancefactoryimplementationinordertocreatetheembeddedVert.xinstance.BydefaultthebeannamevertxInstanceFactoryisrecognizedbyallVert.xendpointcomponents.WewilltalkaboutVert.xinstancefactoriesinmoredetaillateroninthischapter.

CitrusReferenceGuide

332Vertx

Page 333: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AsmessagecontentyoucansendandreceiveJSONobjectsorsimplecharactersequencestotheeventbus.LetushavealookatasimplesamplesendingactionthatusesthenewVert.xendpointcomponent:

<sendendpoint="simpleVertxEndpoint"><messagetype="plaintext"><payload>HellofromCitrus!</payload></message></send>

AstheVert.xCitrusendpointisbidirectionalyoucanalsoreceivemessagesfromtheeventbus.

<receiveendpoint="simpleVertxEndpoint"><messagetype="plaintext"><payload>HellofromVert.x!</payload></message><header><elementname="citrus_vertx_address"value="news-feed"/></header></receive>

Citrusautomaticallyaddssomespecialmessageheaderstothemessage,soyoucanvalidatetheVert.xeventbusaddress.ThiscompletesthesimplesendandreceiveoperationsonaVert.xeventbus.NowletsmoveontosynchronousendpointswhereCitruswaitsforareplyontheeventbus.

SynchronousVert.xendpoint

ThesynchronousVert.xeventbusproducersendsamessageandwaitssynchronouslyfortheresponsetoarriveonsomereplyaddressdestination.Thereplyaddressnameisgeneratedautomaticallyandsetintherequestmessageheaderattributessothereceivingcounterpartinthiscommunicationcansenditsreplytothateventbusaddress.ThebasicconfigurationforasynchronousVert.xendpointcomponentlookslikefollows:

<citrus-vertx:sync-endpointid="vertxSyncEndpoint"address="hello"timeout="1000"polling-interval="300"/>

CitrusReferenceGuide

333Vertx

Page 334: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Synchronousendpointspollforsynchronousreplymessagestoarriveontheeventbusreplyaddress.Thepollintervalisanoptionalsettinginordertomanagetheamountofreplymessagehandshakeattempts.Oncetheendpointwasabletoreceivethereplymessagesynchronouslythetestcasecanreceivethereply.Incaseallmessagehandshakeattemptsdofailbecausethereplymessageisnotavailableintimeweraisesometimeouterrorandthetestwillfail.

NoteTheVert.xendpointusestemporaryreplyaddressdestinations.Thetemporaryreplyaddressingeneratedandisonlyusedonceforasinglecommunicationhandshake.Afterthatthereplyaddressisdismissedagain.

WhensendingamessagetothesynchronousVert.xendpointtheproducerwillwaitsynchronouslyfortheresponsemessagetoarriveonthereplyaddress.Youcanreceivethereplymessageinyourtestcaseusingthesameendpointcomponent.Sowehavetwoactionsonthesameendpoint,firstsendthenreceive.

<sendendpoint="vertxSyncEndpoint"><messagetype="plaintext"><payload>HellofromCitrus!</payload></message></send>

<receiveendpoint="vertxSyncEndpoint"><messagetype="plaintext"><payload>ThisisthereplyfromVert.x!</payload></message></receive>

Inthelastsectionwesawthatsynchronouscommunicationisbasedonreplymessagesontemporaryreplyeventbusaddress.WesawthatCitrusisabletosendmessagestoeventbusaddressandwaitforreplymessagestoarrive.Thisnextsectiondealswiththesamesynchronouscommunication,butsendandreceiverolesareswitched.NowCitrusreceivesamessageandhastosendareplymessagetoatemporaryreplyaddress.

WehandlethissynchronouscommunicationwiththesamesynchronousVert.xendpointcomponent.Onlydifferenceisthatweinitiallystartthecommunicationbyreceivingamessagefromtheendpoint.KnowingthisCitrusisabletosendasynchronousresponseback.Againjustusethesameendpointreferenceinyourtestcase.Thehandlingofthetemporaryreplyaddressisdoneautomaticallybehindthescenes.Sowehaveagaintwoactionsinourtestcase,butthistimefirstreceivethensend.

<receiveendpoint="vertxSyncEndpoint">

CitrusReferenceGuide

334Vertx

Page 335: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<messagetype="plaintext"><payload>HellofromVert.x!</payload></message></receive>

<sendendpoint="vertxSyncEndpoint"><messagetype="plaintext"><payload>ThisisthereplyfromCitrus!</payload></message></send>

ThesynchronousmessageendpointforVert.xeventbuscommunicationwillhandleallreplyaddressdestinationsandprovidethosebehindthescenes.

Vert.xinstancefactory

CitrusstartsanembeddedVert.xinstanceatruntimeinordertoparticipateintheVert.xcluster.WithinthisclustermultipleVert.xinstancesareconnectedviatheeventbus.ForstartingtheVert.xeventbusCitrususesaclusterhostnameandportdefinition.Youcancustomizethisclusterhostinordertoconnecttoaveryspecialclusterinyournetwork.

NowCitrusneedstomanagetheVert.xinstancescreatedduringthetestrun.BydefaultCitruswilllookforainstancefactorybeannamedvertxInstanceFactory.Youcanchoosethefactoryimplementationtouseinyourproject.BydefaultyoucanusethecachingfactoryimplementationthatcachestheVert.xinstancessowedonotconnectmorethanoneVert.xinstancetothesameclusterhost.Citrusoffersfollowinginstancefactoryimplementations:

com.consol.citrus.vertx.factory.CachingVertxInstanceFactory-defaultimplementationthatreusestheVert.xinstancebasedongivenclusterhostandport.Withthisimplementationweensureto

connectasingleCitrusVert.xinstancetoaclusterhost.

com.consol.citrus.vertx.factory.SingleVertxInstanceFactory-createsasingleVert.xinstanceandreusesthisinstanceforallendpoints.YoucanalsosetyourverycustomVert.xinstanceviaconfiguration

forcustomVert.xinstantiation.

CitrusReferenceGuide

335Vertx

Page 336: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheinstancefactoryimplementationsdoimplementtheVertxInstanceFactoryinterface.Soyoucanalsoprovideyourveryspecialimplementation.BydefaultCitruslooksforabeannamedvertxInstanceFactorybutyoucanalsodefineyourveryspecialfactoryimplementationonmanendpointcomponent.TheVert.xinstancefactoryissetontheVert.xendpointasfollows:

<citrus-vertx:endpointid="vertxHelloEndpoint"address="hello"vertx-factory="singleVertxInstanceFactory"/>

<beanid="singleVertxInstanceFactory"class="com.consol.citrus.vertx.factory.SingleVertxInstanceFactory"/>

CitrusReferenceGuide

336Vertx

Page 337: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

MailsupportSendingandreceivingmailsisthenextinterestwearegoingtotalkabout.WhendealingwithmailcommunicationyoumostcertainlyneedtointeractwithsomesortofIMAPorPOPmailserver.ButinCitruswedonotwanttomanagemailsinapersonalinbox.Wejustneedtobeabletoexchangemailmessagesthepersistinginauserinboxisnotpartofourbusiness.

ThisiswhyCitrusprovidesjustaSMTPmailserverwhichacceptsmailmessagesfromclients.OncetheSMTPserverhasacceptedanincomingmailitforwardsthosedatatotherunningtestcase.Inthetestcaseyoucanreceivetheincomingmailmessageandperformmessagevalidationasusual.ThemailsendingpartiseasyasCitrusoffersamailclientthatconnectstosomeSMTPserverforsendingmailstotheoutsideworld.

NoteThemailcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldcheckthatthemoduleisavailableasMavendependencyinyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-mail</artifactId><version>2.7</version></dependency>

AsusualCitrusprovidesacustomizedmailconfigurationschemathatisusedinSpringconfigurationfiles.Simplyincludethecitrus-mailnamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-mail="http://www.citrusframework.org/schema/mail/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/mail/confighttp://www.citrusframework.org/schema/mail/config/citrus-mail-config.xsd">

[...]

</beans>

CitrusReferenceGuide

337Mail

Page 338: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowyouarereadytousethecustomizedHttpconfigurationelementswiththecitrus-mailnamespaceprefix.

ReadthenextsectioninordertofindoutmoreaboutthemailmessagesupportinCitrus.

Mailclient

Themailsendingpartisquiteeasyandstraightforward.WejustneedtosendamailmessagetosomeSMTPserver.SoCitrusprovidesamailclientthatsendsoutmailmessages.

<citrus-mail:clientid="simpleMailClient"host="localhost"port="25025"/>

ThisishowaCitrusmailclientcomponentisdefinedintheSpringapplicationcontext.Youcanusethisclientreferencedbyitsidornameinyourtestcaseinamessagesendingaction.TheclientdefinesahostandportattributewhichshouldconnecttheclienttosomeSMTPserverinstance.

Weallknowmailmessagecontents.Themailmessagehassomegeneralpropertiessetbytheuser:

from:Themessagesendermailaddressto:Themessagerecipientmailaddress.Youcanaddmultiplerecipientsbyusingacommaseparatedlist.cc:Copyrecipientmailaddress.Youcanaddmultiplerecipientsbyusingacommaseparatedlist.bcc:Blindcopyrecipientmailaddress.Youcanaddmultiplerecipientsbyusingacommaseparatedlist.subject:Somesubjectusedasmailheadline.

Asatesteryouareabletosetthesepropertiesinyourtestcase.CitrusdefinesaXMLmailmessagerepresentationthatyoucanuseinsideyoursendaction.Letushavealookatthis:

<sendendpoint="simpleMailClient"><message><payload><mail-messagexmlns="http://www.citrusframework.org/schema/mail/message"><from>[email protected]</from>

CitrusReferenceGuide

338Mail

Page 339: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<to>[email protected]</to><cc></cc><bcc></bcc><subject>Thisisatestmailmessage</subject><body><contentType>text/plain;charset=utf-8</contentType><content>HelloCitrusmailserver!</content></body></mail-message></payload></message></send>

ThebasicXMLmailmessagerepresentationdefinesalistofbasicmailpropertiessuchasfrom,toorsubject.InadditiontothatwedefineatextbodywhichiseitherplaintextorHTML.Youcanspecifythecontenttypeofthemailbodyveryeasy(e.g.text/plainortext/html).BydefaultCitrususestext/plaincontenttype.

Nowwhendealingwithmailmessagesyouoftencometousemultipartstructuresforattachments.InCitrusyoucandefineattachmentcontentasbase64charactersequence.TheCitrusmailclientwillautomaticallycreateapropermultipartmailmimemessageusingthecontenttypesandbodypartsspecified.

<sendendpoint="simpleMailClient"><message><payload><mail-messagexmlns="http://www.citrusframework.org/schema/mail/message"><from>[email protected]</from><to>[email protected]</to><cc></cc><bcc></bcc><subject>Thisisatestmailmessage</subject><body><contentType>text/plain;charset=utf-8</contentType><content>HelloCitrusmailserver!</content><attachments><attachment><contentType>text/plain;charset=utf-8</contentType><content>Thisisattachmentdata</content><fileName>attachment.txt</fileName></attachment></attachments></body></mail-message></payload></message></send>

CitrusReferenceGuide

339Mail

Page 340: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thatcompletesthebasicmailclientcapabilities.Butwaitwehavenottalkedabouterrorscenarioswheremailcommunicationresultsinerror.Whenrunningintomailerrorscenarioswehavetohandletheerrorrespectivelywithexceptionhandling.WhenthemailserverrespondedwitherrorsCitruswillraisemailexceptionsautomaticallyandyourtestcasefailsaccordingly.

Asatesteryoucancatchandassertthesemailexceptionsverifyingyourerrorscenario.

<assertexception="org.springframework.mail.MailSendException"><when><sendendpoint="simpleMailClient"><message><payload><mail-messagexmlns="http://www.citrusframework.org/schema/mail/message">[...]</mail-message></payload></message></send></when><assert/>

WeasserttheMailSendExceptionfromSpringtobethrownwhilesendingthemailmessagetotheSMTPserver.Withexceptionmessagevalidationyouareabletoexpectveryspecificmailsenderrorsontheclientside.Thisishowyoucanhandlesomesortoferrorsituationreturnedbythemailserver.SpeakingofmailserversweneedtoalsotalkaboutprovidingamailserverendpointinCitrusforclients.Thisispartofournextsection.

Mailserver

Consumingmailmessagesisamorecomplicatedtaskasweneedtohavesomesortofserverthatclientscanconnectto.InyourmailclientsoftwareyoutypicallypointtosomeIMAPorPOPinboxandreceivemailsfromthatendpoint.InCitruswedonotwanttomanageawholepersonalmailinboxsuchasIMAPorPOPwouldprovide.WejustneedaSMTPserverendpointforclientstosendmailsto.TheSMTPserveracceptsmailmessagesandforwardsthosetoarunningtestcaseforfurthervalidation.

NoteWehavenouserinboxwhereincomingmailsarestored.Themailserverjustforwardsincomingmailstotherunningtestforvalidation.Afterthetesttheincomingmailmessageisgone.

CitrusReferenceGuide

340Mail

Page 341: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AndthisisexactlywhattheCitrusmailserveriscapableof.TheserverisaverylightweightSMTPserver.AllincomingmailclientconnectionsareacceptedbydefaultandthemaildataisconvertedintoaCitrusXMLmailinterfacerepresentation.TheXMLmailmessageisthenpassedtotherunningtestforvalidation.

LetushavealookattheCitrusmailservercomponentandhowyoucanaddittotheSpringapplicationcontext.

<citrus-mail:serverid="simpleMailServer"port="25025"auto-start="true"/>

Themailservercomponentreceivesseveralpropertiessuchasportorauto-start.CitrusstartsainmemorySMTPserverthatclientscanconnectto.

InyourtestcaseyoucanthenreceivetheincomingmailmessagesontheserverinordertoperformthewellknownXMLvalidationmechanismswithinCitrus.Themessageheaderandthepayloadcontainallmailinformationsoyoucanverifythecontentwithexpectedtemplatesasusual:

<receiveendpoint="simpleMailServer"><message><payload><mail-messagexmlns="http://www.citrusframework.org/schema/mail/message"><from>[email protected]</from><to>[email protected]</to><cc></cc><bcc></bcc><subject>Thisisatestmailmessage</subject><body><contentType>text/plain;charset=utf-8</contentType><content>HelloCitrusmailserver!</content></body></mail-message></payload><header><elementname="citrus_mail_from"value="[email protected]"/><elementname="citrus_mail_to"value="[email protected]"/><elementname="citrus_mail_subject"value="Thisisatestmailmessage"/><elementname="citrus_mail_content_type"value="text/plain;charset=utf-8"/></header></message></receive>

CitrusReferenceGuide

341Mail

Page 342: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thegeneralmailpropertiessuchasfrom,to,subjectareavailableaselementsinthemailpayloadandinthemessageheaderinformation.ThemessageheadernamesdostartwithacommonCitrusmailprefixcitrus_mail.Followingfromthatyoucanverifythesespecialmailmessageheadersinyourtestasshownabove.Citrusoffersfollowingmailheaders:

citrus_mail_fromcitrus_mail_tocitrus_mail_cccitrus_mail_bcccitrus_mail_subjectcitrus_mail_replyTocitrus_mail_date

InadditiontothatCitrusconvertstheincomingmaildatatoaspecialXMLmailrepresentationwhichispassedasmessagepayloadtothetest.Themailbodypartsarerepresentedasbodyandoptionalattachmentelements.AsthisisplainXMLyoucanverifythemailmessagecontentasusualusingCitrusvariables,functionsandvalidationmatchers.

RegardlessofhowthemailmessagehaspassedthevalidationtheCitrusSMTPmailserverwillautomaticallyrespondwithsuccesscodes(SMTP250OK)tothecallingclient.ThisisthebasicCitrusmailserverbehaviorwhereallclientconnectionsareacceptedanallmailmessagesarerespondedwithSMTP250OKresponsecodes.

Nowinmoreadvancedusagescenariosthetestermaywanttocontrolthemailcommunicationoutcome.UsercanforcesomeerrorscenarioswheremailclientsarenotacceptedormailcommunicationshouldfailwithsomeSMTPerrorstateforinstance.

Byusingamoreadvancedmailserversetupthetestergetsmorepowertosendingbackmailserverresponsecodestothemailclient.Justusetheadvancedmailadapterimplementationinyourmailservercomponentconfiguration:

<citrus-mail:serverid="advancedMailServer"auto-accept="false"split-multipart="true"port="25025"auto-start="true"/>

CitrusReferenceGuide

342Mail

Page 343: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Wehavedisabledtheauto-acceptmodeonthemailserver.Thismeansthatwehavetodosomeadditionalstepsinyourtestcasetoaccepttheincomingmailmessagefirst.Sowecandecideinourtestcasewhethertoacceptordeclinetheincomingmailmessageforamorepowerfultest.Youaccept/declineamailmessagewithaspecialXMLacceptrequest/responseexchangeinyourtestcase:

<receiveendpoint="advancedMailServer"><message><payload><accept-requestxmlns="http://www.citrusframework.org/schema/mail/message"><from>[email protected]</from><to>[email protected]</to></accept-request></payload></message></receive>

Sobeforereceivingtheactualmailmessagewereceivethissimpleaccept-requestinourtest.Theacceptrequestgivesusthemessagefromandtoresourcesofthemailmessage.Nowthetestdecidestoalsodeclineamailclientconnection.Youcansimulatethattheserverdoesnotacceptthemailclientconnectionbysendingbackanegativeacceptresponse.

<sendendpoint="advancedMailServer"><message><payload><accept-responsexmlns="http://www.citrusframework.org/schema/mail/message"><accept>true</accept></accept-response></payload></message></send>

Dependingontheacceptoutcomethemailclientwillreceiveanerrorresponsewithpropererrorcodes.Ifyouacceptthemailmessagewithapositiveacceptresponsethenextstepinyourtestreceivestheactualmailmessageaswehaveseenitbeforeinthischapter.

Nowbesidesnotacceptingamailmessageinthefirstplaceyoucanalssimulateanothererrorscenariowiththemailserver.InthisscenariothemailservershouldrespondwithsomesortofSMTPerrorcodeafteracceptingthemessage.Thisisdonewithaspecialmailresponsemessagelikethis:

CitrusReferenceGuide

343Mail

Page 344: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<receiveendpoint="advancedMailServer"><message><payload><mail-messagexmlns="http://www.citrusframework.org/schema/mail/message"><from>[email protected]</from><to>[email protected]</to><cc></cc><bcc></bcc><subject>Thisisatestmailmessage</subject><body><contentType>text/plain;charset=utf-8</contentType><content>HelloCitrusmailserver!</content></body></mail-message></payload></message></receive>

<sendendpoint="advancedMailServer"><message><payload><mail-responsexmlns="http://www.citrusframework.org/schema/mail/message"><code>443</code><message>Failed!</message></mail-response></payload></message></send>

Asyoucanseefromtheexampleabovewefirstaccepttheconnectionandreceivethemailcontentasusual.Nowthetestreturnsanegativemailresponsewithsomeerrorcodereasonset.TheCitrusSMTPcommunicationwillthenfailandthecallingmailclientreceivestherespectiveerror.

IfyouskipthenegativemailresponsetheserverwillautomaticallyresponsewithpositiveSMTPresponsecodestothecallingclient.

CitrusReferenceGuide

344Mail

Page 345: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ArquilliansupportArquillianisawellknownintegrationtestframeworkthatcomeswithagreatfeaturesetwhenitcomestoJavaEEtestinginsideofafullqualifiedapplicationserver.WithArquiliianyoucandeployyourJavaEEservicesinarealapplicationserverofyourchoiceandexecutethetestsinsidetheapplicationserverboundaries.ThismakesitveryeasytotestyourJavaEEservicesinscopewithproperJNDIresourceallocationandotherresourcesprovidedbytheapplicationserver.CitrusisabletoconnectwiththeArquilliantestcase.SpeakinginmoredetailyourArquilliantestisabletouseaCitrusextensioninordertousetheCitrusfeaturesetinsidetheArquillianboundaries.

ReadthenextsectioninordertofindoutmoreabouttheCitrusArquillianextension.

CitrusArquillianextension

ArquillianoffersafinemechanismforextensionsaddingfeaturestotheArquilliantestsetupandtestexecution.TheCitrusextensionrespectivelyaddsCitrusframeworkinstancecreationandCitrustestexecutiontotheArquillianworld.Firstofallletshavealookattheextensiondescriptorpropertiessettableviaarquillian.xml:

<extensionqualifier="citrus"><propertyname="citrusVersion">2.7</property><propertyname="autoPackage">true</property><propertyname="suiteName">citrus-arquillian-suite</property></extension>

TheCitrusextensionusesaspecificqualifiercitrusfordefiningpropertiesinsidetheArquilliandescriptor.Followingpropertiesaresettableincurrentversion:

citrusVersion:TheexplicitversionofCitrusthatshouldbeused.Besuretohavethesamelibraryversionavailableinyourproject(e.g.asMavendependency).Thispropertyisoptional.

Bydefaulttheextensionjustusesthelateststableversion.

autoPackage:Whentrue(defaultsetting)theextensionwillautomaticallyaddCitruslibrariesandalltransitivedependenciestothetestdeployment.ThisautomaticallyenablesyoutousetheCitrusAPIinsidetheArquilliantest

evenwhenthetestisexecutedinsidetheapplicationcontainer.

CitrusReferenceGuide

345Arquillian

Page 346: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

suiteName:ThisoptionalsettingdefinesthenameofthetestsuitethatisusedfortheCitrustestrun.Whenusingbefore/aftersuitefunctionalityinCitrusthissettingmightbeofinterest.configurationClass:FullqualifiedJavaclassnameofcustomizedCitrusSpringbeanconfigurationtousewhenloadingtheCitrusSpringapplicationcontext.Asauseryoucandefineacustomconfigurationclassthatmust

beasubclassofcom.consol.citrus.config.CitrusSpringConfig.Whenspecifiedthecustomclassisloadedotherwisethedefaultcom.consol.citrus.config.CitrusSpringConfigisloadedtosetuptheSpringapplicationcontext.

NowthatwehaveaddedtheextensiondescriptorwithallpropertiesweneedtoaddtherespectiveCitrusArquillianextensionaslibrarytoourproject.ThisisdoneviaMaveninyourproject'sPOMfileasnormaldependency:

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-arquillian</artifactId><version>2.7</version><scope>test</scope></dependency>

NoweverythingissetuptouseCitruswithinArquillian.LetsuseCitrusfunctionalityinaArquilliantestcase.

Clientsidetesting

Arquillianseparatesclientandcontainersidetesting.Whenusingclientsidetestingthetestcaseisexecutedoutsideoftheapplicationcontainerdeployment.ThismeansthatyourtestcasehasnodirectaccesstocontainermanagedresourcessuchasJNDIresources.Ontheplussideitisnotnecessarytoincludeyourtestinthecontainerdeployment.Thetestcaseinteractswiththecontainerdeploymentasanormalclientwoulddo.Letshavealookatafirstexample:

@RunWith(Arquillian.class)@RunAsClientpublicclassEmployeeResourceTest

@CitrusFrameworkprivateCitruscitrusFramework;

CitrusReferenceGuide

346Arquillian

Page 347: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@ArquillianResourceprivateURLbaseUri;

privateStringserviceUri;

@DeploymentpublicstaticWebArchivecreateDeployment()returnShrinkWrap.create(WebArchive.class).addClasses(RegistryApplication.class,EmployeeResource.class,Employees.class,Employee.class,EmployeeRepository.class);

@BeforepublicvoidsetUp()throwsMalformedURLExceptionserviceUri=newURL(baseUri,"registry/employee").toExternalForm();

@Test@CitrusTestpublicvoidtestCreateEmployeeAndGet(@CitrusResourceTestDesignerdesigner)designer.send(serviceUri).message(newHttpMessage("name=Penny&age=20").method(HttpMethod.POST).contentType(MediaType.APPLICATION_FORM_URLENCODED));

designer.receive(serviceUri).message(newHttpMessage().statusCode(HttpStatus.NO_CONTENT));

designer.send(serviceUri).message(newHttpMessage().method(HttpMethod.GET).accept(MediaType.APPLICATION_XML));

designer.receive(serviceUri).message(newHttpMessage(""+""+"20"+"Penny"+""+"").statusCode(HttpStatus.OK));

citrusFramework.run(designer.build());

FirstofallweusethebasicArquillianJUnittestrunner@RunWith(Arquillian.class)incombinationwiththe@RunAsClientannotationtellingArquillianthatthisisaclientsidetestcase.AsthisisausualArquilliantestcasewehaveaccesstoArquillianresources

CitrusReferenceGuide

347Arquillian

Page 348: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

thatautomaticallygetinjectedsuchasthebaseuriofthetestdeployment.ThetestdeploymentisawebdeploymentcreatedviaShrinkWrap.WeaddtheapplicationspecificclassesthatbuildourremoteRESTfulservicethatwewouldliketotest.

TheCitrusArquillianextensionisabletosetupaproperCitrustestenvironmentinthebackground.AsaresultthetestcasecanreferenceaCitrusframeworkinstancewiththe@CitrusFrameworkannotation.WewillusethisinstanceofCitruslateronwhenitcomestoexecutetheCitrustestinglogic.

NowecanfocusonwritingatestmethodwhichisagainnothingbutanormalJUnittestmethod.TheCitrusextensiontakescareoninjectingthe@CitrusResourceannotatedmethodparameter.WiththisCitrustestdesignerinstancewecanbuildaCitrustestlogicforsendingandreceivingmessagesviaHttpinordertocalltheremoteRESTfulemployeeserviceofourtestdeployment.TheHttpendpointuriisinjectedviaArquillianandweareabletocalltheremoteserviceasaclient.

TheCitrustestdesignerprovidesJavaDSLmethodsforbuildingthetestlogic.Pleasenotethatthedesignerwillaggregateallactionssuchassendorreceiveuntilthedesigneriscalledtobuildthetestcasewithbuild()methodinvocation.TheresultingtestcaseobjectcanbeexecutedbytheCitrusframeworkinstancewithrun()method.

WhentheCitrustestcaseisexecutedthemessagesaresentoverthewire.TherespectiveresponsemessageisreceivedwithwellknownCitrusreceivemessagelogic.Wecanvalidatetheresponsemessagesaccordinglyandmakesuretheclientcallwasdoneright.IncasesomethinggoeswrongwithinCitrustestexecutiontheframeworkwillraiseexceptionsaccordingly.AsaresulttheJUnittestmethodissuccessfulorfailedwitherrorscomingfromCitrustestexecution.

ThisishowCitrusandArquilliancaninteractinatestscenariowherethetestdeploymentismanagedbyArquillianandtheclientsideactionstakeplacewithinCitrus.ThisisagreatwaytocombinebothframeworkswithCitrusbeingabletocalldifferentserviceAPIendpointsinadditionwithvalidatingtheoutcome.Thiswasaclientsidetestcasewherethetestlogicwasexecutedoutsideoftheapplicationcontainer.Arquillianalsosupportscontainerremotetestcaseswherewehavedirectaccesstocontainermanagedresources.ThefollowingsectiondescribeshowthisworkswithCitrus.

Containersidetesting

InprevioussectionswehaveseenhowtocombineCitruswithArquillianinaclientsidetestcase.Thisisthewaytogoforalltestcasesthatdonotneedtohaveaccessoncontainermanagedresources.Letshavealookatasamplewherewewanttogain

CitrusReferenceGuide

348Arquillian

Page 349: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

accesstoaJMSqueueandconnectionmanagedbytheapplicationcontainer.

@RunWith(Arquillian.class)publicclassEchoServiceTest

@CitrusFrameworkprivateCitruscitrusFramework;

@Resource(mappedName="jms/queue/test")privateQueueechoQueue;

@Resource(mappedName="/ConnectionFactory")privateConnectionFactoryconnectionFactory;

privateJmsSyncEndpointjmsSyncEndpoint;

@Deployment@OverProtocol("Servlet3.0")publicstaticWebArchivecreateDeployment()throwsMalformedURLExceptionreturnShrinkWrap.create(WebArchive.class).addClasses(EchoService.class);

@BeforepublicvoidsetUp()JmsSyncEndpointConfigurationendpointConfiguration=newJmsSyncEndpointConfiguration();endpointConfiguration.setConnectionFactory(newSingleConnectionFactory(connectionFactory));endpointConfiguration.setDestination(echoQueue);jmsSyncEndpoint=newJmsSyncEndpoint(endpointConfiguration);

@AfterpublicvoidcleanUp()closeConnections();

@Test@CitrusTestpublicvoidshouldBeAbleToSendMessage(@CitrusResourceTestDesignerdesigner)throwsExceptionStringmessageBody="ping";

designer.send(jmsSyncEndpoint).messageType(MessageType.PLAINTEXT).message(newJmsMessage(messageBody));

designer.receive(jmsSyncEndpoint).messageType(MessageType.PLAINTEXT).message(newJmsMessage(messageBody));

citrusFramework.run(designer.build());

CitrusReferenceGuide

349Arquillian

Page 350: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

privatevoidcloseConnections()((SingleConnectionFactory)jmsSyncEndpoint.getEndpointConfiguration().getConnectionFactory()).destroy();

AsyoucanseethetestcaseaccessestwocontainermanagedresourcesviaJNDI.ThisisaJMSqueueandaJMSconnectionthatgetautomaticallyinjectedasresources.InabeforetestannotatedmethodwecanusetheseresourcestobuildupaproperCitrusJMSendpoint.InsidethetestmethodwecanusetheJMSendpointforsendingandreceivingJMSmessagesviaCitrus.Asusualresponsemessagesreceivedarevalidatedandcomparedtoanexpectedmessage.AsusualweusetheCitrusTestDesignermethodparameterthatisinjectedbytheframework.ThedesignerisabletobuildCitrustestlogicwithJavaDSLmethods.Oncethecompletetestisdesignedwecanbuildthetestcaseandrunthetestcasewiththeframeworkinstance.AfterthetestweshouldclosetheJMSconnectioninordertoavoidexceptionswhentheapplicationcontainerisshuttingdownafterthetest.

Thetestisnowpartofthetestdeploymentandisexecutedwithintheapplicationcontainerboundaries.AsusualwecanusetheCitrusextensiontoautomaticallyinjecttheCitrusframeworkinstanceaswellastheCitrustestbuilderinstanceforbuildingtheCitrustestlogic.

ThisishowtocombineCitrusandArquillianinordertobuildintegrationtestsonJavaEEservicesinarealapplicationcontainerenvironment.WithCitrusyouareabletosetupmorecomplextestscenarioswithsimulatedservicessuchasmailorftpservers.WecanbuildCitrusendpointswithcontainermanagedresources.

Testrunners

IntheprevioussectionswehaveusedtheCitrusTestDesignerinordertoconstructaCitrustestcasetoexecutewithintheArquillianboundaries.ThenatureofthetestdesigneristoaggregateallJavaDSLmethodcallsinordertobuildacompleteCitrustestcasebeforeexecutionisdoneviatheCitrusframework.ThisapproachcancausesomeunexpectedbehaviorwhenmixingtheCitrusJavaDSLmethodcallswithArquilliantestlogic.LetsdescribethisbyhavingalookatanexamplewherethmixtureoftestdesignerandpureJavatestlogiccausesunseenproblems.

@Test@CitrusTest

CitrusReferenceGuide

350Arquillian

Page 351: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

publicvoidtestDesignRuntimeMixture(@CitrusResourceTestDesignerdesigner)throwsExceptiondesigner.send(serviceUri).message(newHttpMessage("name=Penny&age=20").method(HttpMethod.POST).contentType(MediaType.APPLICATION_FORM_URLENCODED));

designer.receive(serviceUri).message(newHttpMessage()).statusCode(HttpStatus.NO_CONTENT));

EmployeetestEmployee=employeeService.findEmployee("Penny");employeeService.addJob(testEmployee,"waitress");

designer.send(serviceUri).message(newHttpMessage().method(HttpMethod.GET).accept(MediaType.APPLICATION_XML));

designer.receive(serviceUri).message(newHttpMessage(""+""+"20"+"Penny"+""+"waitress"+""+""+"")).statusCode(HttpStatus.OK));

citrusFramework.run(designer.build());

AsyoucanseeinthisexamplewecreateanewEmployeenamedPennyviatheHttpRESTAPIonourservice.WedothiswithCitrusHttpsendandreceivemessagelogic.Oncethisisdonewewouldliketoaddajobdescriptiontotheemployee.WeuseaserviceinstanceofEmployeeServicewhichisaserviceofourtestdomainthatisinjectedtotheArquilliantestascontainerJEEresource.Firstofallwefindtheemployeeobjectandthenweaddsomejobdescriptionusingtheservice.NowasaresultwewouldliketoreceivetheemployeeasXMLrepresentationviaaRESTservicecallwithCitrusandweexpectthejobdescriptiontobepresent.

ThiscombinationofCitrusJavaDSLmethodsandservicecalllogicwillnotworkwithTestDesigner.ThisisbecausetheCitrustestlogicisnotexecutedimmediatelybutaggregatedtotheveryendwherethedesigneriscalledtobuildthetestcase.ThecombinationofCitrusdesigntimeandJavatestruntimeistricky.

CitrusReferenceGuide

351Arquillian

Page 352: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FortunatelywehavesolvedthisissuewithprovidingaseparateTestRunnercomponent.ThetestrunnerprovidesnearlythesameJavaDSLmethodsforconstructingCitrustestlogicasthetestdesigner.ThedifferencethoughisthatthetestlogicisexecutedimmediatelywhencallingtheJavaDSLmethods.SofollowingfromthatwecanmixCitrusJavaDSLcodewithtestruntimelogicasexpected.Seehowthislookslikewithourexample:

@Test@CitrusTestpublicvoidtestDesignRuntimeMixture(@CitrusResourceTestRunnerrunner)throwsExceptionrunner.send(newBuilderSupport<SendMessageBuilder>()@Overridepublicvoidconfigure(SendMessageBuilderbuilder)builder.endpoint(serviceUri).message(newHttpMessage("name=Penny&age=20").method(HttpMethod.POST).contentType(MediaType.APPLICATION_FORM_URLENCODED)););

runner.receive(newBuilderSupport<ReceiveMessageBuilder>()@Overridepublicvoidconfigure(ReceiveMessageBuilderbuilder)builder.endpoint(serviceUri).message(newHttpMessage().statusCode(HttpStatus.NO_CONTENT)););

EmployeetestEmployee=employeeService.findEmployee("Penny");employeeService.addJob(testEmployee,"waitress");

runner.send(newBuilderSupport<SendMessageBuilder>()@Overridepublicvoidconfigure(SendMessageBuilderbuilder)builder.endpoint(serviceUri).message(newHttpMessage().method(HttpMethod.GET).accept(MediaType.APPLICATION_XML)););

runner.receive(newBuilderSupport<ReceiveMessageBuilder>()@Overridepublicvoidconfigure(ReceiveMessageBuilderbuilder)builder.endpoint(serviceUri).message(newHttpMessage(""+""+"20"+

CitrusReferenceGuide

352Arquillian

Page 353: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

"Penny"+""+"waitress"+""+""+"").statusCode(HttpStatus.OK)););

Thetestlogichasnotchangedsignificantly.WeusetheCitrusTestRunnerasmethodinjectedparameterinsteadoftheTestDesigner.Andthisisprettymuchthetrick.NowtheJavaDSLmethodsdoexecutetheCitrustestlogicimmediately.ThisiswhythesyntaxoftheCitrusJavaDSLmethodshavechangedalittlebit.Wenowuseaanonymousinterfaceimplementationforconstructingthesend/receivetestactionlogic.AsaresultwecanusetheCitrusJavaDSLasnormalcodeandwecanmixtheruntimeJavalogicaseachstatementisexecutedimmediately.

WithJava8lambdaexpressionsourcodelooksevenmorestraightforwardandlessverboseaswecanskiptheanonymousinterfaceimplementations.WithJava8youcanwritethesametestlikethis:

@Test@CitrusTestpublicvoidtestDesignRuntimeMixture(@CitrusResourceTestRunnerrunner)throwsExceptionrunner.send(builder->builder.endpoint(serviceUri).message(newHttpMessage("name=Penny&age=20").method(HttpMethod.POST).contentType(MediaType.APPLICATION_FORM_URLENCODED));

runner.receive(builder->builder.endpoint(serviceUri).message(newHttpMessage().statusCode(HttpStatus.NO_CONTENT));

EmployeetestEmployee=employeeService.findEmployee("Penny");employeeService.addJob(testEmployee,"waitress");

runner.send(builder->builder.endpoint(serviceUri).message(newHttpMessage().method(HttpMethod.GET).accept(MediaType.APPLICATION_XML));

runner.receive(builder->builder.endpoint(serviceUri).message(newHttpMessage(""+""+"20"+

CitrusReferenceGuide

353Arquillian

Page 354: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

"Penny"+""+"waitress"+""+""+"").statusCode(HttpStatus.OK));

CitrusReferenceGuide

354Arquillian

Page 355: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DockersupportCitrusprovidesconfigurationcomponentsandtestactionsforinteractionwithaDockerdaemon.TheCitrusdockerclientcomponentwillexecuteDockercommandsforcontainermanagementsuchasstart,stop,build,inspectandsoon.TheDockerclientbydefaultusestheDockerremoteRESTAPI.AsauseryoucanexecuteDockercommandsaspartofaCitrustestandvalidatepossiblecommandresults.

NoteTheDockertestcomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-docker</artifactId><version>2.7</version></dependency>

Citrusprovidesa"citrus-docker"configurationnamespaceandschemadefinitionforDockerrelatedcomponentsandactions.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusDockerconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-docker="http://www.citrusframework.org/schema/docker/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/docker/confighttp://www.citrusframework.org/schema/docker/config/citrus-docker-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

Dockerclient

CitrusReferenceGuide

355Docker

Page 356: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusoperateswiththeDockerremoteRESTAPIinordertointeractwiththeDockerdaemon.TheDockerclientisdefinedasSpringbeancomponentintheconfigurationasfollows:

<citrus-docker:clientid="dockerClient"/>

TheDockerclientcomponentaboveisusingalldefaultconfigurationvalues.BydefaultCitrusissearchingthesystempropertiesaswellasenvironmentvariablesfordefaultDockersettingssuchas:

DOCKER_HOST="tcp://localhost:2376"DOCKER_CERT_PATH="~/.docker/machine/machines/default"DOCKER_TLS_VERIFY="1"DOCKER_MACHINE_NAME="default"

IncasethesesettingsarenotsettableinyourenvironmentyoucanalsouseexplicitsettingsintheDockerclientcomponent:

<citrus-docker:clientid="dockerClient"url="tcp://localhost:2376"version="1.20"username="user"password="s!cr!t"email="[email protected]"registry="https://index.docker.io/v1/"cert-path="/path/to/some/cert/directory"config-path="/path/to/some/config/directory"/>

NowCitrusisabletoaccesstheDockerremoteAPIforexecutingcommandssuchasstart,stop,build,inspectandsoon.

Dockercommands

WehaveseveralCitrustestactionseachrepresentingaDockercommand.TheseactionscanbepartofatestcasewhereyoucanmanageDockercontainersinsidethetest.AsaprerequisitewehavetoenabletheDockerspecifictestactionsinourXMLtestasfollows:

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:docker="http://www.citrusframework.org/schema/docker/testcase"xsi:schemaLocation="

CitrusReferenceGuide

356Docker

Page 357: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/docker/testcasehttp://www.citrusframework.org/schema/docker/testcase/citrus-docker-testcase.xsd">

[...]

</beans>

Weaddedaspecialdockernamespacewithprefixdocker:sonowwecanstarttoaddDockertestactionstothetestcase:

XMLDSL

<testcasename="DockerCommandIT"><actions><docker:ping></docker:ping>

<docker:version><docker:expect><docker:result><![CDATA["Version":"1.8.3","ApiVersion":"1.21","GitCommit":"@ignore@","GoVersion":"go1.4.2","Os":"darwin","Arch":"amd64","KernelVersion":"@ignore@"]]></docker:result></docker:expect></docker:version></actions></testcase>

InthisverysimpleexamplewefirstpingtheDockerdaemontomakesurewehaveconnectivityupandrunning.AfterthatwegettheDockerversioninformation.ThesecondactionshowsanimportantconceptwhenexecutingDockercommandsinCitrus.Asatesterwemightbeinterestedinvalidatingthecommandresult.Sowencanspecifyanoptionaldocker:resultwhichisusuallyinJSONdataformat.AsusualwecanusetestvariableshereandignoresomevaluesexplicitlysuchastheGitCommitvalue.

BasedonthatwecanexecuteseveralDockercommandsinatestcase:

CitrusReferenceGuide

357Docker

Page 358: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLDSL

<testcasename="DockerCommandIT"><variables><variablename="imageId"value="busybox"></variable><variablename="containerName"value="citrus_box"></variable></variables>

<actions><docker:pullimage="$imageId"tag="latest"/>

<docker:createimage="$imageId"name="$containerName"cmd="top"><docker:expect><docker:result><![CDATA["Id":"@variable(containerId)@","Warnings":null]]></docker:result></docker:expect></docker:create>

<docker:startcontainer="$containerName"/></actions></testcase>

InthisexamplewepullaDockerimage,buildanewcontaineroutofthisimageandstartthecontainer.AsyoucanseeeachDockercommandactionoffersattributessuchascontainer,imageortag.ThesearecommandsettingsthatareavailableontheDockercommandspecification.ReadmoreabouttheDockercommandsandthespecificsettingsinofficialDockerAPIreferenceguide.

CitrussupportsthefollowingDockercommandswithrespectivetestactions:

docker:pulldocker:builddocker:createdocker:startdocker:stopdocker:waitdocker:pingdocker:versiondocker:inspect

CitrusReferenceGuide

358Docker

Page 359: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

docker:removedocker:info

SomeoftheDockercommandscanbeexecutedbothoncontainerandimagetargetssuchasdocker:inspectordocker:remove.Thecommandactionthenoffersbothcontainerandimageattributessotheusercanchoosethetargetofthecommandoperationtobeacontaineroranimage.

UptonowwehaveonlyusedtheCitrusXMLDSL.OfcourseallDockercommandsarealsoavailableinJavaDSLasthenextexampleshows.

JavaDSL

@CitrusTestpublicvoiddockerTest()docker().version().validateCommandResult(newCommandResultCallback<Version>()@OverridepublicvoiddoWithCommandResult(Versionversion,TestContextcontext)Assert.assertEquals(version.getApiVersion(),"1.20"););

docker().ping();

docker().start("my_container");

TheJavaDSLDockercommandsprovideanoptionalCommandResultCallbackthatiscalledwiththeunmarshalledcommandresultobject.IntheexampleabovetheVersionmodelobjectispassedasargumenttothecallback.Sothetestercanaccessthecommandresultandvalidateitspropertieswithassertions.

BydefaultCitrustriestofindaDockerclientcomponentwithintheCitrusSpringapplicationcontext.IfnotpresentCitruswillinstantiateadefaultdockerclientwithalldefaultsettings.YoucanalsoexplicitlysetthedockerclientinstancewhenusingtheJavaDSLDockercommandactions:

JavaDSL

@AutowiredprivateDockerClientdockerClient;

@CitrusTestpublicvoiddockerTest()

CitrusReferenceGuide

359Docker

Page 360: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

docker().client(dockerClient).version().validateCommandResult(newCommandResultCallback<Version>()@OverridepublicvoiddoWithCommandResult(Versionversion,TestContextcontext)Assert.assertEquals(version.getApiVersion(),"1.20"););

docker().client(dockerClient).ping();

docker().client(dockerClient).start("my_container");

CitrusReferenceGuide

360Docker

Page 361: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

KubernetessupportKubernetesisoneofthehottestmanagementplatformsforcontainerizedapplicationsthesedays.Kubernetesletsyoudeploy,scaleandmanageyourcontainersontheplatformsoyougetfeatureslikeauto-scaling,self-healing,servicediscoveryandloadbalancing.CitrusprovidesinteractionwiththeKubernetesRESTAPIsoyoucanaccesstheKubernetesplatformanditsresourceswithinaCitrustestcase.

NoteTheKubernetestestcomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-kubernetes</artifactId><version>2.7</version></dependency>

Citrusprovidesa"citrus-kubernetes"configurationnamespaceandschemadefinitionforKubernetesrelatedcomponentsandactions.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusKubernetesconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-k8s="http://www.citrusframework.org/schema/kubernetes/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/kubernetes/confighttp://www.citrusframework.org/schema/kubernetes/config/citrus-kubernetes-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

CitrusReferenceGuide

361Kubernetes

Page 362: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Kubernetesclient

CitrusoperateswiththeKubernetesremoteRESTAPIinordertointeractwiththeKubernetesplatform.TheKubernetesclientisdefinedasSpringbeancomponentintheconfigurationasfollows:

<citrus-k8s:clientid="myK8sClient"/>

TheKubernetesclientisbasedontheFabric8JavaKubernetesclientimplementation.Followingfromthatthecomponentcanbeconfiguredinvariousways.BydefaulttheclientreadsthesystempropertiesaswellasenvironmentvariablesfordefaultKubernetessettingssuchas:

kubernetes.master/KUBERNETES_MASTERkubernetes.api.version/KUBERNETES_API_VERSIONkubernetes.trust.certificates/KUBERNETES_TRUST_CERTIFICATES

Ifyousetthesepropertiesinyourenvironmenttheclientcomponentwillautomaticallypickuptheconfigurationsettings.Alsowhenusingkubectlcommandlinelocallytheclientmayautomaticallyusethestoreduserauthenticationsettingsfromthere.ForacompletelistofsettingsandexplanationofthosepleaserefertotheFabric8clientdocumentation.

IncaseyouneedtosettheclientconfigurationexplicitlyonyourenvironmentyoucanalsouseexplicitsettingsontheKubernetesclientcomponent:

<citrus-k8s:clientid="myK8sClient"url="http://localhost:8843"version="v1"username="user"password="s!cr!t"namespace="user_namespace"message-converter="messageConverter"object-mapper="objectMapper"/>

NowCitrusisabletoaccesstheKubernetesremoteAPIforexecutingcommandssuchaslist-pods,watch-servicesandsoon.CitrusprovidesasetofactionsthatperformaKubernetescommandviaREST.TheresultsusuallygetvalidatedintheCitrustestasusual.

BasedonthatwecanexecuteseveralKubernetescommandsinatestcaseandvalidatetheJsonresults:

CitrusReferenceGuide

362Kubernetes

Page 363: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrussupportsthefollowingKubernetesAPIcommandswithrespectivetestactions:

k8s:infok8s:list-podsk8s:get-podk8s:delete-podk8s:list-servicesk8s:get-servicek8s:delete-servicek8s:list-namespacesk8s:list-eventsk8s:list-endpointsk8s:list-nodesk8s:list-replication-controllersk8s:watch-podsk8s:watch-servicesk8s:watch-namespacesk8s:watch-nodesk8s:watch-replication-controllers

Wewilldiscussthesecommandsindetaillateroninthischapter.FornowletshaveacloserlookonhowtousethecommandsinsideofaCitrustest.

KubernetescommandsinXML

WehaveseveralCitrustestactionseachrepresentingaKubernetescommand.TheseactionscanbepartofatestcasewhereyoucanmanageKubernetespodsinsidethetest.AsaprerequisitewehavetoenabletheKubernetesspecifictestactionsinourXMLtestasfollows:

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:k8s="http://www.citrusframework.org/schema/kubernetes/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/kubernetes/testcasehttp://www.citrusframework.org/schema/kubernetes/testcase/citrus-kubernetes-testcase.xsd"

[...]

</beans>

CitrusReferenceGuide

363Kubernetes

Page 364: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Weaddedaspecialkubernetesnamespacewithprefixk8s:sonowwecanstarttoaddKubernetestestactionstothetestcase:

XMLDSL

<testcasename="KubernetesCommandIT"><actions><k8s:infoclient="myK8sClient"><k8s:validate><k8s:result>"result":"clientVersion":"1.4.27","apiVersion":"v1","kind":"Info","masterUrl":"$masterUrl","namespace":"test"</k8s:result></k8s:validate></k8s:info>

<k8s:list-pods><k8s:validate><k8s:result>"result":"apiVersion":"v1","kind":"PodList","metadata":"@ignore@","items":[]</k8s:result><k8s:elementpath="$.result.items.size()"value="0"/></k8s:validate></k8s:list-pods></actions></testcase>

InthisverysimpleexamplewefirstpingtheKubernetesRESTAPItomakesurewehaveconnectivityupandrunning.TheinfocommandconnectstheRESTAPIandreturnsalistofstatusinformationoftheKubernetesclient.AfterthatwegetthelistofavailableKubernetespods.Asatesterwemightbeinterestedinvalidatingthecommandresults.Sowencanspecifyanoptionalk8s:resultwhichisusuallyinJsonformat.WiththatwecanapplythefullCitrusJsonvalidationpowertotheKubernetes

CitrusReferenceGuide

364Kubernetes

Page 365: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

results.Asusualwecanusetestvariableshereandignoresomevaluesexplicitlysuchasthemetadatavalue.AlsoJsonPathexpressionvalidationandJsontestmessagevalidationfeaturesinCitruscomeinheretovalidatetheresults.

KubernetescommandsinJava

UptonowwehaveonlyusedtheCitrusXMLDSL.OfcourseallKubernetescommandsarealsoavailableinJavaDSLasthenextexampleshows.

JavaDSL

@CitrusTestpublicvoidkubernetesTest()kubernetes().info().validate(newCommandResultCallback<InfoResult>()@OverridepublicvoiddoWithCommandResult(InfoResultinfo,TestContextcontext)Assert.assertEquals(info.getApiVersion(),"v1"););

kubernetes().pods().list().withoutLabel("running").label("app","myApp");

TheJavaDSLKubernetescommandsprovideanoptionalCommandResultCallbackthatisautomaticallycalledwiththeunmarshalledcommandresultobject.IntheexampleabovetheInfoResultmodelobjectispassedasargumenttothecallback.Sothetestercanaccessthecommandresultandvalidateitspropertieswithassertions.

Java8Lambdaexpressionsaddsomesyntacticalsugartothecommandresultvalidation:

JavaDSL

@CitrusTestpublicvoidkubernetesTest()kubernetes().info().validate((info,context)->Assert.assertEquals(info.getApiVersion(),"v1"));

kubernetes().pods().list()

CitrusReferenceGuide

365Kubernetes

Page 366: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.withoutLabel("running").label("app","myApp");

BydefaultCitrustriestofindaKubernetesclientcomponentwithintheCitrusSpringapplicationcontext.IfnotpresentCitruswillinstantiateadefaultkubernetesclientwithalldefaultsettings.YoucanalsoexplicitlysetthekubernetesclientinstancewhenusingtheJavaDSLKubernetescommandactions:

JavaDSL

@AutowiredprivateKubernetesClientkubernetesClient;

@CitrusTestpublicvoidkubernetesTest()kubernetes().client(kubernetesClient).info().validate((info,context)->Assert.assertEquals(info.getApiVersion(),"v1"));

kubernetes().client(kubernetesClient).pods().list().withoutLabel("running").label("app","myApp");

Infocommand

TheinfocommandjustgetstheclientconnectionsettingsandprovidesthemasaJsonresulttotheaction.

XMLDSL

<k8s:infoclient="myK8sClient"><k8s:validate><k8s:result>"result":"clientVersion":"1.4.27","apiVersion":"v1","kind":"Info","masterUrl":"$masterUrl","namespace":"test"

CitrusReferenceGuide

366Kubernetes

Page 367: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</k8s:result></k8s:validate></k8s:info>

JavaDSL

@CitrusTestpublicvoidinfoTest()kubernetes().info().validate((info,context)->Assert.assertEquals(info.getApiVersion(),"v1"));

Listresources

WecanlistKubernetesresourcessuchaspods,services,endpointsandreplicationcontrollers.Thelistcanbefilteredbyseveralpropertiessuchas

labelnamespace

Thetestactionisabletodefinerespectivefilterstothelistsowegetonlypodsthematchthegivenattributes:

XMLDSL

<k8s:list-podslabel="app=todo"><k8s:validate><k8s:result>"result":"apiVersion":"$apiVersion","kind":"PodList","metadata":"@ignore@","items":"@ignore@"</k8s:result><k8s:elementpath="$.result.items.size()"value="1"/><k8s:elementpath="$..status.phase"value="Running"/></k8s:validate></k8s:list-pods>

JavaDSL

@CitrusTestpublicvoidlistPodsTest()

CitrusReferenceGuide

367Kubernetes

Page 368: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

kubernetes().client(k8sClient).pods().list().label("app=todo").validate("$..status.phase","Running").validate((pods,context)->Assert.assertFalse(CollectionUtils.isEmpty(pods.getResult().getItems())););

Asyoucanseeweareabletogivethepodlabelthatissearchedforinlistofallpods.ThelistreturnedisvalidatedeitherbygivinganexpectedJsonmessageorbyaddingJsonPathexpressionswithexpectedvaluestocheck.

InJavaDSLwecanaddavalidationresultcallbackthatisprovidedwiththeunmarshalledresultobjectforvalidation.Besideslabelfilteringwecanalsospecifythenamespaceandthepodnametosearchfor.

Youcanalsodefinemultiplelabelsascommadelimitedlist:

<k8s:list-serviceslabel="stage!=test,provider=fabric8"namespace="default"/>

Asyoucanseewehavecombinedtolabelfiltersstage!=testandprovider=fabric8onpodsinnamespacedefault.Thefirstlabelfilterisnegatedsothelabelstageshouldnotbetesthere.

Listnodesandnamespaces

Nodesandnamespacesarespecialresourcesthatarenotfilteredbytheirnamespaceastheyaremoreglobalresources.Therestisprettysimilartolistingpodsorservices.Wecanaddfilteressuchasnameandlabel.

XMLDSL

<k8s:list-namespaceslabel="provider=citrus"><k8s:validate><k8s:elementpath="$.result.items.size()"value="1"/></k8s:validate></k8s:list-namespaces>

JavaDSL

CitrusReferenceGuide

368Kubernetes

Page 369: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CitrusTestpublicvoidlistPodsTest()kubernetes().client(k8sClient).namespaces().list().label("provider=citrus").validate((pods,context)->Assert.assertFalse(CollectionUtils.isEmpty(pods.getResult().getItems())););

Getresources

WecangetaveryspecialKubernetesresourcesuchasapodorservicefordetailedvalidationofthatresource.WeneedtospecifyaresourcenameinordertoselecttheresourcefromlistofavailableresourcesinKubernetes.

XMLDSL

<k8s:get-podname="citrus_pod"><k8s:validate><k8s:result>"result":"apiVersion":"$apiVersion","kind":"Pod","metadata":"annotations":"@ignore@","creationTimestamp":"@ignore@","finalizers":[],"generateName":"@startsWith('hello-minikube-')@","labels":"pod-template-hash":"@ignore@","run":"hello-minikube","name":"$podName","namespace":"default","ownerReferences":"@ignore@","resourceVersion":"@ignore@","selfLink":"/api/$apiVersion/namespaces/default/pods/$podName","uid":"@ignore@","spec":"containers":["args":[],"command":[],"env":[],"image":"gcr.io/google_containers/echoserver:1.4",

CitrusReferenceGuide

369Kubernetes

Page 370: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

"imagePullPolicy":"IfNotPresent","name":"hello-minikube","ports":["containerPort":8080,"protocol":"TCP"],"resources":,"terminationMessagePath":"/dev/termination-log","volumeMounts":"@ignore@"],"dnsPolicy":"ClusterFirst","imagePullSecrets":"@ignore@","nodeName":"minikube","restartPolicy":"Always","securityContext":"@ignore@","serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":30,"volumes":"@ignore@","status":"@ignore@"</k8s:result><k8s:elementpath="$..status.phase"value="Running"/></k8s:validate></k8s:get-pod>

JavaDSL

@CitrusTestpublicvoidgetPodsTest()kubernetes().client(k8sClient).pods().get("citrus_pod").validate("$..status.phase","Running").validate((pod,context)->Assert.assertEquals(pods.getResult().getStatus().getPhase(),"Running"););

AsyoucanseeweareablegetthecompletepodinformationfromKubernetes.TheresultisvalidatedwithJsonmessagevalidatorinCitrus.Thismeanswecanuse@ignore@aswellastestvariablesandJsonPathexpressions.

Createresources

CitrusReferenceGuide

370Kubernetes

Page 371: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WecancreatenewKubernetesresourceswithinaCitrustest.Thisisveryimportantincaseweneedtosetupnewpodsorservicesforthetestrun.Youcancreatenewresourcesbygivinga.ymlfileholdingallinformationhowtocreatethenewresource.SeethefollowingsampleYAMLforanewpodandservice:

kind:PodapiVersion:v1metadata:name:hello-jetty-$randomIdnamespace:defaultselfLink:/api/v1/namespaces/default/pods/hello-jetty-$randomIduid:citrus:randomUUID()labels:server:hello-jettyspec:containers:-name:hello-jettyimage:jetty:9.3imagePullPolicy:IfNotPresentports:-containerPort:8080protocol:TCPrestartPolicy:AlwaysterminationGracePeriodSeconds:30dnsPolicy:ClusterFirstserviceAccountName:defaultserviceAccount:defaultnodeName:minikube

ThisYAMLfilespecifiesanewresourceofkindPod.Wedefinethemetadataaswellasallcontainersthatarepartofthispod.Thecontainerisbuildfromjetty:9.3DockerimagethatshouldbepulledautomaticallyfromDockerHubregistry.Wealsoexposeport8080ascontainerPortsotheupcomingserviceconfigurationcanprovidethisporttoclientsasKubernetesservice.

kind:ServiceapiVersion:v1metadata:name:hello-jettynamespace:defaultselfLink:/api/v1/namespaces/default/services/hello-jettyuid:citrus:randomUUID()labels:service:hello-jettyspec:ports:-protocol:TCP

CitrusReferenceGuide

371Kubernetes

Page 372: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

port:8080targetPort:8080nodePort:31citrus:randomNumber(3)selector:server:hello-jettytype:NodePortsessionAffinity:None

Theserviceresourcemapstheport8080andselectsallpodswithlabelserver=hello-jetty.Thismakesthejettycontaineravailabletoclients.TheservicetypeisNodePortwhichmeansthatclientsoutsideofKubernetesarealsoabletoaccesstheservicebyusingthedynamicportnodePort=31xxx.WecanuseCitrusfunctionssuchasrandomNumberintheYAMLfiles.

InthetestcasewecanusetheseYAMLfilestocreatetheresourcesinKubernetes:

XMLDSL

<k8s:create-podnamespace="default"><k8s:templatefile="classpath:templates/hello-jetty-pod.yml"/></k8s:create-pod>

<k8s:create-servicenamespace="default"><k8s:templatefile="classpath:templates/hello-jetty-service.yml"/></k8s:create-service>

JavaDSL

@CitrusTestpublicvoidcreatePodsTest()kubernetes().pods().create(newClassPathResource("templates/hello-jetty-pod.yml")).namespace("default");

kubernetes().services().create(newClassPathResource("templates/hello-jetty-service.yml")).namespace("default");

Creatingnewresourcesmaytakesometimetofinish.Kuberneteswillhavetopullimages,buildcontainersandstartupeverything.Thecreateactionisnotwaitingsynchronouslyforallthattohavehappened.Thereforewemightaddalist-podsactionthatwaitsforthenewresourcestoappear.

CitrusReferenceGuide

372Kubernetes

Page 373: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<repeat-onerror-until-truecondition="@assertThat('greaterThan(9)')@"auto-sleep="1000"><k8s:list-podslabel="server=hello-jetty"><k8s:validate><k8s:elementpath="$.result.items.size()"value="1"/><k8s:elementpath="$..status.phase"value="Running"/></k8s:validate></k8s:list-pods></repeat-onerror-until-true>

Withthisrepeatonerroractionwewaitforthenewserver=hello-jettylabeledpodtobeinstateRunning.

Deleteresources

WiththatcommandweareabletodeletearesourceinKubernetes.Uptonowdeletionofpodsandservicesissupported.Wehavetogiveanameoftheresourcethatwewanttodelete.

XMLDSL

<k8s:delete-podname="citrus_pod"><k8s:validate><k8s:elementpath="$.result.success"value="true"/></k8s:validate></k8s:delete-pod>

JavaDSL

@CitrusTestpublicvoiddeletePodsTest()kubernetes().pods().delete("citrus_pod").validate((result,context)->Assert.assertTrue(result.getResult().getSuccess()));

Watchresources

NoteThewatchoperationisstillinexperimentalstateandmayfacesevereadjustmentsandimprovementsinnearfuture.

CitrusReferenceGuide

373Kubernetes

Page 374: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WhenusingawatchcommandweaddasubscriptiontochangeeventsonaKubernetesresources.Sowecanwatchresourcessuchaspods,servicesforfuturechanges.Eachchangeonthatresourcetriggersanewwatcheventresultthatwecanexpectandvalidate.

XMLDSL

<k8s:watch-podslabel="provider=citrus"><k8s:validate><k8s:elementpath="$.action"value="DELETED"/></k8s:validate></k8s:watch-pods>

JavaDSL

@CitrusTestpublicvoidlistPodsTest()kubernetes().pods().watch().label("provider=citrus").validate((watchEvent,context)->Assert.assertFalse(watchEvent.hasError());Assert.assertEquals(((WatchEventResult)watchEvent).getAction(),Watcher.Action.DELETED););

NoteThewatchcommandmaybetriggeredseveraltimesformultiplechangesontherespectiveKubernetesresource.Thewatchactionwillalwayshandleonesingleeventresult.Thefirsteventtriggerisforwardedtotheactionvalidation.Allfurtherwatcheventsonthatsameresourceareignored.Thismeansthatyoumayneedmultiplewatchactionsinyourtestcaseincaseyouexpectmultiplewatcheventstobetriggered.

Kubernetesmessaging

WehaveseenhowtoaccesstheKubernetesremoteRESTAPIbyusingspecialCitrustestactionsinouttest.Asanalternativetothatwecanalsousemoregenericsend/receiveactionsinCitrusforaccessingtheKubernetesAPI.Wedemonstratethiswithasimpleexample:

XMLDSL

CitrusReferenceGuide

374Kubernetes

Page 375: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<testcasename="KubernetesSendReceiveIT"><actions><sendendpoint="k8sClient"><message><data>"command":"info"</data></message></send>

<receiveendpoint="k8sClient"><messagetype="json"><data>"command":"info","result":"clientVersion":"1.4.27","apiVersion":"v1","kind":"Info","masterUrl":"$masterUrl","namespace":"test"</data></message></receive>

<echo><message>Listallpods</message></echo>

<sendendpoint="k8sClient"><message><data>"command":"list-pods"</data></message></send>

<receiveendpoint="k8sClient"><messagetype="json"><data>"command":"list-pods","result":"apiVersion":"v1","kind":"PodList","metadata":"@ignore@","items":[]</data><validatepath="$.result.items.size()"value="0"/></message></receive>

CitrusReferenceGuide

375Kubernetes

Page 376: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</actions></testcase>

Asyoucanseewecanusethesend/receiveactionstocallKubernetesAPIcommandsandreceivetherespectiveresultsinJsonformat,too.ThisgivesusthewellknownJsonvalidationmechanisminCitrusinordertovalidatetheresultsfromKubernetes.ThiswayyoucanloadKubernetesresourcesverifyingitsstateandproperties.OfcourseJsonPathexpressionsalsocomeinhereinordertovalidateJsonelementsexplicitly.

CitrusReferenceGuide

376Kubernetes

Page 377: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SSHsupportInthespiritofotherCitrusmockservices,thereissupportforsimulatinganexternalSSHserveraswellasforconnectingtoSSHserversasaclientduringthetestexecution.CitrustranslatesSSHrequestsandresponsestosimpleXMLdocumentsforbettervalidationwiththecommonCitrusmechanisms.

ThismeansthattheCitrustestcasedoesnotdealwithpureSSHprotocolcommands.InsteadofthisweusethepowerfulXMLvalidationcapabilitiesinCitruswhendealingwiththesimpleXMLdocumentsthatrepresenttheSSHrequest/responsedata.

Letusclarifythiswithalittleexample.OncetherealSSHserverdaemonisfiredupwithinCitrusweacceptaSSHEXECrequestforinstance.TherequestistranslatedintoaXMLmessageofthefollowingformat:

<ssh-requestxmlns="http://www.citrusframework.org/schema/ssh/message"><command>cat-|sed-e's/Hello/HelloSSH/'</command><stdin>HelloWorld</stdin></ssh-request>

ThismessagecanbevalidatedwiththeusualCitrusmechanisminareceivetestaction.Ifyoudonotknowhowtodothis,pleasereadoneofthesectionsaboutXMLmessagevalidationinthisreferenceguidefirst.NowafterhavingreceivedthisrequestmessagetherespectiveSSHresponseshouldbeprovidedasappropriateanswer.ThisisdonewithamessagesendingactiononareplyhandlerasitisknownfromsynchronoushttpmessagecommunicationinCitrusforinstance.TheSSHXMLrepresentationofaresponsemessagelookslikethis:

<ssh-responsexmlns="http://www.citrusframework.org/schema/ssh/message"><stdout>HelloSSHWorld</stdout><stderr></stderr><exit>0</exit></ssh-response>

BesidessimulatingafullfeaturedSSHserver,CitrusalsoprovidesSSHclientfunctionality.Thisclientusesthesamerequestmessagepattern,whichistranslatedintoarealSSHcalltoanSSHserver.TheSSHresponsereceivedisalsotranslatedintoaXMLmessageasshownabovesowecanvalidateitwithknownvalidationmechanismsinCitrus.

CitrusReferenceGuide

377Ssh

Page 378: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SimilartotheotherCitrusmodules(http,soap),aCitrusSSHserverandclientisconfiguredinCitrusSpringapplicationcontext.ThereisadedicatedsshnamespaceavailableforallsshCitruscomponents.Thenamespacedeclarationgoesintothecontexttop-levelelementasusual:

<beans[...]xmlns:citrus-ssh="http://www.citrusframework.org/schema/ssh/config"[...]xsi:schemaLocation="[...]http://www.citrusframework.org/schema/ssh/confighttp://www.citrusframework.org/schema/ssh/config/citrus-ssh-config.xsd[...]">[...]</beans>

Both,SSHserverandclientalongwiththeirconfigurationoptionsaredescribedinthefollowingtwosections.

SSHClient

ACitrusSSHclientisusefulfortestingagainstarealSSHserver.SoCitrusisabletoinvokeSSHcommandsontheexternalserverandvalidatetheSSHresponseaccordingly.ThetestcasedoesnotdealwiththepureSSHprotocolwithinthiscommunication.TheCitrusSSHclientcomponentexpectsacustomizedXMLrepresentationandautomaticallytranslatestheserequestmessagesintoarealSSHcalltoaspecifichost.OncethesynchronousSSHresponsewasreceivedtheresultgetstranslatedbacktotheXMLresponsemessagerepresentation.OnthistranslatedresponsewecaneasilyapplythevalidationstepsbytheusualCitrusmeans.

TheSSHclientcomponentsreceiveitsconfigurationintheSpringapplicationcontextasusual.WecanusethespecialSSHmodulenamespaceforeasyconfiguration:

<citrus-ssh:clientid="sshClient"port="9072"user="roland"private-key-path="classpath:com/consol/citrus/ssh/test_user.priv"strict-host-checking="false"host="localhost"/>

TheSSHclientreceivesseveralattributes,theseare:

CitrusReferenceGuide

378Ssh

Page 379: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

id:Ididentifyingthebeanandusedasreferencefromwithtestdescriptions.(e.g.id="sshClient")host:HosttoconnecttoforsendinganSSHExecrequest.Defaultis'localhost'(e.g.host="localhost")portPorttouse.Defaultis2222(e.g.port="9072")private-key-path:Pathtoaprivatekey,whichcanbeeitheraplainfilepathoranclassresourceifprefixedwith'classpath'(e.g.private-key-path="classpath:test_user.priv")private-key-password:Optionalpasswordfortheprivatekey(e.g.password="s!cr!t")user:UserusedforconnectingtotheSSHserver(e.g.user="roland")password:Passwordusedforpasswordbasedauthentication.Mightbecombinedwith"private-key-path"inwhichcasebothauthenticationmechanismaretried(e.g.password="ps!st)strict-host-checking:Whetherthehostkeyshouldbeverifiedbylookingitupina'known_hosts'file.Defaultisfalse(e.g.strict-host-checking="true")known-hosts-path:Pathtoaknownhostsfile.Ifprefixedwith'classpath:'thisfileislookedupasaresourceintheclasspath(e.g.known-hosts-path="/etc/ssh/known_hosts")command-timeout:TimeoutinmillisecondsforhowlongtowaitfortheSSHcommandtocomplete.Defaultis5minutes(e.g.command-timeout="300000")connection-timeout:Timeoutinmillisecondsforhowlongtoforaconnectiuontoconnect.Defaultis1minute(e.g.connection-timeout="60000")actor:Actorusedforswitchinggroupsofactions(e.g.actor="ssh-mock")

OncedefinesasclientcomponentintheSpringapplicationcontexttestcasescanreferencetheclientineverysendtestaction.

<sendendpoint="sshClient"><message><payload><ssh-requestxmlns="http://www.citrusframework.org/schema/ssh/message"><command>shutdown</command><stdin>input</stdin></ssh-request></payload></message></send>

<receiveendpoint="sshClient"><message><payload>

CitrusReferenceGuide

379Ssh

Page 380: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<ssh-responsexmlns="http://www.citrusframework.org/schema/ssh/message"><stdout>HelloCitrus</stdout><stderr/><exit>0</exit></ssh-response></payload></message></receive>

Asyoucanseeweuseusualsendandreceivetestactions.TheXMLSSHrepresentationhelpsustospecifytherequestandresponsedataforvalidation.ThiswayyoucancallSSHcommandsagainstanexternalSSHserverandvalidatetheresponsedata.

SSHServer

NowthatwehaveusedCitrusontheclientsidewecanalsouseCitrusSSHservermoduleinordertoprovideafullstackedSSHserverdaemon.WecanacceptSSHclientconnectionsandprovideproperresponsemessagesasananswer.

GiventheaboveSSHmodulenamespacedeclaration,addinganewSSHserverisquitesimple:

<citrus-ssh:serverid="sshServer"allowed-key-path="classpath:com/consol/citrus/ssh/test_user_pub.pem"user="roland"port="9072"auto-start="true"endpoint-adapter="sshEndpointAdapter"/>

endpoint-adapteristhehandlerwhichreceivestheSSHrequestasmessages(intherequestformatdescribedabove).Endpointadapterimplementationsarefullydescribedinhttp-serverAlladaptersdescribedtherearesupportedinSSHservermodule,too.

The<citrus-ssh:server>supportsthefollowingattributes:

SSHServerAttributes:

id:NameoftheSSHserverwhichidentifiesituniquewithintheCitrusSpringcontext(e.g.id="sshServer")host-key-path:PathtoPEMencodedkeypair(publicandprivatekey)whichisusedashostkey.Bydefault,astandard,pre-generate,fixedkeypairisused.Thepathcanbespecifiedeitherasanfilepath,or,ifprefixedwithclasspath:islooked

CitrusReferenceGuide

380Ssh

Page 381: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

upfromwithintheclasspath.Thepaththeisrelativefromtothetop-levelpackage,sonoleadingslashshouldbeused(e.g.hist-key-path="/etc/citrus_ssh_server.pem)user:Userwhichisallowedtoconnect(e.g.user="roland")allowed-key-path:PathtoaSSHpublickeystoredinPEMformat.Thesearethekeys,whichareallowedtoconnecttotheSSHserverwhenpublickeyauthenticationisused.Itsevesthesamepurposeasauthorized_keysforstandardSSHinstallations.Thepathcanbespecifiedeitherasanfilepath,or,ifprefixedwithclasspath:islookedupfromwithintheclasspath.Thepaththeisrelativefromtothetop-levelpackage,sonoleadingslashshouldbeused(e.g.allowed-key-path="classpath:test_user_pub.pem)password:Passwordwhichshouldbeusedwhenpasswordauthenticationisused.Bothpublickeyauthenticationandpasswordbasedauthenticationcanbeusedtogetherinwhichcasebothmethodsaretriedinturn(e.g.password="s!cr!t")host:Hostaddress(e.g.localhost)port:Portonwhichtolisten.TheSSHserverwillbindonlocalhosttothisport(e.g.port="9072")auto-start:WhethertostartthisSSHserverautomatically.Defaultistrue.Ifsettofalse,atestactionisresponsibleforstarting/stoppingtheserver(e.g.auto-start="true")endpoint-adapter:BeanreferencetoaendpointadapterwhichprocessestheincomingSSHrequest.Themessageformatfortherequestandresponsearedescribedabove(e.g.endpoint-adapter="sshEndpointAdapter")

OncetheSSHservercomponentisaddedtotheSpringapplicationcontextwithaproperendpointadapterliketheMessageChannelforwardingadapterwecanreceiveincomingrequestsinatestcaseandprovidearesponemessagefortheclient.

<receiveendpoint="sshServer"><message><payload><ssh-requestxmlns="http://www.citrusframework.org/schema/ssh/message"><command>shutdown</command><stdin>input</stdin></ssh-request></payload></message></receive>

<sendendpoint="sshServer"><message><payload><ssh-responsexmlns="http://www.citrusframework.org/schema/ssh/message"><stdout>HelloCitrus</stdout>

CitrusReferenceGuide

381Ssh

Page 382: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<exit>0</exit></ssh-response></payload></message></send>

CitrusReferenceGuide

382Ssh

Page 383: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

RMIsupportRMIstandsforRemoteMethodInvocationandisastandardwayofcallingJavamethodinterfaceswherecallerandcallee(clientandserver)arenotlocatedwithinthesameJVM.Sotheobjectpassedtothemethodasargumentaswellasthemethodreturnvaluearetransmittedoverthewire.

AsaclientCitrusisabletoconnecttosomeRMIregistrythatexposessomeremoteinterfaces.AsaserverCitrusimplementssuchaRMIregistryandhandlesincomingmethodcallswithprovidingtherespectivereturnvalue.

NoteTheRMIcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldcheckthatthemoduleisavailableasMavendependencyinyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-rmi</artifactId><version>2.7</version></dependency>

AsusualCitrusprovidesacustomizedrmiconfigurationschemathatisusedinSpringconfigurationfiles.Simplyincludethecitrus-rminamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-rmi="http://www.citrusframework.org/schema/rmi/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/rmi/confighttp://www.citrusframework.org/schema/rmi/config/citrus-rmi-config.xsd">

[...]

</beans>

NowyouarereadytousethecustomizedHttpconfigurationelementswiththecitrus-rminamespaceprefix.

CitrusReferenceGuide

383Rmi

Page 384: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ReadthenextsectioninordertofindoutmoreabouttheRMImessagesupportinCitrus.

RMIclient

Ontheclientsidewewanttocalleremoteinterface.Weneedtospecifythemethodtocallaswellasallmethodarguments.Therespectivemethodreturnvalueisreceivablewithinthetestcaseforvalidation.CitrusprovidesaclientcomponentforRMIthatsendsoutserviceinvocationcalls.

<citrus-rmi:clientid="rmiClient1"host="localhost"port="1099"binding="newsService"/>

<citrus-rmi:clientid="rmiClient2"server-url="rmi://localhost:1099/newsService"/>

TheclientcomponentintheSpringapplicationcontextreceiveshostandportconfigurationofavalidRMIserviceregistry.Eitherbyspecifyingaproperserverurlorbygivinghost,portandbindingproperties.Theservicebindingisthenameoftheservicethatwewouldliketoaddressintheregistry.Nowwearereadytousethisclientreferencedbyitsidornameinatestcaseforamessagesendingaction.

XMLDSL

<sendendpoint="rmiClient"><message><payload><service-invocationxmlns="http://www.citrusframework.org/schema/rmi/message"><remote>com.consol.citrus.rmi.remote.NewsService</remote><method>getNews</method></service-invocation></payload></message></send>

JavaDSL

@CitrusTestpublicvoidrmiClientTest()send(rmiClient).message(RmiMessage.invocation(NewsService.class,"getNews"));

CitrusReferenceGuide

384Rmi

Page 385: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WeareusingtheusualCitrussendmessageactionreferencingthermiClientasendpoint.ThemessagepayloadisaspecialCitrusmessagethatdefinestheserviceinvocation.Wedefinetheremoteinterfaceaswellasthemethodtocall.CitrusRMIclientcomponentwillbeabletointerpretthismessagecontentandcalltheservicemethod.

Themethodreturnvalueisreceivableforvalidationusingtheverysameclientendpoint.

XMLDSL

<receiveendpoint="rmiClient"><message><payload><service-resultxmlns="http://www.citrusframework.org/schema/rmi/message"><objecttype="java.lang.String"value="ThisisnewsfromRMI!"/></service-result></payload></message></receive>

JavaDSL

@CitrusTestpublicvoidrmiClientTest()receive(rmiClient).message(RmiMessage.result("ThisisnewsfromRMI!"));

Inthesampleabovewereceivetheserviceresultandexpectajava.lang.Stringobjectreturnvalue.Thereturnvaluecontentisalsovalidatedwithintheserviceresultpayload.

Ofcoursewecanalsodealwithmethodarguments.

XMLDSL

<sendendpoint="rmiClient"><message><payload><service-invocationxmlns="http://www.citrusframework.org/schema/rmi/message"><remote>com.consol.citrus.rmi.remote.NewsService</remote><method>setNews</method><args><argvalue="Thisisbreakingnews!"/></args>

CitrusReferenceGuide

385Rmi

Page 386: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</service-invocation></payload></message></send>

@CitrusTestpublicvoidrmiServerTest()send(rmiClient).message(RmiMessage.invocation(NewsService.class,"setNews").argument("Thisisbreakingnews!"));

Thiscompletesthebasicremoteservicecall.Citrusinvokestheremoteinterfacemethodandvalidatesthemethodreturnvalue.Asatesteryoumightalsofaceerrorsandexceptionswhencallingtheremoteinterfacemethod.Youcancatchandasserttheseremoteexceptionsverifyingyourerrorscenario.

XMLDSL

<assertexception="java.rmi.RemoteException"><when><sendendpoint="rmiClient"><message><payload><service-invocationxmlns="http://www.citrusframework.org/schema/rmi/message"[...]</service-invocation></payload></message></send></when><assert/>

WeasserttheRemoteExceptiontobethrownwhilecallingtheremoteservicemethod.Thisishowyoucanhandlesomesortoferrorsituationwhilecallingremoteservices.InthenextsectionwewillhandleRMIcommunicationwhereCitrusprovidestheremoteinterfaces.

RMIserver

CitrusReferenceGuide

386Rmi

Page 387: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

OntheserversideCitrusneedstoprovideremoteinterfaceswithmethodscallableforclients.ThismeansthatCitrusneedstosupportallyourremoteinterfaceswithmethodargumentsandreturnvalues.TheCitrusRMIserverisabletobindyourremoteinterfacestoaserviceregistry.AllincomingRMIclientmethodcallsareautomaticallyacceptedandthemethodargumentsareconvertedintoaCitrusXMLserviceinvocationrepresentation.TheRMImethodcallisthenpassedtotherunningtestforvalidation.

LetushavealookattheCitrusRMIservercomponentandhowyoucanaddittotheSpringapplicationcontext.

<citrus-rmi:serverid="rmiServer"host="localhost"port="1099"interface="com.consol.citrus.rmi.remote.NewsService"binding="newService"create-registry="true"auto-start="true"/>

TheRMIservercomponentusespropertiessuchashostandporttodefinetheserviceregistry.BydefaultCitruswillconnecttothisserviceregistryandbinditsremoteinterfacestoit.Withtheattributecreate-registryCitruscanalsocreatetheregistryforyou.

YouhavetogiveCitrusthefullyqualifiedremoteinterfacenamesoCitruscanbindittotheserviceregistryandhandleincomingmethodcallsproperly.Inyourtestcaseyoucanthenreceivetheincomingmethodcallsontheserverinordertoperformvalidationsteps.

XMLDSL

<receiveendpoint="rmiServer"><message><payload><service-invocationxmlns="http://www.citrusframework.org/schema/rmi/message"><remote>com.consol.citrus.rmi.remote.NewsService</remote><method>getNews</method></service-invocation></payload><header><elementname="citrus_rmi_interface"value="com.consol.citrus.rmi.remote.NewsService"<elementname="citrus_rmi_method"value="getNews"/></header></message></receive>

CitrusReferenceGuide

387Rmi

Page 388: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSL

@CitrusTestpublicvoidrmiServerTest()receive(rmiServer).message(RmiMessage.invocation(NewsService.class,"getNews"));

AsyoucanseeCitrusconvertstheincomingserviceinvocationtoaspecialXMLrepresentationwhichispassedasmessagepayloadtothetest.AsthisisplainXMLyoucanverifytheRMImessagecontentasusualusingCitrusvariables,functionsandvalidationmatchers.

Sincewehavereceivedthemethodcallweneedtoprovidesomereturnvaluefortheclient.AsusualwecanspecifythemethodreturnvaluewithsomeXMLrepresentation.

XMLDSL

<sendendpoint="rmiServer"><message><payload><service-resultxmlns="http://www.citrusframework.org/schema/rmi/message"><objecttype="java.lang.String"value="ThisisnewsfromRMI!"/></service-result></payload></message></send>

JavaDSL

@CitrusTestpublicvoidrmiServerTest()send(rmiServer).message(RmiMessage.result("ThisisnewsfromRMI!"));

Theserviceresultisdefinedasobjectwithatypeandvalue.TheCitrusRMIremoteinterfacemethodwillreturnthisvaluetothecallingclient.Thiswouldcompletethesuccessfulremoteserviceinvocation.Atthispointwealsohavetothinkofchoosingtoraisesomeremoteexceptionasserviceoutcome.

XMLDSL

CitrusReferenceGuide

388Rmi

Page 389: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<sendendpoint="rmiServer"><message><payload><service-resultxmlns="http://www.citrusframework.org/schema/rmi/message"><exception>Somethingwentwrong<exception/></service-result></payload></message></send>

JavaDSL

@CitrusTestpublicvoidrmiServerTest()send(rmiServer).message(RmiMessage.exception("Somethingwentwrong"));

IntheexampleaboveCitruswillnotreturnsomeobjectasserviceresultbutraiseajava.rmi.RemoteExceptionwithrespectiveerrormessageasspecifiedinthetestcase.Thecallingclientwillreceivetheexceptionaccordingly.

CitrusReferenceGuide

389Rmi

Page 390: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JMXsupportJMXisastandardJavaAPIformakingbeansaccessibletoothersintermsofmanagementandremoteconfiguration.JMXistheshorttermforJavaManagementExtensionsandisoftenusedinJEEapplicationserverstomanagebeanattributesandoperationsfromoutside(e.g.anotherJVM).AmanagedbeanserverhostsmultiplemanagedbeansforJMXaccess.RemoteconnectionstoJMXcanberealizedwithRMI(Remotemethodinvocation)capabilities.

CitrusisabletoconnecttoJMXmanagedbeansasclientandserver.AsaclientCitruscaninvokemanagedbeanoperationsandreadwritemanagedbeanattributes.AsaserverCitrusisabletoexposemanagedbeansasmbeanserver.ClientscanaccessthoseCitrusmanagedbeansandgetproperresponseobjectsasresult.DoingsoyoucanusetheJVMplatformmanagedbeanserverorsomeRMIregistryforprovidingremoteaccess.

NoteTheJMXcomponentsinCitrusarekeptinaseparateMavenmodule.SoyoushouldcheckthatthemoduleisavailableasMavendependencyinyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-jmx</artifactId><version>2.7</version></dependency>

AsusualCitrusprovidesacustomizedjmxconfigurationschemathatisusedinSpringconfigurationfiles.Simplyincludethecitrus-jmxnamespaceintheconfigurationXMLfilesasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus="http://www.citrusframework.org/schema/config"xmlns:citrus-jmx="http://www.citrusframework.org/schema/jmx/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/confighttp://www.citrusframework.org/schema/config/citrus-config.xsdhttp://www.citrusframework.org/schema/jmx/confighttp://www.citrusframework.org/schema/jmx/config/citrus-jmx-config.xsd">

[...]

CitrusReferenceGuide

390Jmx

Page 391: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</beans>

NowyouarereadytousethecustomizedHttpconfigurationelementswiththecitrus-jmxnamespaceprefix.

NextsectionsdescribetheJMXmessagesupportinCitrusinmoredetail.

JMXclient

Ontheclientsidewewanttocallsomemanagedbeanbyeitheraccessingmanagedattributeswithread/writeorbyinvokingamanagedbeanoperation.ForpropermbeanserverconnectivityweshouldspecifyaclientcomponentforJMXthatsendsoutmbeaninvocationcalls.

<citrus-jmx:clientid="jmxClient"server-url="platform"/>

Theclientcomponentspecifiesthetargetmanagedbeanserverthatwewanttoconnectto.InthisexampleweareusingtheJVMplatformmbeanserver.ThismeansweareabletoaccessallJVMmanagedbeanssuchasMemory,ThreadingandLogging.Inadditiontothatwecanaccessallcustommanagedbeansthatwereexposedtotheplatformmbeanserver.

InmostcasesyoumaywanttoaccessmanagedbeansonadifferentJVMorapplicationserver.Soweneedsomeremoteconnectiontotheforeignmbeanserver.

<citrus-jmx:clientid="jmxClient"server-url="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"username="user"password="s!cr!t"auto-reconnect="true"delay-on-reconnect="5000"/>

InthisexampleaboveweconnecttoaremotembeanserverviaRMIusingthedefaultRMIregistrylocalhost:1099andtheservicenamejmxrmi.Citrusisabletohandledifferentremotetransportprotocols.Justdefinethoseintheserver-url.

Nowthatwehavesetuptheclientcomponentwecanuseitinatestcasetoaccessamanagedbean.

XMLDSL

CitrusReferenceGuide

391Jmx

Page 392: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<sendendpoint="jmxClient"><message><payload><mbean-invocationxmlns="http://www.citrusframework.org/schema/jmx/message"><mbean>java.lang:type=Memory</mbean><attributename="Verbose"/></mbean-invocation></payload></message></send>

JavaDSL

@CitrusTestpublicvoidjmxClientTest()send(jmxClient).message(JmxMessage.invocation("java.lang:type=Memory").attribute("Verbose"));

Asyoucanseewejustusedanormalsendactionreferencingthejmxclientcomponentthatwehavejustadded.ThemessagepayloadisaXMLrepresentationofthemanagedbeanaccess.ThisisaspecialCitrusXMLrepresentation.CitruswillconvertthisXMLpayloadtotheactuelmanagedbeanaccess.Intheexampleabovewetrytoaccessamanagedbeanwithobjectnamejava.lang:type=Memory.TheobjectnameisdefinedinJMXspecificationandconsistsofakeyjava.lang:typeandavalueMemory.Soweidentifythemanagedbeanontheserverbyitstype.

NowthatwehaveaccesstothemanagedbeanwecanreaditsmanagedattributessuchasVerbose.ThisisabooleantypeattributesothembeaninvocationresultwillbearespectiveBooleanobject.Wecanvalidatethemanagedbeanattributeaccessinareceiveaction.

XMLDSL

<receiveendpoint="jmxClient"><message><payload><mbean-resultxmlns="http://www.citrusframework.org/schema/jmx/message"><objecttype="java.lang.Boolean"value="false"/></mbean-result></payload></message></receive>

CitrusReferenceGuide

392Jmx

Page 393: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSL

@CitrusTestpublicvoidjmxClientTest()receive(jmxClient).message(JmxMessage.result(false));

Inthesampleabovewereceivethembeanresultandexpectajava.lang.Booleanobjectreturnvalue.Thereturnvaluecontentisalsovalidatedwithinthembeanresultpayload.

Somemanagedbeanattributesmightalsobesettableforus.Sowencandefinetheattributeaccessaswriteoperationbyspecifyingavalueinthesendactionpayload.

XMLDSL

<sendendpoint="jmxClient"><message><payload><mbean-invocationxmlns="http://www.citrusframework.org/schema/jmx/message"><mbean>java.lang:type=Memory</mbean><attributename="Verbose"value="true"type="java.lang.Boolean"/></mbean-invocation></payload></message></send>

JavaDSL

@CitrusTestpublicvoidjmxClientTest()send(jmxClient).message(JmxMessage.invocation("java.lang:type=Memory").attribute("Verbose",true));

NowwehavewriteaccesstothemanagedattributeVerbose.Wedospecifythevalueanditstypejava.lang.Boolean.Thisishowwecansetattributevaluesonmanagedbeans.

Lastnotleastweareabletoaccessmanagedbeanoperations.

XMLDSL

CitrusReferenceGuide

393Jmx

Page 394: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<sendendpoint="jmxClient"><message><payload><mbean-invocationxmlns="http://www.citrusframework.org/schema/jmx/message"><mbean>com.consol.citrus.jmx.mbean:type=HelloBean</mbean><operationname="sayHello">>parameter>>paramtype="java.lang.String"value="HelloJMX!"/>>/parameter>>/operation></mbean-invocation></payload></message></send>

JavaDSL

@CitrusTestpublicvoidjmxClientTest()send(jmxClient).message(JmxMessage.invocation("com.consol.citrus.jmx.mbean:type=HelloBean").operation("sayHello").parameter("HelloJMX!"));

IntheexampleaboveweaccessacustommanagedbeanandinvokeitsoperationsayHello.Wearealsousingoperationparametersfortheinvocation.Thisshouldcallthemanagedbeanoperationandreturnitsresultifanyasusual.

ThiscompletesthebasicJMXmanagedbeanaccessasclient.NowwealsowanttodiscusstheserversidewereCitrusisabletoprovidemanagedbeansforothers

JMXserver

Theserversideisalwaysalittlebitmoretrickybecauseweneedtosimulatecustommanagedbeanaccessasaserver.FirstofallCitrusprovidesaservercomponentthatspecifiestheconnectionpropertiesforclientssuchastransportprotocols,portsandmbeanobjectnames.LetscreateanewserverthatacceptsincomingrequestsviaRMIonaremoteregistrylocalhost:1099.

<citrus-jmx:serverid="jmxServer"server-url="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"<citrus-jmx:mbeans><citrus-jmx:mbeantype="com.consol.citrus.jmx.mbean.HelloBean"/><citrus-jmx:mbeantype="com.consol.citrus.jmx.mbean.NewsBean"objectDomain="com.consol.citrus.news"objectName="name=News"/>

CitrusReferenceGuide

394Jmx

Page 395: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</citrus-jmx:mbeans></citrus-jmx:server>

Asusualwedefineaserver-urlthatcontrolstheJMXconnectoraccesstothembeanserver.InthisexampleaboveweopenaJMXRMIconnectorforclientsusingtheregistrylocalhost:1099andtheservicenamejmxrmiBydefaultCitruswillnotattempttocreatethisregistryautomaticallysotheregistryhastobepresentbeforetheserverstartup.Withtheoptionalserverpropertycreate-registrysettotrueyoucanautocreatetheregistrywhentheserverstartsup.ThesepropertiesdoonlyapplywhenusingaremoteJMXconnectorserver.

Besidesusingthewholeserver-urlaspropertywecanalsoconstructtheconnectionbyhost,port,protocolandbindingproperties.

<citrus-jmx:serverid="jmxServer"host="localhost"port="1099"protocol="rmi"binding="jmxrmi"<citrus-jmx:mbeans><citrus-jmx:mbeantype="com.consol.citrus.jmx.mbean.HelloBean"/><citrus-jmx:mbeantype="com.consol.citrus.jmx.mbean.NewsBean"objectDomain="com.consol.citrus.news"objectName="name=News"/></citrus-jmx:mbeans></citrus-jmx:server>

Onlastthingtomentionisthatwecouldhavealsousedplatformasserver-urlinordertousetheJVMplatformmbeanserverinstead.

NowthatweclarifiedtheconnectivityweneedtotalkabouthowtodefinethemanagedbeansthatareavailableonourJMXmbeanserver.Thisisdoneasnestedmbeanconfigurationelements.HerethemanagedbeandefinitionsdescribethemanagedbeanwithitsobjectDomain,objectName,operationsandattributes.Themostconvenientwayofdefiningsuchmanagedbeandefinitionsistogiveabeantypewhichisthefullyqualifiedclassnameofthemanagedbean.CitruswillusethepackagenameandclassnameforproperobjectDomainandobjectNameconstruction.

Letshaveacloserlookattheirstmbeandefinitionintheexampleabove.Sothefirstmanagedbeanisdefinedbyitsclassnamecom.consol.citrus.jmx.mbean.HelloBeanandthereforeisaccessibleusingtheobjectNamecom.consol.citrus.jmx.mbean:type=HelloBean.InadditiontothatCitruswillreadthe

CitrusReferenceGuide

395Jmx

Page 396: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

classinformationsuchasavailablemethods,gettersandsettersforconstructingaproperMBeanInfo.InthesecondmanagedbeandefinitioninourexamplewehaveusedadditionalcustomobjectDomainandobjectNamevalues.SotheNewsBeanwillbeaccessiblewithcom.consol.citrus.news:name=Newsonthemanagedbeanserver.

Thisishowwecandefinethebindingsofmanagedbeansandwhatclientsneedtosearchforwhenfindingandaccessingthemanagedbeansontheserver.WhenclientstrytofindthemanagedbeanstheyhavetouseproperobjectNamesaccordingly.ObjectNamesthatarenotdefinedontheserverwillberejectedwithmanagedbeannotfounderror.

Rightnowwehavetousethequalifiedclassnameofthemanagedbeaninthedefinition.Whathappensifwedonothaveaccesstothatmbeanclassorifthereisnotmanagedbeaninterfaceavailableatall?Citrusprovidesagenericmanagedbeanthatisabletohandleanymanagedbeaninteraction.Thegenericbeanimplementationneedstoknowthemanagedoperationsandattributesthough.Soletsdefineanewgenericmanagedbeanonourserver:

<citrus-jmx:serverid="jmxServer"server-url="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"<citrus-jmx:mbeans><citrus-jmx:mbeanname="fooBean"objectDomain="foo.object.domain"objectName="type=FooBean"><citrus-jmx:operations><citrus-jmx:operationname="fooOperation"><citrus-jmx:parameter><citrus-jmx:paramtype="java.lang.String"/><citrus-jmx:paramtype="java.lang.Integer"/></citrus-jmx:parameter></citrus-jmx:operation><citrus-jmx:operationname="barOperation"/></citrus-jmx:operations><citrus-jmx:attributes><citrus-jmx:attributename="fooAttribute"type="java.lang.String"/><citrus-jmx:attributename="barAttribute"type="java.lang.Boolean"/></citrus-jmx:attributes></citrus-jmx:mbean></citrus-jmx:mbeans></citrus-jmx:server>

Thegenericbeandefinitionneedstodefinealloperationsandattributesthatareavailableforaccess.UptonowwearerestrictedtousingJavabasetypeswhendefiningoperationparameterandattributereturntypes.Thereisactuallynowaytodefinemore

CitrusReferenceGuide

396Jmx

Page 397: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

complexreturntypes.NeverthelessCitrusisnowabletoexposethemanagedbeanforclientaccesswithouthavingtoknowtheactualmanagedbeanimplementation.

Nowwecanusetheservercomponentinatestcasetoreceivesomeincomingmanagedbeanaccess.

XMLDSL

<receiveendpoint="jmxServer"><message><payload><mbean-invocationxmlns="http://www.citrusframework.org/schema/jmx/message"><mbean>com.consol.citrus.jmx.mbean:type=HelloBean</mbean><operationname="sayHello">>parameter>>paramtype="java.lang.String"value="HelloJMX!"/>>/parameter></operation></mbean-invocation></payload></message></receive>

JavaDSL

@CitrusTestpublicvoidjmxServerTest()receive(jmxServer).message(JmxMessage.invocation("com.consol.citrus.jmx.mbean:type=HelloBean").operation("sayHello").parameter("HelloJMX!"));

Inthisveryfirstexampleweexpectamanagedbeanaccesstothebeancom.consol.citrus.jmx.mbean:type=HelloBean.WefurtherexpecttheoperationsayHellotobecalledwithrespectiveparametervalues.Nowwehavetodefinetheoperationresultthatwillbereturnedtothecallingclientasoperationresult.

XMLDSL

<sendendpoint="jmxServer"><message><payload><mbean-resultxmlns="http://www.citrusframework.org/schema/jmx/message"><objecttype="java.lang.String"value="HellofromJMX!"/></mbean-result>

CitrusReferenceGuide

397Jmx

Page 398: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</payload></message></send>

JavaDSL

@CitrusTestpublicvoidjmxServerTest()send(jmxServer).message(JmxMessage.result("HellofromJMX!"));

TheoperationreturnsaStringHellofromJMX!.Thisishowwecanexpectoperationcallsonmanagedbeans.Nowwealreadyhaveseenthatmanagedbeansalsoexposeattributes.Thenextexampleishandlingincomingattributereadaccess.

XMLDSL

<receiveendpoint="jmxServer"><message><payload><mbean-invocationxmlns="http://www.citrusframework.org/schema/jmx/message"><mbean>com.consol.citrus.news:name=News</mbean>>attributename="newsCount"/></mbean-invocation></payload></message></receive>

<sendendpoint="jmxServer"><message><payload><mbean-resultxmlns="http://www.citrusframework.org/schema/jmx/message"><objecttype="java.lang.Integer"value="100"/></mbean-result></payload></message></send>

JavaDSL

@CitrusTestpublicvoidjmxServerTest()receive(jmxServer).message(JmxMessage.invocation("com.consol.citrus.news:name=News").attribute("newsCount");

CitrusReferenceGuide

398Jmx

Page 399: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

send(jmxServer).message(JmxMessage.result(100));

ThereceiveactionexpectsreadaccesstotheNewsBeanattributenewsCountandreturnsaresultobjectoftypejava.lang.Integer.Thiswaywecanexpectallattributeaccesstoourmanagedbeans.Writeoperationswillhaveaattributevaluespecified.

ThiscompletestheJMXservercapabilitieswithmanagedbeanaccessonoperationsandattributes.

CitrusReferenceGuide

399Jmx

Page 400: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CucumberBDDsupportBehaviordrivendevelopment(BDD)isbecomingmoreandmorepopularthesedays.Theideaofdefininganddescribingthesoftwarebehaviorasbasisforalltestsinpriortotranslatingthosefeaturedescriptionsintoexecutabletestsisaveryinterestingapproachbecauseitincludesthetechnicalexpertsaswellasthedomainexperts.WithBDDthedomainexpertscaneasilyreadandverifythetestsandthetechnicalexpertsgetadetaileddescriptionofwhatshouldhappeninthetest.

ThetestscenariodescriptionsfollowtheGherkinsyntaxwitha"Given-When-Then"structuremostofthetime.TheGherkinlanguageisbusinessreadableandwellknowninBDD.

TherearelotsofframeworksintheJavacommunitythatsupportBDDconcepts.CitrushasdedicatedsupportfortheCucumberframeworkbecauseCucumberiswellsuitedforextensionsandplugins.SowiththeCitrusandCucumberintegrationyoucanwriteGherkinsyntaxscenarioandfeaturestoriesinordertoexecutetheCitrusintegrationtestcapabilities.Asusualwehavealookatafirstexample.FirstletsseetheCitruscucumberdependencyandXMLschemadefinitions.

NoteTheCucumbercomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-cucumber</artifactId><version>2.7</version></dependency>

CitrusprovidesaseparateconfigurationnamespaceandschemadefinitionforCucumberrelatedstepdefinitions.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusCucumberconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<spring:beansxmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.citrusframework.org/schema/cucumber/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/cucumber/testcase

CitrusReferenceGuide

400Cucumber

Page 401: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

http://www.citrusframework.org/schema/cucumber/testcase/citrus-cucumber-testcase.xsd">

[...]

</spring:beans>

CucumberworkswithbothJUnitandTestNGasunittestingframework.YoucanchoosewhichframeworktousewithCucumber.SofollowingfromthatweneedaMavendependencyfortheunittestingframeworksupport:

<dependency><groupId>info.cukes</groupId><artifactId>cucumber-junit</artifactId><version>$cucumber.version</version></dependency>

InordertoenableCitrusCucumbersupportweneedtospecifyaspecialobjectfactoryintheenvironment.Themostcomfortablewaytospecifyacustomobjectfactoryistoaddthispropertytothecucumber.propertiesinclasspath.

cucumber.api.java.ObjectFactory=cucumber.runtime.java.CitrusObjectFactory

Thisspecialobjectfactorytakescareoncreatingallstepdefinitioninstances.Theobjectfactoryisabletoinject@CitrusResourceannotatedfieldsinstepclasses.Wewillseethislateronintheexamples.TheusageofthisspecialobjectfactoryismandatoryinordertocombineCitrusandCucumbercapabilities.

TheCitrusObjectFactorywillautomaticallyinitializetheCitrusworldforus.Thisincludesthedefaultcitrus-context.xmlCitrusSpringconfigurationthatisautomaticallyloadedwithintheobjectfactory.SoyoucandefineanduseCitruscomponentsasusualwithinyourtest.

AfterthesepreparationstepsyouareabletocombineCitrusandCucumberinyourproject.

Cucumberintegration

CucumberisabletoruntestswithJUnit.ThebasictestcaseisanemptytestwhichusestherespectiveJUnitrunnerimplementationfromcucumber.

CitrusReferenceGuide

401Cucumber

Page 402: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@RunWith(Cucumber.class)@CucumberOptions(plugin="com.consol.citrus.cucumber.CitrusReporter")publicclassMyFeatureIT

ThetestcaseaboveusestheCucumberJUnittestrunner.InadditiontothatwegivesomeoptionstotheCucumberexecution.WedefineaspecialCitrusreporterimplementation.ThisclassisresponsibleforprintingtheCitrustestsummary.ThisreporterextendsthedefaultCucumberreporterimplementationsothedefaultCucumberreportsummariesarealsoprintedtotheconsole.

ThatcompletestheJUnitclassconfiguration.NowweareabletoaddfeaturestoriesandstepdefinitionstothepackageofourtestMyFeatureIT.CucumberandCitruswillautomaticallypickupstepdefinitionsandgluecodeinthattestpackage.Soletswriteafeaturestoryecho.featurerightnexttotheMyFeatureITtestclass.

Feature:Echoservice

Scenario:SayhelloGivenMynameisCitrusWhenIsayhellototheserviceThentheserviceshouldreturn:"Hello,mynameisCitrus!"

Scenario:SaygoodbyeGivenMynameisCitrusWhenIsaygoodbyetotheserviceThentheserviceshouldreturn:"GoodbyefromCitrus!"

AsyoucanseethisstorydefinestwoscenarioswiththeGherkinGiven-When-Thensyntax.NowweneedtoaddstepdefinitionsthatgluethestorydescriptiontoCitrustestactions.LetsdothisinanewclassEchoSteps.

publicclassEchoSteps

@CitrusResourceprotectedTestDesignerdesigner;

@Given("^Mynameis(.*)$")publicvoidmy_name_is(Stringname)designer.variable("username",name);

@When("^Isayhello.*$")

CitrusReferenceGuide

402Cucumber

Page 403: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

publicvoidsay_hello()designer.send("echoEndpoint").messageType(MessageType.PLAINTEXT).payload("Hello,mynameis$username!");

@When("^Isaygoodbye.*$")publicvoidsay_goodbye()designer.send("echoEndpoint").messageType(MessageType.PLAINTEXT).payload("Goodbyefrom$username!");

@Then("^theserviceshouldreturn:\"([^\"]*)\"$")publicvoidverify_return(finalStringbody)designer.receive("echoEndpoint").messageType(MessageType.PLAINTEXT).payload("Youjustsaid:"+body);

IfwehaveacloserlookatthestepdefinitionclassweseethatitisanormalPOJOthatusesa@CitrusResourceannotatedTestDesigner.ThetestdesignerisautomaticallyinjectedbyCitrusCucumberextension.Thisisdonebecausewehaveincludedthecitrus-cucumberdependencytoourprojectbefore.Nowwecanwrite@Given,@Whenor@Thenannotatedmethodsthatmatchthescenariodescriptionsinourstory.Cucumberwillautomaticallyfindmatchingmethodsandexecutethem.ThemethodsaddtestactionstothetestdesignerasweareusedtoitinnormalJavaDSLtests.Attheendthetestdesignerisautomaticallyexecutedwiththetestlogic.

ImportantOfcourseyoucandothedependencyinjectionwith@CitrusResourceannotationsonTestRunnerinstances,too.WhichvariationshouldsomeoneuseTestDesignerorTestRunner?Infactthereisasignificantdifferencewhenlookingatthetwoapproaches.ThedesignerwillusetheGherkinmethodstobuildthewholeCitrustestcasefirstbeforeanyactionisexecuted.TherunnerwillexecuteeachtestactionthathasbeenbuiltwithaGherkinstepimmediately.ThismeansthatadesignerapproachwillalwayscompleteallBDDstepdefinitionsbeforetakingaction.ThisdirectlyaffectstheCucumberstepreports.AllstepsareusuallymarkedassuccessfulwhenusingadesignerapproachastheCitrustestisexecutedaftertheCucumberstepshavebeenexecuted.Therunnerapproachincontrastwillfailthestepwhenthecorrespondingtestactionfails.TheCucumbertestreportswilldefinitelylookdifferent

CitrusReferenceGuide

403Cucumber

Page 404: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

dependingonwhatapproachyouarechoosing.Allotherfunctionsstaythesameinbothapproaches.Ifyouneedtolearnmoreaboutdesignerandrunnerapproachespleasesee

IfweruntheCucumbertesttheCitrustestcaseautomaticallyperformsitsactions.ThatisafirstcombinationofCitrusandCucumberBDD.Thestorydescriptionsaretranslatedtotestactionsandweareabletorunintegrationtestswithbehaviordrivendevelopment.Great!InanextstepwewilluseXMLstepdefinitionsratherthancodingthestepsinJavaDSL.

CucumberXMLsteps

SofarwehavewrittengluecodeinJavainordertotranslateGherkinsyntaxdescriptionstotestactions.NowwewanttodothesamewithjustXMLconfiguration.TheJUnitCucumberclassshouldnotchange.WestillusetheCucumberrunnerimplementationwithsomeoptionsspecifictoCitrus:

@RunWith(Cucumber.class)@CucumberOptions(plugin="com.consol.citrus.cucumber.CitrusReporter")publicclassMyFeatureIT

Thescenariodescriptionisalsonotchanged:

Feature:Echoservice

Scenario:SayhelloGivenMynameisCitrusWhenIsayhellototheserviceThentheserviceshouldreturn:"Hello,mynameisCitrus!"

Scenario:SaygoodbyeGivenMynameisCitrusWhenIsaygoodbyetotheserviceThentheserviceshouldreturn:"GoodbyefromCitrus!"

Inthefeaturepackagemy.company.featuresweaddanewXMLfileEchoSteps.xmlthatholdsthenewXMLstepdefinitions:

<?xmlversion="1.0"encoding="UTF-8"?><spring:beansxmlns:citrus="http://www.citrusframework.org/schema/testcase"

CitrusReferenceGuide

404Cucumber

Page 405: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

xmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.citrusframework.org/schema/cucumber/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/cucumber/testcasehttp://www.citrusframework.org/schema/cucumber/testcase/citrus-cucumber-testcase.xsd"

<stepgiven="^Mynameis(.*)$"parameter-names="username"><citrus:create-variables><citrus:variablename="username"value="$username"/></citrus:create-variables></step>

<stepwhen="^Isayhello.*$"><citrus:sendendpoint="echoEndpoint"><citrus:messagetype="plaintext"><citrus:data>Hello,mynameis$username!</citrus:data></citrus:message></citrus:send></step>

<stepwhen="^Isaygoodbye.*$"><citrus:sendendpoint="echoEndpoint"><citrus:messagetype="plaintext"><citrus:data>Goodbyefrom$username!</citrus:data></citrus:message></citrus:send></step>

<stepthen="^theserviceshouldreturn:&quot;([^&quot;]*)&quot;$"parameter-names="body"><citrus:receiveendpoint="echoEndpoint"><citrus:messagetype="plaintext"><citrus:data>Youjustsaid:$body</citrus:data></citrus:message></citrus:receive></step>

</spring:beans>

TheabovestepsdefinitioniswritteninpureXML.CitruswillautomaticallyreadthestepdefinitionandaddthosetotheCucumberruntime.Followingfromthatthestepdefinitionsareexecutedwhenmatchingtothefeaturestory.TheXMLstepfilesfollowanamingconvention.Citruswilllookforallfileslocatedinthefeaturepackagewithnamepattern**/.Steps.xml**andloadthosedefinitionswhenCucumberstartsup.

CitrusReferenceGuide

405Cucumber

Page 406: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheXMLstepsareabletoreceiveparametersfromtheGherkinregexpmatcher.Theparametersarepassedtothestepastestvariable.Theparameternamesgetdeclaredintheoptionalattributeparameter-names.Inthestepdefinitionactionsyoucanusetheparameternamesastestvariables.

NoteThetestvariablesarevisibleinallupcomingsteps,too.Thisisbecausethetestvariablesareglobalbydefault.Ifyouneedtosetlocalstateforastepdefinitionyoucanuseanotherattributeglobal-contextandsetittofalseinthestepdefinition.Thiswayalltestvariablesandparametersareonlyvisibleinthestepdefinition.Otherstepswillnotseethetestvariables.

NoteAnothernotablethingistheXMLescapingofreservedcharactersinthepatterndefinition.Youcanseethatinthelaststepwherethethenattributeisescapingquotationcharacters.

then="^theserviceshouldreturn:&quot;([^&quot;]*)&quot;$"

WehavetodothisbecauseotherwisethequotationcharacterswillinterferewiththeXMLsyntaxintheattribute.

ThiscompletesthedescriptionofhowtoaddXMLstepdefinitionstothecucumberBDDtests.Inanextsectionwewillusepredefinedstepsforsendingandreceivingmessages.

CucumberSpringsupport

CucumberprovidessupportforSpringdependencyinjectioninstepdefinitionclasses.TheCucumberSpringcapabilitiesareincludedinaseparatemodule.Sowefirstofallwehavetoaddthisdependencytoourproject:

<dependency><groupId>info.cukes</groupId><artifactId>cucumber-spring</artifactId><version>$cucumber.version</version></dependency>

TheCitrusCucumberextensionhastohandlethingsdifferentwhenCucumberSpringsupportisenabled.ThereforeweuseanotherobjectfactoryimplementationthatalsosupportCucumberSpringfeatures.Changetheobjectfactorypropertyincucumber.propertiestothefollowing:

CitrusReferenceGuide

406Cucumber

Page 407: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

cucumber.api.java.ObjectFactory=cucumber.runtime.java.spring.CitrusSpringObjectFactory

Nowwearereadytoadd@AutowiredSpringbeandependenyinjectiontostepdefinitionclasses:

@ContextConfiguration(classes=CitrusSpringConfig.class)publicclassEchoSteps@AutowiredprivateEndpointechoEndpoint;

@CitrusResourceprotectedTestDesignerdesigner;

@Given("^Mynameis(.*)$")publicvoidmy_name_is(Stringname)designer.variable("username",name);

@When("^Isayhello.*$")publicvoidsay_hello()designer.send(echoEndpoint).messageType(MessageType.PLAINTEXT).payload("Hello,mynameis$username!");

@When("^Isaygoodbye.*$")publicvoidsay_goodbye()designer.send(echoEndpoint).messageType(MessageType.PLAINTEXT).payload("Goodbyefrom$username!");

@Then("^theserviceshouldreturn:\"([^\"]*)\"$")publicvoidverify_return(finalStringbody)designer.receive(echoEndpoint).messageType(MessageType.PLAINTEXT).payload("Youjustsaid:"+body);

AsyoucanseeweusedSpringautowiringmechanismfortheechoEndpointfieldinthestepdefinition.Alsobesuretodefinethe@ContextConfigurationannotationonthestepdefinition.TheCucumberSpringsupportloadstheSpringapplicationcontextandtakescareondependencyinjection.WeusetheCitrusCitrusSpringConfigJavaconfigurationbecausethisisthemainentranceforCitrustestcases.Youcanadd

CitrusReferenceGuide

407Cucumber

Page 408: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

custombeansandfurtherSpringrelatedconfigurationtothisSpringapplicationcontext.IfyouwanttoaddmorebeansforautowiringdosointheCitrusSpringconfiguration.Usuallythisisthedefaultcitrus-context.xmlwhichisautomaticallyloaded.

OfcourseyoucanalsouseacustomJavaSpringconfigurationclasshere.ButbesuretoalwaysimporttheCitrusSpringJavaconfigurationclasses,too.OtherwiseyouwillnotbeabletoexecutetheCitrusintegrationtestcapabilities.

Asusualweareabletouse@CitrusResourceannotatedTestDesignerfieldsforbuildingtheCitrusintegrationtestlogic.WiththisextensionyoucanusethefullSpringtestingpowerinyourtestsinparticulardependencyinjectionandalsotransactionmanagementfordatapersistancetests.

Citrusstepdefinitions

Citrusprovidessomeoutoftheboxpredefinedstepsfortypicalintegrationtestscenarios.Thesestepsarereadytouseinscenarioorfeaturestories.Youcanbasicallydefinesendandreceiveoperations.AsthesestepsarepredefinedinCitrusyoujustneedtowritefeaturestories.Thestepdefinitionswithgluetotestactionsarehandledautomatically.

Ifyouwanttoenablepredefinedstepssupportinyourtestyouneedtoincludethegluecodepackageinyourtestclasslikethis:

@RunWith(Cucumber.class)@CucumberOptions(glue="com.consol.citrus.cucumber.step.designer"plugin="com.consol.citrus.cucumber.CitrusReporter")publicclassMyFeatureIT

Insteadofwritingthegluecodeonourowninstepdefinitionclassesweincludethegluepackagecom.consol.citrus.cucumber.step.designer.ThisautomaticallyloadsallCitrusgluestepdefinitionsinthispackage.OnceyouhavedonethisyoucanusepredefinedstepsthataddCitrustestlogicwithouthavingtowriteanygluecodeinJavastepdefinitions.

OfcourseyoucanalsochoosetoincludetheTestRunnerstepdefinitionsbychoosingthegluepackagecom.consol.citrus.cucumber.step.runner.

@RunWith(Cucumber.class)

CitrusReferenceGuide

408Cucumber

Page 409: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@CucumberOptions(glue="com.consol.citrus.cucumber.step.runner"plugin="com.consol.citrus.cucumber.CitrusReporter")publicclassMyFeatureIT

Followingbasicstepdefinitionsareincludedinthispackage:

Givenvariable[name]is"[value]"Givenvariables|[name1]|[value1]||[name2]|[value2]|

When<[endpoint-name]>sends"[message-payload]"Then<[endpoint-name]>shouldreceive(message-type)"[message-payload]"

When<[endpoint-name]>sends"""[message-payload]"""Then<[endpoint-name]>shouldreceive(message-type)"""[message-payload]"""

When<[endpoint-name]>receives(message-type)"[message-payload]"Then<[endpoint-name]>shouldsend"[message-payload]"

When<[endpoint-name]>receives(message-type)"""[message-payload]"""Then<[endpoint-name]>shouldsend"""[message-payload]"""

Onceagainitshouldbesaidthatthestepdefinitionsincludedinthispackageareloadedautomaticallyasgluecode.SoyoucanstarttowritefeaturestoriesinGherkinsyntaxthattriggerthepredefinedsteps.InthefollowingsectionswehaveacloserlookatallpredefinedCitrusstepsandhowtheywork.

Variablesteps

CitrusReferenceGuide

409Cucumber

Page 410: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AsyoualreadyknowCitrusisabletoworkwithtestvariablesthatholdimportantinformationduringatestsuchasidentifiersanddynamicvalues.ThepredefinedstepdefinitionsinCitrusareabletocreatenewtestvariables.

GivenvariablemessageTextis"Hello"

Thesyntaxofthispredefinedstepisprettyselfdescribing.Thestepinstructionfollowsthepattern:

Givenvariable[name]is"[value]"

Ifyoukeepthissyntaxinyourfeaturestorythepredefinedstepisactivatedforcreatinganewvariable.WealwaysusetheGivensteptocreatenewvariables.

Scenario:CreateVariablesGivenvariablemessageTextis"Hello"AndvariableoperationHeaderis"sayHello"

SowecanusetheAndkeywordtocreatemorethanonevariable.Evenmorecomfortableistheusageofdatatables:

Givenvariables|hello|Isayhello||goodbye|Isaygoodbye|

Thisdatatablewillcreatethetestvariableforeachrow.ThisishowyoucaneasilycreatenewvariablesinyourCitrustest.Asusualthevariablesarereferencedinmessagepayloadsandheadersasplaceholdersfordynamicallyaddingcontent.

AddingvariablesisusuallydonewithinaScenarioblockinyourfeaturestory.ThismeansthatthetestvariableisusedinthisscenariowhichisexactlyoneCitrustestcase.CucumberBDDalsodefinesaBackgroundblockattheverybeginningofyourFeature.Wecanalsoplacevariablesinhere.ThismeansthatCucumberwillexecutethesestepsforallupcomingscenarios.Thetestvariableissotospeakglobalforthisfeaturestory.

Feature:Variables

Background:GivenvariablemessageTextis"Hello"

CitrusReferenceGuide

410Cucumber

Page 411: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Scenario:DosomethingScenario:Dosomethingelse

ThatcompletesthevariablestepdefinitionsinCitrus.

Messagingsteps

IntheprevioussectionwehavelearnedhowtouseafirstpredefinedCitrusstep.NowwewanttocovermessagingstepsforsendingandreceivingmessagesinCitrus.Asusualwithpredefinedstepsyoudonotneedtowriteanygluecodeforthestepstotakeaction.ThestepsarealreadyincludedinCitrusjustusetheminyourfeaturestories.

Feature:Messagingfeatures

Background:GivenvariablemessageTextis"Hello"

Scenario:SendandreceiveplaintextWhen<echoEndpoint>sends"$messageText"Then<echoEndpoint>shouldreceiveplaintext"Youjustsaid:$messageText"

Ofcourseweneedtofollowthepredefinedsyntaxwhenwritingfeaturestoriesinordertotriggerapredefinedstep.Let'shaveacloserlookatthispredefinedsyntaxbyfurtherdescribingtheaboveexample.

FirstofallwedefineanewtestvariablewithGivenvariablemessageTextis"Hello".ThistellsCitrustocreateanewtestvariablenamedmessageTextwithrespectivevalue.Wecandothesameforsendingandreceivingmessageslikedoneinourtestscenario:

When<[endpoint-name]>sends"[message-payload]"

Thestepdefinitionrequirestheendpointcomponentnameandamessagepayload.ThepredefinedstepwillautomaticallyconfigureasendtestactionintheCitrustestasresult.

Then<[endpoint-name]>shouldreceive(message-type)"[message-payload]"

Thepredefinedreceivestepalsorequirestheendpoint-nameandmessage-payload.Asoptionalparameteryoucandefinethemessage-type.ThisisrequiredwhensendingmessagepayloadsotherthanXML.

CitrusReferenceGuide

411Cucumber

Page 412: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThiswayyoucanwriteCitrustestswithjustwritingfeaturestoriesinGherkinsyntax.Uptonowwehaveusedprettysimplemessagepayloadsinonsingleline.Ofcoursewecanalsousemultilinepayloadsinthestories:

Feature:Messagingfeatures

Background:GivenvariablemessageTextis"Hello"

Scenario:SendandreceiveWhen<echoEndpoint>sends"""<message><text>$messageText</text></message>"""Then<echoEndpoint>shouldreceive"""<message><text>$messageText</text></message>"""

AsyoucanseeweareabletousethesendandreceivestepswithmultilineXMLmessagepayloaddata.

Namedmessages

IntheprevioussectionwehavelearnedhowtouseCitruspredefinedstepdefinitionsforsendandreceiveoperations.Themessagepayloadhasbeenaddeddirectlytothestoriessofar.Butwhatiswithmessageheaderinformation?Wewanttospecifyacompletemessagewithpayloadandheader.Youcandothisbydefininganamedmessage.

Asusualwedemonstratethisinafirstexample:

Feature:Namedmessagefeature

Background:GivenmessageechoRequestAnd<echoRequest>payloadis"HimynameisCitrus!"And<echoRequest>headeroperationis"sayHello"

GivenmessageechoResponseAnd<echoResponse>payloadis"Hi,Citrushowareyoudoingtoday?"And<echoResponse>headeroperationis"sayHello"

CitrusReferenceGuide

412Cucumber

Page 413: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Scenario:SendandreceiveWhen<echoEndpoint>sendsmessage<echoRequest>Then<echoEndpoint>shouldreceivemessage<echoResponse>

IntheBackgroundsectionweintroducenamedmessagesechoRequestandechoResponse.Thismakesuseofthenewpredefinedstepforaddingnamedmessage:

Givenmessage[message-name]

Oncethemessageisintroducedwithitsnamewecanusethemessageinfurtherconfigurationsteps.Youcanaddpayloadinformationandyoucanaddmultipleheaderstothemessage.Thenamedmessagethenisreferencedinsendandreceivestepsasfollows:

When<[endpoint-name]>sendsmessage<[message-name]>Then<[endpoint-name]>shouldreceivemessage<[message-name]>

ThestepsreferenceamessagebyitsnameechoRequestandechoResponse.

Asyoucanseethenamedmessagesareusedtodefinecompletemessageswithpayloadandheaderinformation.Ofcoursethenamedmessagescanbereferencedinmanyscenariosandsteps.Alsowithusageoftestvariablesinpayloadandheaderyoucandynmaicallyadjustthosemessagesineachstep.

Messagecreatorsteps

Intheprevioussectionwehavelearnedhowtousenamedmessagesaspredefinedstep.Thenamedmessagehasbeendefineddirectlyinthestoriessofar.ThemessagecreatorconceptmovesthistasktosomeJavaPOJO.Thiswayyouareabletoconstructmorecomplicatedmessagesforreuseinseveralscenariosandfeaturestories.

Asusualwedemonstratethisinafirstexample:

Feature:Messagecreatorfeatures

Background:Givenmessagecreatorcom.consol.citrus.EchoMessageCreatorAndvariablemessageTextis"Hello"Andvariableoperationis"sayHello"

CitrusReferenceGuide

413Cucumber

Page 414: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Scenario:SendandreceiveWhen<echoEndpoint>sendsmessage<echoRequest>Then<echoEndpoint>shouldreceivemessage<echoResponse>

IntheBackgroundsectionweintroduceamessagecreatorEchoMessageCreatorinpackagecom.consol.citrus.Thismakesuseofthenewpredefinedstepforaddingmessagecreatorstothetest:

Givenmessagecreator[message-creator-name]

ThemessagecreatornamemustbethefullyqualifiedJavaclassnamewithpackageinformation.Oncethisisdonewecanusenamedmessagesinthesendandreceiveoperations:

When<[endpoint-name]>sendsmessage<[message-name]>Then<[endpoint-name]>shouldreceivemessage<[message-name]>

ThestepsreferenceamessagebyitsnameechoRequestandechoResponse.NowletshavealookatthemessagecreatorEchoMessageCreatorimplementationinordertoseehowthiscorrelatestoarealmessage.

publicclassEchoMessageCreator@MessageCreator("echoRequest")publicMessagecreateEchoRequest()returnnewDefaultMessage(""+"$messageText"+"").setHeader("operation","$operation");

@MessageCreator("echoResponse")publicMessagecreateEchoResponse()returnnewDefaultMessage(""+"$messageText"+"").setHeader("operation","$operation");

AsyoucanseethemessagecreatorisaPOJOJavaclassthatdefinesoneormoremethodsthatareannotatedwith@MessageCreatorannotation.Theannotationrequiresamessagename.ThisishowCitruswillcorrelatemessagenamesinfeaturestoriestomessagecreatormethods.Themessagereturnedistheusedforthesendand

CitrusReferenceGuide

414Cucumber

Page 415: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

receiveoperationsinthetest.Themessagecreatorisreusableaccrossmultiplefeaturestoriesandscenarios.Inadditiontothatthecreatorisabletoconstructmessagesinamorepowerfulway.Forinstancethemessagepayloadcouldbeloadedfromfilesystemresources.

Echosteps

AnotherpredefinedstepdefinitioninCitrusisusedtoaddaechotestaction.Youcanusethefollowingstepinyourfeaturescenarios:

Feature:Echofeatures

Scenario:EchomessagesGivenvariablefoois"bar"Thenecho"Variablefoo=$foo"Thenecho"Todayiscitrus:currentDate()"

Thestepdefinitionrequiresfollowingpattern:

Thenecho"[message]"

Sleepsteps

Youcanaddsleeptestactionstothefeaturescenarios:

Feature:Sleepfeatures

Scenario:SleepdefaulttimeThensleep

Scenario:SleepmillisecondstimeThensleep200ms

Thestepdefinitionrequiresoneofthefollowingpatterns:

ThensleepThensleep[time]ms

ThisaddsanewsleeptestactiontotheCitrustest.

CitrusReferenceGuide

415Cucumber

Page 416: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

416Cucumber

Page 417: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ZookeepersupportCitrusprovidesconfigurationcomponentsandtestactionsforinteractingwithZookeeper.TheCitrusZookeeperclientcomponentexecutescommandslikecreate-node,checknode-exists,delete-node,getnode-dataorsetnode-data.AsauseryoucanexecuteZookeepercommandsaspartofaCitrustestandvalidatepossiblecommandresults.

NoteTheZookeepertestcomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-zookeeper</artifactId><version>2.7</version></dependency>

Citrusprovidesa"citrus-zookeeper"configurationnamespaceandschemadefinitionforZookeeperrelatedcomponentsandactions.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitruszookeeperconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-zookeeper="http://www.citrusframework.org/schema/zookeeper/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/zookeeper/confighttp://www.citrusframework.org/schema/zookeeper/config/citrus-zookeeper-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

CitrusReferenceGuide

417Zookeeper

Page 418: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Zookeeperclient

BeforeyoucaninteractwithaZookeeperserveryouhavetoconfiguretheZookeeperclient.Asampleconfigurationisprovidedbelowdescribingtheconfigurationoptionsavailable:

<citrus-zookeeper:clientid="zookeeperClient"url="http://localhost:21118"timeout="2000"/>

ThisisatypicalclientconfigurationforconnectingtoaZookeeperserver.Nowyouareabletoexecuteseveralcommands.ThesecommandswillbesenttotheZookeeperserverforexecution.

Zookeepercommands

SeebelowallavailableZookeepercommandsthataCitrusclientisabletoexecute.

info:Retrievesthecurrentstateoftheclientconnectioncreate:CreatesaznodeinaspecifiedpathoftheZooKeepernamespacedelete:DeletesaznodefromaspecifiedpathoftheZooKeepernamespaceexists:Checksifaznodeexistsinthepathchildren:Getsalistofchildrenofaznodeget:Getsthedataassociatedwithaznodeset:Sets/writesdataintothedatafieldofaznode

BeforeweseesomeofthesecommandsinactionwehavetoaddanewtestnamespacetoourtestcasewhenusingtheXMLDSL.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:zookeeper="http://www.citrusframework.org/schema/zookeeper/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/zookeeper/testcasehttp://www.citrusframework.org/schema/zookeeper/testcase/citrus-zookeeper-testcase.xsd"

[...]

</beans>

CitrusReferenceGuide

418Zookeeper

Page 419: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WeaddedtheZookeepernamespacewithprefixzookeeper:sonowwecanstarttoaddspecialtestactionstothetestcase:

XMLDSL

<zookeeper:createzookeeper-client="zookeeperClient"path="/$randomString"acl="OPEN_ACL_UNSAFE"<zookeeper:data>foo</zookeeper:data><zookeeper:expect><zookeeper:result><![CDATA["responseData":"path":"/$randomString"]]></zookeeper:result></zookeeper:expect></zookeeper:create>

<zookeeper:getzookeeper-client="zookeeperClient"path="/$randomString"><zookeeper:expect><zookeeper:result><![CDATA["responseData":"data":"foo"]]></zookeeper:result></zookeeper:expect></zookeeper:getData>

<zookeeper:setzookeeper-client="zookeeperClient"path="/$randomString"><zookeeper:data>bar</zookeeper:data></zookeeper:setData>

WhenusingtheJavaDSLwecandirectlyconfigurethecommandswithafluentAPI.

JavaDSLdesignerandrunner

@CitrusTestpublicvoidtestZookeeper()variable("randomString","citrus:randomString(10)");

zookeeper().create("/$randomString","foo")

CitrusReferenceGuide

419Zookeeper

Page 420: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

.acl("OPEN_ACL_UNSAFE").mode("PERSISTENT").validateCommandResult(newCommandResultCallback<ZooResponse>()@OverridepublicvoiddoWithCommandResult(ZooResponseresult,TestContextcontext)Assert.assertEquals(result.getResponseData().get("path"),context.replaceDynamicContentInString("/$randomString")););

zookeeper().get("/$randomString").validateCommandResult(newCommandResultCallback<ZooResponse>()@OverridepublicvoiddoWithCommandResult(ZooResponseresult,TestContextcontext)Assert.assertEquals(result.getResponseData().get("version"),0););

zookeeper().set("/$randomString","bar");

TheexamplesabovecreateanewznodeinZookeeperusingarandomStringaspath.WecangetandsetthedatawithexpectingandvalidatingtheresultoftheZookeeperserver.ThisisbasicallytheideaofintegratingZookepperoperationstoaCitrustest.ThisopensthegatetomanageZookeeperrelatedentitieswithinaCitrustest.WecanmanipulateandvalidatetheznodesontheZookeeperinstance.

Zookeeperkeepsitsnodesinahierarchicalstorage.Thismeansaznodecanhavechildrenandwecanaddandremovethose.InCitrusyoucangetallchildrenofaznodeandmanagethosewithinthetest:

XMLDSL

<zookeeper:createzookeeper-client="zookeeperClient"path="/$randomString/child1"acl="OPEN_ACL_UNSAFE"<zookeeper:data></zookeeper:data><zookeeper:expect><zookeeper:result><![CDATA["responseData":"path":"/$randomString/child1"]]></zookeeper:result></zookeeper:expect>

CitrusReferenceGuide

420Zookeeper

Page 421: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</zookeeper:create>

<zookeeper:createzookeeper-client="zookeeperClient"path="/$randomString/child2"acl="OPEN_ACL_UNSAFE"<zookeeper:data></zookeeper:data><zookeeper:expect><zookeeper:result><![CDATA["responseData":"path":"/$randomString/child2"]]></zookeeper:result></zookeeper:expect></zookeeper:create>

<zookeeper:childrenzookeeper-client="zookeeperClient"path="/$randomString"><zookeeper:expect><zookeeper:result><![CDATA["responseData":"children":["child1","child2"]]]></zookeeper:result></zookeeper:expect></zookeeper:children>

JavaDSLdesignerandrunner

zookeeper().create("/$randomString/child1","").acl("OPEN_ACL_UNSAFE").mode("PERSISTENT").validateCommandResult(newCommandResultCallback<ZooResponse>()@OverridepublicvoiddoWithCommandResult(ZooResponseresult,TestContextcontext)Assert.assertEquals(result.getResponseData().get("path"),context.replaceDynamicContentInString("/$randomString/child1")););

zookeeper().create("/$randomString/child2","").acl("OPEN_ACL_UNSAFE").mode("PERSISTENT").validateCommandResult(newCommandResultCallback<ZooResponse>()

CitrusReferenceGuide

421Zookeeper

Page 422: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

@OverridepublicvoiddoWithCommandResult(ZooResponseresult,TestContextcontext)Assert.assertEquals(result.getResponseData().get("path"),context.replaceDynamicContentInString("/$randomString/child2")););

zookeeper().children("/$randomString").validateCommandResult(newCommandResultCallback<ZooResponse>()@OverridepublicvoiddoWithCommandResult(ZooResponseresult,TestContextcontext)Assert.assertEquals(result.getResponseData().get("children").toString(),"[child1,child2]"););

CitrusReferenceGuide

422Zookeeper

Page 423: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SpringRestdocssupportSpringRestdocsprojecthelpstoeasilygenerateAPIdocumentationforRESTfulservices.WhilemessagesareexchangedtheRestdocslibrarygeneratesrequest/responsesnippetsandAPIdocumentation.YoucanaddtheSpringRestdocsdocumentationtotheCitrusclientcomponentsforHttpandSOAPendpoints.

NoteTheSpringRestdocssupportcomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-restdocs</artifactId><version>2.7</version></dependency>

ForeasyconfigurationCitrushascreatedaseparatenamespaceandschemadefinitionforSpringRestdocsrelateddocumentation.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusRestdocsconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<spring:beansxmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.citrusframework.org/schema/cucumber/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/restdocs/confighttp://www.citrusframework.org/schema/restdocs/config/citrus-restdocs-config.xsd">

[...]

</spring:beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

SpringRestdocsusingHttp

CitrusReferenceGuide

423Restdocs

Page 424: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FirstofallweconcentrateonaddingtheSpringRestdocsfeaturetoHttpclientcommunication.ThenextsampleconfigurationusesthenewSpringRestdocscomponentsinCitrus:

<citrus-restdocs:documentationid="restDocumentation"output-directory="test-output/generated-snippets"identifier="rest-docs/method-name"/>

Theabovecomponentaddsanewdocumentationconfiguration.Behindthescenesthecomponentcreatesanewrestdocsconfigurerandaclientinterceptor.Wecanreferencethenewrestdocscomponentincitrus-httpclientcomponentslikethis:

<citrus-http:clientid="httpClient"request-url="http://localhost:8080/test"request-method="POST"interceptors="restDocumentation"/>

TheSpringRestdocsdocumentationcomponentactsasaclientinterceptor.EverytimetheclientcomponentisusedtosendandreceiveamessagetherestdocsinterceptorwillautomaticallycreateitsAPIdocumentation.Theconfigurationidentifierattributedescribestheoutputformatrest-docs/method-namewhichresultsinafolderlayoutlikethis:

test-output|-rest-docs|-test-a|-curl-request.adoc|-http-request.adoc|-http-response.adoc|-test-b|-curl-request.adoc|-http-request.adoc|-http-response.adoc|-test-c|-curl-request.adoc|-http-request.adoc|-http-response.adoc

TheexampleaboveistheresultofthreetestcaseseachofthemperformingaclientHttprequest/responsecommunication.Eachtestmessageexchangeisdocumentedwithseparatefiles:

CitrusReferenceGuide

424Restdocs

Page 425: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

curl-request.adoc

[source,bash]----$curl'http://localhost:8080/test'-i-XPOST-H'Accept:application/xml'-H'CustomHeaderId:123456789'-H'Content-Type:application/xml;charset=UTF-8'-H'Accept-Charset:utf-8'-d'>testRequestMessage>>text>HelloHttpServer>/text>>/testRequestMessage>'----

Thecurlfilerepresentstheclientrequestascurlcommandandcanbeseenasasampletoreproducetherequest.

http-request.adoc

[source,http,options="nowrap"]----POST/testHTTP/1.1Accept:application/xmlCustomHeaderId:123456789Content-Type:application/xml;charset=UTF-8Content-Length:118Accept-Charset:utf-8Host:localhost

>testRequestMessage>>text>HelloHttpServer>/text>>/testRequestMessage>----

Thehttp-request.adocfilerepresentsthesentmessagedatafortheclientrequest.Therespectivehttp-response.adocrepresentstheresponsethatwassenttotheclient.

http-response.adoc

[source,http,options="nowrap"]----HTTP/1.1200OKDate:Tue,07Jun201612:10:46GMTContent-Type:application/xml;charset=UTF-8Accept-Charset:utf-8Content-Length:122Server:Jetty(9.2.15.v20160210)

>testResponseMessage>>text>HelloCitrus!>/text>>/testResponseMessage>

CitrusReferenceGuide

425Restdocs

Page 426: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

----

Nicework!WehaveautomaticallycreatedsnippetsfortheRESTfulAPIbyjustaddingtheinterceptortotheCitrusclientcomponent.SpringRestdocscomponentscanbecombinedmanually.Seethenextconfigurationthatusesthisapproach.

<citrus-restdocs:configurerid="restDocConfigurer"output-directory="test-output/generated-snippets"<citrus-restdocs:client-interceptorid="restDocClientInterceptor"identifier="rest-docs/method-name"

<util:listid="restDocInterceptors"><refbean="restDocConfigurer"/><refbean="restDocClientInterceptor"/></util:list>

<citrus-http:clientid="httpClient"request-url="http://localhost:8080/test"request-method="POST"interceptors="restDocInterceptors"/>

Whatexactlyisthedifferencetothecitrus-restdocs:documentationthatwehaveusedbefore?Ingeneralthereisnodifference.Bothconfigurationsareidenticalinitsoutcome.Whyshouldsomeoneusethesecondapproachthen?Itismoreverboseasweneedtoalsodefinealistofinterceptors.Theansweriseasy.Ifyouwanttocombinetherestdocsinterceptorswithotherclientinterceptorsinalistthenyoushouldusethemanualcombinationapproach.Wecanaddbasicauthenticationinterceptorsforinstancetothelistofinterceptorsthen.Themorecomfortablecitrus-restdocs:documentationcomponentonlysupportsexclusiverestdocsinterceptors.

SpringRestdocsusingSOAP

YoucanusetheSpringRestdocsfeaturesalsoforSOAPclientsinCitrus.ThisisacontroversyideaasSOAPendpointsaredifferenttoRESTfulconcepts.ButattheendSOAPHttpcommunicationisHttpcommunicationwithrequestandresponsemessages.Whyshouldwemissoutthefantasticdocumentationfeatureherejustbecauseofideologyreasons.

TheconceptofaddingtheSpringRestdocsdocumentationasinterceptortotheclientisstillthesame.

<citrus-restdocs:documentationid="soapDocumentation"

CitrusReferenceGuide

426Restdocs

Page 427: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

type="soap"output-directory="test-output/generated-snippets"identifier="soap-docs/method-name"/>

Wehaveaddedatypesettingwithvaluesoap.Andthatisbasicallyallweneedtodo.NowCitrusknowsthatwewouldliketoadddocumentationforaSOAPclient:

<citrus-ws:clientid="soapClient"request-url="http://localhost:8080/test"interceptors="soapDocumentation"/>

FollowingfromthatthesoapClientisenabledtogenerateSpringRestdocsdocumentationforeachrequest/response.ThegeneratedsnippetsthendorepresenttheSOAPrequestandresponsemessages.

http-request.adoc

[source,http,options="nowrap"]----POST/testHTTP/1.1SOAPAction:"test"Accept:application/xmlCustomHeaderId:123456789Content-Type:application/xml;charset=UTF-8Content-Length:529Accept-Charset:utf-8Host:localhost

>SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">>SOAP-ENV:Header>>Operationxmlns="http://citrusframework.org/test">sayHello>/Operation>>/SOAP-ENV:Header>>SOAP-ENV:Body>>testRequestMessage>>text>HelloHttpServer>/text>>/testRequestMessage>>/SOAP-ENV:Body>>/SOAP-ENV:Envelope>----

http-response.adoc

[source,http,options="nowrap"]----HTTP/1.1200OK

CitrusReferenceGuide

427Restdocs

Page 428: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Date:Tue,07Jun201612:10:46GMTContent-Type:application/xml;charset=UTF-8Accept-Charset:utf-8Content-Length:612Server:Jetty(9.2.15.v20160210)

>SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">>SOAP-ENV:Header>>Operationxmlns="http://citrusframework.org/test">sayHello>/Operation>>/SOAP-ENV:Header>>SOAP-ENV:Body>>testResponseMessage>>text>HelloCitrus!>/text>>/testResponseMessage>>/SOAP-ENV:Body>>/SOAP-ENV:Envelope>----

Thefilenamesarestillusinghttp-requestandhttp-responsebutthecontentisclearlytheSOAPrequest/responsemessagedata.

SpringRestdocsinJavaDSL

HowcanweuseSpringRestdocsinJavaDSL?OfcoursewehavespecialsupportinCitrusJavaDSLfortheSpringRestdocsconfiguration,too.

JavaDSL

publicclassRestDocConfigurationITextendsTestNGCitrusTestDesigner

@AutowiredprivateTestListenerstestListeners;

privateHttpClienthttpClient;

@BeforeClasspublicvoidsetup()CitrusRestDocConfigurerrestDocConfigurer=CitrusRestDocsSupport.restDocsConfigurer(newManualRestDocumentation("target/generated-snippets"));RestDocClientInterceptorrestDocInterceptor=CitrusRestDocsSupport.restDocsInterceptor("rest-docs/method-name");

httpClient=CitrusEndpoints.http().client().requestUrl("http://localhost:8073/test").requestMethod(HttpMethod.POST).contentType("text/xml").interceptors(Arrays.asList(restDocConfigurer,restDocInterceptor)).build();

CitrusReferenceGuide

428Restdocs

Page 429: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

testListeners.addTestListener(restDocConfigurer);

@Test@CitrusTestpublicvoidtestRestDocs()http().client(httpClient).send().post().payload("<testRequestMessage>"+"<text>HelloHttpServer</text>"+"</testRequestMessage>");

http().client(httpClient).receive().response(HttpStatus.OK).payload("<testResponseMessage>"+"<text>HelloTestFramework</text>"+"</testResponseMessage>");

ThemechanismisquitesimilartotheXMLconfiguration.WeaddtheRestdocsconfigurerandinterceptortothelistofinterceptorsfortheHttpclient.Ifwedothisallclientcommunicationisautomaticallydocumented.TheCitrusJavaDSLprovidessomeconvenientconfigurationmethodsinclassCitrusRestDocsSupportforcreatingtheconfigurerandinterceptorobjects.

NoteTheconfigurermustbeaddedtothelistoftestlisteners.Thisisamandatorystepinordertoenabletheconfigurerfordocumentationpreparationsbeforeeachtest.Otherwisewewouldnotbeabletogenerateproperdocumentation.IfyouareusingtheXMLconfigurationthisisdoneautomaticallyforyou.

CitrusReferenceGuide

429Restdocs

Page 430: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SeleniumsupportSeleniumisaverypopulartoolfortestinguserinterfaceswithbrowserautomation.CitrusisabletointegratewiththeSeleniumJavaAPIinordertoexecuteSeleniumcommands.

NoteTheSeleniumtestcomponentsinCitrusarekeptinaseparateMavenmodule.IfnotalreadydonesoyouhavetoincludethemoduleasMavendependencytoyourproject

<dependency><groupId>com.consol.citrus</groupId><artifactId>citrus-selenium</artifactId><version>2.7</version></dependency>

Citrusprovidesa"citrus-selenium"configurationnamespaceandschemadefinitionforSeleniumrelatedcomponentsandactions.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusSeleniumconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-selenium="http://www.citrusframework.org/schema/selenium/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/selenium/confighttp://www.citrusframework.org/schema/selenium/config/citrus-selenium-config.xsd">

[...]

</beans>

AfterthatyouareabletousecustomizedCitrusXMLelementsinordertodefinetheSpringbeans.

Seleniumbrowser

CitrusReferenceGuide

430Selenium

Page 431: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Seleniumusesbrowserautomationinordertosimulatetheuserinteractwithwebapplications.YoucanconfiguretheSeleniumbrowserandwebdriverasSpringbean.

<citrus-selenium:browserid="seleniumBrowser"type="firefox"start-page="http://citrusframework.org"/>

TheSeleniumbrowsercomponentsupportsdifferentbrowsertypesforthecommonlyusedbrowsersoutinthewild.

htmlunitfirefoxsafarichromegooglechromeinternetexploreredgecustom

Htmlunitisthedefaultbrowsertypeandrepresentsaheadlessbrowserthatexecutedwithoutdisplayingthegraphicaluserinterface.IncaseyouneedatotallydifferentbrowseroryouneedtocustomizetheSeleniumwebdriveryoucanusethebrowserType="custom"incombinationwithawebdriverreference:

<citrus-selenium:browserid="mySeleniumBrowser"type="custom"web-driver="operaWebDriver"/>

<beanid="operaWebDriver"class="org.openqa.selenium.opera.OperaDriver"/>

NowCitrusisusingthecustomizedSeleniumwebdriverimplementation.

NoteWhenusingFirefoxasbrowseryoumayalsowanttosettheoptionalpropertiesfirefox-profileandversion.

<citrus-selenium:browserid="mySeleniumBrowser"type="firefox"firefox-profile="firefoxProfile"version="FIREFOX_38"start-page="http://citrusframework.org"/>

<beanid="firefoxProfile"class="org.openqa.selenium.firefox.FirefoxProfile"/>

CitrusReferenceGuide

431Selenium

Page 432: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowCitrusisabletoexecuteSeleniumoperationsasauser.

Seleniumactions

WehaveseveralCitrustestactionseachrepresentingaSeleniumcommand.TheseactionscanbepartofaCitrustestcase.AsaprerequisitewehavetoenabletheSeleniumspecifictestactionsinourXMLtestasfollows:

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:selenium="http://www.citrusframework.org/schema/selenium/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/selenium/testcasehttp://www.citrusframework.org/schema/selenium/testcase/citrus-selenium-testcase.xsd"

[...]

</beans>

Weaddedaspecialseleniumnamespacewithprefixselenium:sonowwecanstarttoaddSeleniumtestactionstothetestcase:

XMLDSL

<testcasename="SeleniumCommandIT"><actions><selenium:startbrowser="webBrowser"/>

<selenium:navigatepage="http://localhost:8080"/>

<selenium:find><selenium:elementtag-name="h1"text="Welcome!"><selenium:styles><selenium:stylename="font-size"value="40px"/></selenium:styles></selenium:element></selenium:find>

<selenium:click><selenium:elementid="ok-button"/></selenium:click></actions></testcase>

CitrusReferenceGuide

432Selenium

Page 433: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

InthisverysimpleexamplewefirststarttheSeleniumbrowserinstance.AfterthatwecancontinuetouseSeleniumcommandswithoutbrowserattributeexplicitlyset.Citrusknowswhichbrowserinstanceiscurrentlyactiveandwillautomaticallyusethisopenedbrowserinstance.Nextinthisexamplewefindsomeelementonthedisplayedpagebyitstag-nameandtext.Wealsovalidatetheelementstylefont-sizetomeettheexpectedvalue40pxinthisstep.

Inadditiontothattheexampleperformsaclickoperationontheelementwiththeidok-button.Seleniumsupportselementfindoperationsondifferentproperties:

idfindselementbasedontheidattributenamefindselementbasedonthenameattributetag-namefindselementbasedonthetagnameclass-namefindselementbasedonthecssclassnamelink-textfindslinkelementbasedonthelink-textxpathfindselementbasedonXPathevaluationintheDOM

BasedonthatwecanexecuteseveralSeleniumcommandsinatestcaseandvalidatetheresultssuchaswebelements.

CitrussupportsthefollowingSeleniumcommandswithrespectivetestactions:

selenium:startStartthebrowserinstanceselenium:findFindselementoncurrentpageandvalidateselementpropertiesselenium:clickPerformsclickoperationonelementselenium:hoverPerformshoveroperationonelementselenium:navigateNavigatestonewpageurl(includinghistoryback,forwardandrefresh)selenium:set-inputFindsinputelementandsetsvalueselenium:check-inputFindscheckboxelementandsets/unsetsvalueselenium:dropdown-selectFindsdropdownelementandselectssingleormultiplevalue/sselenium:pageInstantiatepageobjectwithdependencyinjectionandexecutepageactionwithverificationselenium:openOpennewwindowselenium:closeClosewindowbygivennameselenium:switchSwitchfocustowindowwithgivennameselenium:wait-untilWaitforelementtobehiddenorvisibleselenium:alertAccesscurrentalertdialog(withactionaccessordismiss)selenium:screenshotMakesscreenshotofcurrentpage

CitrusReferenceGuide

433Selenium

Page 434: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

selenium:store-fileStorefiletotemporarybrowserdirectoryselenium:get-stored-fileGetsstoredfilefromtemporarybrowserdirectoryselenium:javascriptExecuteJavascriptcodeinbrowserselenium:clear-cacheClearbrowsercacheandallcookiesselenium:stopStopsthebrowserinstance

UptonowwehaveonlyusedtheCitrusXMLDSL.OfcourseallSeleniumcommandsarealsoavailableinJavaDSLasthenextexampleshows.

JavaDSL

@AutowiredprivateSeleniumBrowserseleniumBrowser;

@CitrusTestpublicvoidseleniumTest()selenium().start(seleniumBrowser);

selenium().navigate("http://localhost:8080");

selenium().find().element(By.id("header"));.tagName("h1").enabled(true).displayed(true).text("Welcome!").style("font-size","40px");

selenium().click().element(By.linkText("ClickMe!"));

NowletshaveacloserlookatthedifferentSeleniumtestactionssupportedinCitrus.

Start/stopbrowser

Youcanstartandstopthebrowserinstancewithatestaction.Thisinstantiatesanewbrowserwindowandprepareseverythingforinteractingwiththewebinterface.

XMLDSL

<selenium:startbrowser="seleniumBrowser"/>

<!--Dosomethinginbrowser-->

<selenium:stopbrowser="seleniumBrowser"/>

CitrusReferenceGuide

434Selenium

Page 435: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

JavaDSL

selenium().start(seleniumBrowser);

//dosomethinginbrowser

selenium().stop(seleniumBrowser);

AfterstartingabrowserinstanceCitruswillautomaticallyusethisverysamebrowserinstanceinallfurtherSeleniumactions.Thismechanismisbasedonatestvariable(selenium_browser)thatisautomaticallyset.Allothertestactionsareabletoloadthecurrentbrowserinstancebyreadingthistestvariablebeforeexecution.IncaseyouneedtoexplicitlyuseadifferentbrowserinstancethantheactiveinstanceyoucanaddthebrowserattributetoallSeleniumtestactions.

Note

Itisagoodideatostartandstopthebrowserinstancebeforeeachtestcase.Thismakessurethattestsarealsoexecutableinsinglerunanditalwayssetsupanewbrowserinstancesotestswillnotinfluenceeachother.

Find

Thefindelementtestactionsearchesforanelementonthecurrentpage.Theelementisspecifiedbyoneofthefollowingsettings:

idfindselementbasedontheidattributenamefindselementbasedonthenameattributetag-namefindselementbasedonthetagnameclass-namefindselementbasedonthecssclassnamelink-textfindslinkelementbasedonthelink-textxpathfindselementbasedonXPathevaluationintheDOM

Thefindelementactionwillautomaticallyfailincasethereisnosuchelementonthecurrentpage.Incasetheelementisfoundyoucanaddadditionalattributesandpropertiesforfurtherelementvalidation:

XMLDSL

<selenium:find><selenium:elementtag-name="h1"text="Welcome!"><selenium:styles><selenium:stylename="font-size"value="40px"/>

CitrusReferenceGuide

435Selenium

Page 436: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</selenium:styles></selenium:element></selenium:find>

<selenium:find><selenium:elementid="ok-button"text="Ok"enabled="true"displayed="true"><selenium:attributes><selenium:attributename="type"value="submit"/></selenium:attributes></selenium:element></selenium:find>

JavaDSL

selenium().find().element(By.tagName("h1")).text("Welcome!").style("font-size","40px");

selenium().find().element(By.id("ok-button")).tagName("button").enabled(true).displayed(true).text("Ok").style("color","red").attribute("type","submit");

Theexampleabovefindstheh1elementbyitstagnameandvalidatesthetextandcssstyleattributes.Secondlytheok-buttonisvalidatedwithexpectedenabled,displayed,text,styleandattributevalues.Theelementsmustbepresentonthecurrentpageandallexpectedelementpropertieshavetomatch.Otherwisethetestactionandthetestcaseisfailingwithvalidationerrors.

Click

Theactionperformsaclickoperationontheelement.

XMLDSL

<selenium:click><selenium:elementlink-text="ClickMe!"/></selenium:click>

JavaDSL

CitrusReferenceGuide

436Selenium

Page 437: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

selenium().click().element(By.linkText("ClickMe!"));

Hover

Theactionperformsahoveroperationontheelement.

XMLDSL

<selenium:hover><selenium:elementlink-text="FindMe!"/></selenium:hover>

JavaDSL

selenium().hover().element(By.linkText("FindMe!"));

Forminputactions

Thefollowingactionsareusedtoaccessforminputelementssuchastextfields,checkboxesanddropdownlists.

XMLDSL

<selenium:set-inputvalue="Citrus"><selenium:elementname="username"/></selenium:set-input>

<selenium:check-inputchecked="true"><selenium:elementxpath="//input[@type='checkbox']"/></selenium:check-input>

<selenium:dropdown-selectoption="happy"><selenium:elementid="user-mood"/></selenium:dropdown-select>

JavaDSL

selenium().setInput("Citrus").element(By.name("username"));selenium().checkInput(true).element(By.xpath("//input[@type='checkbox']"));

selenium().select("happy").element(By.id("user-mood"));

CitrusReferenceGuide

437Selenium

Page 438: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Theactionsaboveselectdropdownoptionsandsetuserinputontextfieldsandcheckboxes.Asusualtheformelementsareselectedbysomepropertiessuchasids,namesorxpathexpressions.

Pageactions

PageobjectsareawellknownpatternwhenusingSelenium.Thepageobjectsdefineelementsthatthepageisworkingwith.Inadditiontothatthepageobjectsdefineactionsthatcanbeexecutedfromoutside.Thisobjectorientedapproachforaccessingpagesandtheirelementsisaverygoodidea.Letshavealookatasamplepageobject.

publicclassUserFormPageimplementsWebPage

@FindBy(id="userForm")privateWebElementform;

@FindBy(id="username")privateWebElementuserName;

/***Setstheusername.*/publicvoidsetUserName(Stringvalue,TestContextcontext)userName.clear();userName.sendKeys(value);

/***Submitstheform.*@paramcontext*/publicvoidsubmit(TestContextcontext)form.submit();

AsyoucanseethepageobjectisaJavaPOJOthatimplementstheWebPageinterface.ThepagedefinesWebElementmembers.TheseareautomaticallyinjectedbyCitrusandSeleniumbasedontheFindByannotation.NowthetestcaseisabletoloadthatpageobjectandexecutesomeactionmethodsonthepagesuchassetUserNameorsubmit.

XMLDSL

<selenium:pagetype="com.consol.citrus.selenium.pages.UserFormPage"

CitrusReferenceGuide

438Selenium

Page 439: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

action="setUserName"><selenium:arguments><selenium:argument>Citrus</selenium:argument></selenium:arguments></selenium:page>

<selenium:pagetype="com.consol.citrus.selenium.pages.UserFormPage"action="submit"/>

JavaDSL

selenium().page(UserFormPage.class).argument("Citrus").execute("setUserName");

selenium().page(UserFormPage.class).execute("submit");

ThepageobjectclassisautomaticallyloadedandinstantiatedwithdependencyinjectionforallFindByannotatedwebelements.Afterthattheactionmethodisexecuted.TheactionmethodscanalsohavemethodparametersasseeninsetUserName.Thevalueparameterisautomaticallysetwhencallingthemethod.

MethodscanalsousetheoptionalparameterTestContext.Withthiscontextyoucanaccessthecurrenttestcontextwithalltestvariablesforinstance.Thismethodparametershouldalwaysbethelastparameter.

Pagevalidation

Wecanalsousepageobjectforvalidationpurpose.Thepageobjectisloadedandinstantiatedasdescribedinprevioussection.Thenthepagevalidatoriscalled.Thevalidatorperformsassertionsandvalidationoperationswiththepageobject.Letsseeasamplepagevalidator:

publicclassUserFormValidatorimplementsPageValidator<UserFormPage>

@Overridepublicvoidvalidate(UserFormPagewebPage,SeleniumBrowserbrowser,TestContextcontext)Assert.isTrue(webPage.getUserName()!=null);Assert.isTrue(StringUtils.hasText(webPage.getUserName().getAttribute("value")));

CitrusReferenceGuide

439Selenium

Page 440: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thepagevalidatoriscalledwiththewebpageinstance,thebrowserandthetestcontext.Thevalidatorshouldassertpageobjectsandwebelementsforvalidationpurpose.Inatestcasewecancallthevalidatortovalidatethepage.

XMLDSL

<beanid="userFormValidator"class="com.consol.citrus.selenium.pages.UserFormValidator"/>

<selenium:pagetype="com.consol.citrus.selenium.pages.UserFormPage"action="validate"validator="userFormValidator"/>

JavaDSL

@AutowiredprivateUserFormValidatoruserFormValidator;

selenium().page(UserFormPage.class).execute("validate").validator(userFormValidator);

Insteadofusingaseparatevalidatorclassyoucanalsoputthevalidationmethodtothepageobjectitself.Thenpageobjectandvalidationisdonewithinthesameclass:

publicclassUserFormPageimplementsWebPage,PageValidator<UserFormPage>

@FindBy(id="userForm")privateWebElementform;

@FindBy(id="username")privateWebElementuserName;

/***Setstheusername.*/publicvoidsetUserName(Stringvalue,TestContextcontext)userName.clear();userName.sendKeys(value);

/***Submitstheform.*@paramcontext*/publicvoidsubmit(TestContextcontext)form.submit();

@Override

CitrusReferenceGuide

440Selenium

Page 441: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

publicvoidvalidate(UserFormPagewebPage,SeleniumBrowserbrowser,TestContextcontext)Assert.isTrue(userName!=null);Assert.isTrue(StringUtils.hasText(userName.getAttribute("value")));Assert.isTrue(form!=null);

XMLDSL

<selenium:pagetype="com.consol.citrus.selenium.pages.UserFormPage"action="validate"/>

JavaDSL

selenium().page(UserFormPage.class).execute("validate");

Wait

Sometimesitisrequiredtowaitforanelementtoappearordisappearonthecurrentpage.Thewaitactionwillwaitagiventimefortheelementstatustobevisibleorhidden.

XMLDSL

<selenium:waituntil="hidden"><selenium:elementid="info-dialog"/></selenium:wait>

JavaDSL

selenium().waitUntil().hidden().element(By.id("info-dialog"));

Theexamplewaitsfortheelementinfo-dialogtodisappear.Thetimetowaitis5000millisecondsbydefault.Youcansetthetimeoutontheaction.DuetoSeleniumlimitationstheminimumwaittimeis1000milliseconds.

Navigate

TheactionnavigatestoanewpageeitherbyusinganewrelativepathoracompletenewHttpURL.

CitrusReferenceGuide

441Selenium

Page 442: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLDSL

<selenium:navigatepage="http://localhost:8080"/>

<selenium:navigatepage="help"/>

JavaDSL

selenium().navigate("http://localhost:8080");

selenium().navigate("help");

ThesampleabovedescribesanewpagewithnewHttpURL.Thebrowserwillnavigatetothisnewpage.AllfurtherSeleniumactionsareperformedonthisnewpage.ThesecondnavigationactionopenstherelativepagehelpsothenewpageURLishttp://localhost:8080/help.

Navigationisalwaysdoneontheactivebrowserwindow.Youcanmanagetheopenedwindowsasdescribedinnextsection.

Windowactions

Seleniumisabletomanagemultiplewindows.Soyoucanopen,closeandswichactivewindowsinaCitrustest.

XMLDSL

<selenium:open-windowname="my_window"/><selenium:switch-windowname="my_window"/><selenium:close-windowname="my_window"/>

JavaDSL

selenium().open().window("my_window");selenium().focus().window("my_window");selenium().close().window("my_window");

WhenanewwindowisopenedSeleniumcreatesawindowhandleforus.Thiswindowhandleissavedastestvariableusingagivenwindowname.Soafteropeningthewindowyoucanaccessthewindowbyitsnameinfurtheractions.Allupcoming

CitrusReferenceGuide

442Selenium

Page 443: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Seleniumactionswilltakeplaceinthisnewactivewindow.Ofcoursethetestactionswillfailassoonasthewindowwiththatgivennameismissing.Citrususesdefaultwindownamesthatareautomaticallyusedastestvariables:

selenium_active_windowtheactivewindowhandleselenium_last_windowthelastwindowhandlewhenswitchedtootherwindow

Alert

Weareabletoaccessthealertdialogonthecurrentpage.Citruswillvalidatethedisplayeddialogtextandacceptordismissofthedialog.

XMLDSL

<selenium:alertaccept="true"><selenium:alert-text>Hello!</selenium:alert-text></selenium:alert>

JavaDSL

selenium().alert().text("Hello!").accept();

Thealertdialogtextisvalidatedwhenexpectedtextisgivenonthetestaction.Theusercandecidetoacceptordismissthedialog.Afterthatthedialogshouldbeclosed.Incasethetestactionfailstofindanopenalertdialogthetestactionraisesruntimeerrorsandthetestwillfail.

Makescreenshot

Youcanexecutethisactionincaseyouwanttotakeascreenshotofthecurrentpage.Thisactiononlyworkswithbrowsersthatactuallydisplaytheuserinterface.TheactionwillnothaveanyeffectwhenexecutedwithHtmlunitwebdriverinheadlessmode.

XMLDSL

<selenium:screenshot/>

<selenium:screenshotoutput-dir="target"/>

JavaDSL

CitrusReferenceGuide

443Selenium

Page 444: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

selenium().screenhsot();

selenium().screenhsot("target");

Thetestactionhasanoptionalparameteroutput-dirwhichrepresentstheoutputdirectorywherethescreenshotissavedto.

Temporarystorage(Firefox)

ImportantThisactiononlyworkswithFirefoxwebdriver!Otherbrowsersarenotworkingwiththetemporarydownloadstorage.

Thebrowserusesatemporarystoragefordownloadedfiles.Wecanaccessthistemporarystorageduringatestcase.

XMLDSL

<selenium:store-filefile-path="classpath:download/file.txt"/><selenium:get-stored-filefile-name="file.txt"/>

JavaDSL

selenium().store("classpath:download/file.txt");selenium().getStored("file.txt");

Asyoucanseethetestcaseisabletostorenewfilestothetemporarybrowserstorage.Wehavetogivethefilepathasclasspathorfilesystempath.Whenreadingthetemporaryfilestorageweneedtospecifythefilenamethatwewanttoaccessinthetemporarystorage.Thetemporarystorageisnotcapableofsubdirectoriesallfilesarestoreddirectlytothestorageinonesingledirectory.

Incasethestoredfileisnotfoundbythatnamethetestactionfailswithrespectiveerrors.OntheotherhandwhenthefileisfoundintemporarystorageCitruswillautomaticallycreateanewtestvariableselenium_download_filewhichcontainsthefilenameasvalue.

Clearbrowsercache

Whenclearingthebrowsercacheallcookiesandtemporaryfileswillbedeleted.

XMLDSL

CitrusReferenceGuide

444Selenium

Page 445: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<selenium:clear-cache/>

JavaDSL

selenium().clearCache();

CitrusReferenceGuide

445Selenium

Page 446: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DynamicendpointcomponentsEndpointsrepresentthecentralcomponentsinCitrustosendorreceiveamessageonsomedestination.UsuallyendpointsgetdefinedinthebasicCitrusSpringapplicationcontextconfigurationasSpringbeancomponents.Insomecasesthismightbeoverengineeringasthetesterjustwantstosendorreceiveamessage.Inparticularthisisdonewhendoingsanitychecksinserverendpointswhiledebuggingacertainscenario.

WithendpointcomponentsyouareabletocreatetheCitrusendpointforsendingandreceivingamessageattestruntime.ThereisnoadditionalconfigurationorSpringbeancomponentneeded.YoujustusetheendpointuriinaspecialnamingconventionandCitruswillcreatetheendpointforyou.Letusseeafirstexampleofthisscenario:

<testcasename="DynamicEndpointTest"><actions><sendendpoint="jms:Hello.Queue?timeout=10000"><message><payload>[...]</payload></message></send>

<receiveendpoint="jms:Hello.Response.Queue?timeout=5000"><message><payload>[...]</payload></message></receive></actions></testcase>

Asyoucanseetheendpointurijustgoesintothetestcaseactioninsubstitutiontotheusualendpointreferencename.InsteadofreferencingabeanidthatpointstothepreviouslyconfiguredCitrusendpointweusetheendpointuridirectly.Theendpointurishouldgiveallinformationtocreatetheendpointatruntime.Intheexampleaboveweuseakeywordjms:whichtellsCitrusthatweneedtocreateaJMSmessageendpoint.SecondlywegivetheJMSdestinationnameHello.Queuewhichisamandatorypartof

CitrusReferenceGuide

446Endpointcomponent

Page 447: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

theendpointuriwhenusingtheJMScomponent.Theoptionaltimeoutparametercompletedtheuri.CitrusisabletocreatetheJMSendpointatruntimesendingthemessagetothedefineddestinationviaJMS.

OfcoursethismechanismisnotlimitedtoJMSendpoints.WecanusealldefaultCitrusmessagetransportsintheendpointuri.Justpicktherightkeywordthatdefinesthemessagetransporttouse.Hereisalistofsupportedkeywords:

jms:CreatesaJMSendpointforsendingandreceivingmessagetoaqueueortopicchannel:CreatesachannelendpointforsendingandreceivingmessagesusinganinmemorySpringIntegrationmessagechannelhttp:CreatesaHTTPclientforsendingarequesttosomeserverURLsynchronouslywaitingfortheresponsemessagews:CreatesaWebSocketclientforsendingmessagestoorreceivingmessagesfromaWebSocketserversoap:CreatesaSOAPWebServiceclientthatsendaproperSOAPmessagetotheserverURLandwaitsforthesynchronousresponsetoarrivessh:Createsanewsshclientforpublishingacommandtotheservermail:orsmtp:CreatesanewmailclientforsendingamailmimemessagetoaSMTPservercamel:CreatesanewApacheCamelendpointforsendingandreceivingCamelexchangesbothtoandfromCamelroutes.vertx:oreventbus:CreatesanewVert.xinstancesendingandreceivingmessageswiththenetworkeventbusrmi:CreatesanewRMIclientinstancesendingandreceivingmessagesformethodinvocationonremoteinterfacesjmx:CreatesanewJMXclientinstancesendingandreceivingmessagestoandfromamanagedbeanserver.

Dependingonthemessagetransportwehavetoaddmandatoryparameterstotheendpointuri.IntheJMSexamplewehadtospecifythedestinationname.Themandatoryparametersarealwayspartoftheendpointuri.Optionalparameterscanbeaddedaskeyvaluepairstotheendpointuri.Theavailableparametersdependontheendpointkeywordthatyouhavechosen.Seetheseexampleendpointuriexpressions:

jms:queuename?connectionFactory=specialConnectionFactory&timeout=10000jms:topic:topicname?connectionFactory=topicConnectionFactoryjms:sync:queuename?connectionFactory=specialConnectionFactory&pollingInterval=100&replyDestination=myReplyDestination

channel:channelName

CitrusReferenceGuide

447Endpointcomponent

Page 448: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

channel:sync:channelNamechannel:channelName?timeout=10000&channelResolver=myChannelResolver

http:localhost:8088/testhttp://localhost:8088/testhttp:localhost:8088?requestMethod=GET&timeout=10000&errorHandlingStrategy=throwsException&requestFactory=myRequestFactoryhttp://localhost:8088/test?requestMethod=DELETE&customParam=foo

websocket:localhost:8088/testwebsocket://localhost:8088/testws:localhost:8088/testws://localhost:8088/test

soap:localhost:8088/testsoap:localhost:8088?timeout=10000&errorHandlingStrategy=propagateError&messageFactory=myMessageFactory

mail:localhost:25000smtp://localhost:25000smtp://localhost?timeout=10000&username=foo&password=1234&mailMessageMapper=myMapper

ssh:localhost:2200ssh://localhost:2200?timeout=10000&strictHostChecking=true&user=foo&password=12345678

rmi://localhost:1099/someServicermi:localhost/someService&timeout=10000

jmx:rmi:///jndi/rmi://localhost:1099/someServicejmx:platform&timeout=10000

camel:direct:addresscamel:seda:addresscamel:jms:queue:someQueue?connectionFactory=myConnectionFactorycamel:activemq:queue:someQueue?concurrentConsumers=5&destination.consumer.prefetchSize=50camel:controlbus:route?routeId=myRoute&action=status

vertx:addressNamevertx:addressName?port=10105&timeout=10000&pubSubDomain=truevertx:addressName?vertxInstanceFactory=vertxFactory

Theoptionalparametersgetdirectlysetasendpointconfiguration.YoucanuseprimitivevaluesaswellasSpringbeanidreferences.CitruswillautomaticallydetectthetargetparametertypeandresolvethevaluetoaSpringbeanintheapplicationcontextifnecessary.IfyouusesomeunknownparameterCitruswillraiseanexceptionatruntimeastheendpointcouldnotbecreatedproperly.

CitrusReferenceGuide

448Endpointcomponent

Page 449: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Insynchronouscommunicationwehavetoreuseendpointcomponentsinordertoreceivesynchronousmessagesonreplydestinations.Thisisaproblemwhenusingdynamicendpointsastheendpointsgetcreatedatruntime.Citrususesacachingofendpointsthatgetcreatedatruntime.Followingfromthatwehavetousetheexactsameendpointuriinyourtestcaseinordertogetthecachedendpointinstance.Withthislittletricksynchronouscommunicationwillworkjustasitisdonewithstaticendpointcomponents.Havealookatthissampletest:

<testcasename="DynamicEndpointTest"><actions><sendendpoint="jms:sync:Hello.Sync.Queue"><message><payload>[...]</payload></message></send>

<receiveendpoint="jms:sync:Hello.Sync.Queue"><message><payload>[...]</payload></message></receive></actions></testcase>

Asyoucanseeweusedtheexactdynamicendpointuriinbothsendandreceiveactions.Citrusisthenabletoreusethesamedynamicendpointandthesynchronousreplywillbereceivedasexpected.Howeverthereuseofexactlythesameendpointurimightgetannoyingaswealsohavetocopyendpointuriparametersandsoon.

<testcasename="DynamicEndpointTest"><actions><sendendpoint="http://localhost:8080/HelloService?user=1234567"><message><payload>[...]</payload></message></send>

<receiveendpoint="http://localhost:8080/HelloService?user=1234567"><message><payload>

CitrusReferenceGuide

449Endpointcomponent

Page 450: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

[...]</payload></message></receive></actions></testcase>

Wehavetousetheexactsameendpointuriwhenreceivingthesynchronousserviceresponse.Thisisnotverystraightforward.ThisiswhyCitrusalsosupportsdynamicendpointnames.WithaspecialendpointuriparametercalledendpointNameyoucannamethedynamicendpoint.Inacorrespondingreceiveactionyoujustusetheendpointnameasreferencewhichmakeslifemoreeasy:

<testcasename="DynamicEndpointTest"><actions><sendendpoint="http://localhost:8080/HelloService?endpointName=myHttpClient"><message><payload>[...]</payload></message></send>

<receiveendpoint="http://localhost?endpointName=myHttpClient"><message><payload>[...]</payload></message></receive></actions></testcase>

Sowecanreferencethedynamicendpointwiththegivenname.TheinternalendpointNameuriparameterisautomaticallyremovedbeforesendingoutmessages.OnceagainthedynamicendpointurimechanismprovidesafastwaytowritetestcasesinCitruswithlessconfiguration.ButyoushouldconsidertousethestaticendpointcomponentsdefinedinthebasicSpringbeanapplicationcontextforendpointsthatareheavilyreusedinmultipletestcases.

CitrusReferenceGuide

450Endpointcomponent

Page 451: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

EndpointadapterEndpointadapterhelptocustomizethebehaviorofaCitrusserversuchasHTTPorSOAPwebservers.AstheserversgetstartedwiththeCitruscontexttheyarereadytoreceiveincomingclientrequests.Nowtherearedifferentwaystoprocesstheseincomingrequestsandtoprovideaproperresponsemessage.Bydefaulttheserverwillforwardtheincomingrequesttoainmemorymessagechannelwhereatestcanreceivethemessageandprovideasynchronousresponse.Thismessagechannelhandlingisdoneautomaticallybehindthescenessothetesterdoesnotcareaboutthesethings.Thetesterjustusestheserverdirectlyasendpointreferenceinthetestcase.Thisisthedefaultbehaviour.InadditiontothatyoucandefinecustomendpointadaptersontheCitrusserverinordertochangethisdefaultbehavior.

Yousetthecustomendpointadapterdirectlyontheserverconfigurationasfollows:

<citrus-http:serverid="helloHttpServer"port="8080"auto-start="true"endpoint-adapter="emptyResponseEndpointAdapter"resource-base="src/it/resources"/>

<citrus:empty-response-adapterid="emptyResponseEndpointAdapter"/>

Nowletushaveacloserlookattheprovidedendpointadapterimplementations.

Emptyresponseendpointadapter

Thisisthesimplestendpointadapteryoucanthinkof.ItsimplyprovidesanemptysuccessresponseusingtheHTTPresponsecode200.TheadapterdoesnotneedanyconfigurationsorpropertiesasitsimplyrespondswithanemptyHTTPresponse.

<citrus:empty-response-adapterid="emptyResponseEndpointAdapter"/>

Staticresponseendpointadapter

Thenextmorecomplexendpointadapterwillalwaysreturnastaticresponsemessage.

<citrus:static-response-adapterid="endpointAdapter"><citrus:payload>

CitrusReferenceGuide

451Endpointadapter

Page 452: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<![CDATA[<HelloResponsexmlns="http://www.consol.de/schemas/samples/sayHello.xsd"><MessageId>123456789</MessageId><CorrelationId>Cx1x123456789</CorrelationId><Text>HelloUser</Text></HelloResponse>]]></citrus:payload><citrus:header><citrus:elementname="http://www.consol.de/schemas/samplesh1:Operation"value="sayHello"/><citrus:elementname="http://www.consol.de/schemas/samplesh1:MessageId"value="123456789"/></citrus:header></citrus:static-response-adapter>

Theendpointadapterisconfiguredwithastaticmessagepayloadandstaticresponseheadervalues.Theresponsetotheclientisthereforealwaysthesame.YoucanadddynamicvaluesbyusingCitrusfunctionssuchasrandomStringorrandomNumber.Alsoweareabletousevaluesoftheactualrequestmessagethathastriggeredtheresponseadapter.Therequestisavailableviathelocalmessagestore.IncombinationwithXpathorJsonPathfunctionswecanmapvaluesfromtheactualrequest.

<citrus:static-response-adapterid="endpointAdapter"><citrus:payload><![CDATA[<HelloResponsexmlns="http://www.consol.de/schemas/samples/sayHello.xsd"><MessageId>citrus:randomNumber(10)</MessageId><CorrelationId>citrus:xpath(citrus:message(request.payload()),'/hello:HelloRequest/hello:CorrelationId')</CorrelationId><Text>HelloUser</Text></HelloResponse>]]></citrus:payload><citrus:header><citrus:elementname="http://www.consol.de/schemas/samplesh1:Operation"value="sayHello"/><citrus:elementname="http://www.consol.de/schemas/samplesh1:MessageId"value="citrus:randomNumber(10)"/></citrus:header></citrus:static-response-adapter>

CitrusReferenceGuide

452Endpointadapter

Page 453: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheexampleabovemapstheCorrelationIdoftheHelloRequestmessagetotheresponsewithXpathfunction.Thelocalmessagestoreautomaticallyhasthemessagenamedrequeststoredsowecanaccessthepayloadwiththismessagename.

NoteXMLisnamespacespecificsoweneedtousethenamespaceprefixhellointheXpathexpression.ThenamespaceprefixshouldevaluatetoaglobalnamespaceentryintheglobalCitrusxpath-namespace.

Requestdispatchingendpointadapter

Theideabehindtherequestdispatchingendpointadapteristhattheincomingrequestsaredispatchedtoseveralotherendpointadapters.Thedecisionwhichendpointadaptershouldhandletheactualrequestisdonedependingonsomeadaptermapping.Themappingisdonebasedonthepayloadorheaderdataoftheincomingrequest.Amappingstrategyevaluatesamappingkeyusingtheincomingrequest.YoucanthinkofanXPathexpressionthatevaluatestothemappingkeyforinstance.Theendpointadapterthatmapstothemappingkeyisthencalledtohandletherequest.

Sotherequestdispatchingendpointadapterisabletodynamicallycallseveralotherendpointadaptersbasedontheincomingrequestmessageatruntime.Thisisverypowerful.ThenextexampleusestherequestdispatchingendpointadapterwithaXPathmappingkeyextractor.

<citrus:dispatching-endpoint-adapterid="dispatchingEndpointAdapter"mapping-key-extractor="mappingKeyExtractor"mapping-strategy="mappingStrategy"/>

<beanid="mappingStrategy"class="com.consol.citrus.endpoint.adapter.mapping.SimpleMappingStrategy"><propertyname="adapterMappings"><map><entrykey="sayHello"ref="helloEndpointAdapter"/></map></property></bean>

<beanid="mappingKeyExtractor"class="com.consol.citrus.endpoint.adapter.mapping.XPathPayloadMappingKeyExtractor"><propertyname="xpathExpression"value="//TestMessage/Operation/*"/></bean>

<citrus:static-response-adapterid="helloEndpointAdapter"><citrus:payload><![CDATA[<HelloResponse

CitrusReferenceGuide

453Endpointadapter

Page 454: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

xmlns="http://www.consol.de/schemas/samples/sayHello.xsd"><MessageId>123456789</MessageId><Text>HelloUser</Text></HelloResponse>]]></citrus:payload></citrus:static-response-adapter>

TheXPathmappingkeyextractorexpressiondecidesforeachrequestwhichmappingkeytouseinordertofindaproperendpointadapterthroughthemappingstrategy.Theendpointadaptersavailableintheapplicationcontextaremappedviatheirbeanid.Forinstanceanincomingrequestwithamatchingelement//TestMessage/Operation/sayHellowouldbehandledbytheendpointadapterbeanthatisregisteredinthemappingstrategyas"sayHello"key.TheavailableendpointadaptersareconfiguredinthesameSpringapplicationcontext.

Citrusprovidesseveraldefaultmappingkeyextractorimplementations.

HeaderMappingKeyExtractor:Readsaspecialheaderentryandusesitsvalueasmappingkey

SoapActionMappingKeyExtractor:Usesthesoapactionheaderentryasmappingkey

XPathPayloadMappingKeyExtractor:EvaluatesaXPathexpressionontherequestpayloadandusestheresultasmappingkey

Inadditiontothatweneedamappingstrategy.Citrusprovidesfollowingdefaultimplementations.

SimpleMappingStrategy:Simplekeyvaluemapwithendpointadapterreferences

BeanNameMappingStrategy:LoadstheendpointadapterSpringbeanwiththegivenidmatchingthemappingkey

ContextLoadingMappingStrategy:SameasBeanNameMappingStrategybutloadsaseparateapplicationcontextdefinedbyexternalfileresource

Channelendpointadapter

ThechannelconnectingendpointadapteristhedefaultadapterusedinallCitrusservercomponents.Indeedthisadapteralsoprovidesthemostflexibility.Thisadapterforwardsincomingrequeststoachanneldestination.Theadapteriswaitingforaproperresponse

CitrusReferenceGuide

454Endpointadapter

Page 455: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

onareplydestinationsynchronously.Withthechannelendpointcomponentsyoucanreadtherequestsonthechannelandprovideaproperresponseonthereplydestination.

<citrus:channel-endpoint-adapterid="channelEndpointAdapter"channel-name="inbound.channel"timeout="2500"/>

JMSendpointadapter

AnotherpowerfulendpointadapteristheJMSconnectingadapterimplementation.ThisadapterforwardsincomingrequeststoaJMSdestinationandwaitsforaproperresponseonareplydestination.AJMSendpointcanaccesstherequestsinternallyandprovideaproperresponseonthereplydestination.Sothisadapterisveryflexibletoprovideproperresponsemessages.

Thisspecialadaptercomeswiththecitrus-jmsmodule.SoyouhavetoaddthemoduleandthespecialXMLnamespaceforthismoduletoyourconfigurationfiles.TheMavenmoduleforcitrus-jmsgoestotheMavenPOMfileasnormalprojectdependency.Thecitrus-jmsnamespacegoestotheSpringbeanXMLconfigurationfileasfollows:

NoteCitrusprovidesa"citrus-jms"configurationnamespaceandschemadefinitionforJMSrelatedcomponentsandfeatures.IncludethisnamespaceintoyourSpringconfigurationinordertousetheCitrusJMSconfigurationelements.ThenamespaceURIandschemalocationareaddedtotheSpringconfigurationXMLfileasfollows.

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-jms="http://www.citrusframework.org/schema/jms/config"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/jms/confighttp://www.citrusframework.org/schema/jms/config/citrus-jms-config.xsd">

[...]

</beans>

AfterthatyouareabletousetheadapterimplementationintheSpringbeanconfiguration.

CitrusReferenceGuide

455Endpointadapter

Page 456: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<citrus-jms:endpoint-adapterid="jmsEndpointAdapter"destination-name="JMS.Queue.Requests.In"reply-destination-name="JMS.Queue.Response.Out"connection-factory="jmsConnectionFactory"timeout="2500"/>

<beanid="jmsConnectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory"><propertyname="brokerURL"value="tcp://localhost:61616"/></bean>

CitrusReferenceGuide

456Endpointadapter

Page 457: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

FunctionsThetestframeworkwillofferseveralfunctionsthatareusefulthroughoutthetestexecution.Thefunctionswillalwaysreturnastringvaluethatisreadyforuseasvariablevalueordirectlyinsideatextmessage.

Asetoffunctionsisusuallycombinedtoafunctionlibrary.Thelibraryhasaprefixthatwillidentifythefunctionsinsidethetestcase.Thedefaulttestframeworkfunctionlibraryusesadefaultprefix(citrus).Youcanwriteyourownfunctionlibraryusingyourownprefixinordertoextendthetestframeworkfunctionalitywheneveryouwant.

ThelibraryisbuiltintheSpringconfigurationandcontainsasetoffunctionsthatareofpublicuse.

<citrus:function-libraryid="testLibrary"prefix="foo:"><citrus:functionname="randomNumber">class="com.consol.citrus.functions.RandomNumberFunction"/><citrus:functionname="randomString">class="com.consol.citrus.functions.RandomStringFunction"/><citrus:functionname="customFunction">ref="customFunctionBean"/>...</citrus:function-library>

AsyoucanseethelibrarydefinesonetomanyfunctionseitherreferencedasnormalSpringbeanorbyitsimplementingJavaclassname.Citrusconstructsthelibraryandyouareabletousethefunctionsinyourtestcasewiththeleadinglibraryprefixjustlikethis:

foo:randomNumber()foo:randomString()foo:customFunction()

TipYoucanaddcustomfunctionimplementationsandcustomfunctionlibraries.Justuseacustomprefixforyourlibrary.ThedefaultCitrusfunctionlibraryusesthecitrus:prefix.Inthenextchaptersthedefaultfunctionsofferedbytheframeworkwillbedescribedindetail.

concat()

CitrusReferenceGuide

457Functions

Page 458: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thefunctionwillcombineseveralstringtokenstoasinglestringvalue.Thismeansthatyoucancombineastatictextvaluewithavariablevalueforinstance.Afirstexampleshouldclarifytheusage:

<testcasename="concatFunctionTest"><variables><variablename="date"value="citrus:currentDate(yyyy-MM-dd)"/><variablename="text"value="HelloTestFramework!"/></variables><actions><echo><message>citrus:concat('Todayis:',$date,'right!?')</message></echo><echo><message>citrus:concat('Textis:',$text)</message></echo></actions></testcase>

Pleasedonotforgettomarkstatictextwithsinglequotesigns.Thereisnolimitationforstringtokenstobecombined.

citrus:concat('Text1','Text2','Text3',$text,'Text5',…,'TextN')

Thefunctioncanbeusedwherevervariablescanbeused.ForinstancewhenvalidatingXMLelementsinthereceiveaction.

<message><validatepath="//element/element"value="citrus:concat('Cx1x',$generatedId)"/></message>

substring()

Thefunctionwillhavethreeparameters.

1. Stringtoworkon2. Startingindex3. Endindex(optional)

Letushavealookatasimpleexampleforthisfunction:

CitrusReferenceGuide

458Functions

Page 459: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<echo><message>citrus:substring('HelloTestFramework',6)</message></echo><echo><message>citrus:substring('HelloTestFramework',0,5)</message></echo>

Functionoutput:

TestFrameworkHello

stringLength()

Thefunctionwillcalculatethenumberofcharactersinastringrepresentationandreturnthenumber.

<echo><message>citrus:stringLength('HelloTestFramework')</message></echo>

Functionoutput:

20

translate()

Thisfunctionwillreplaceregularexpressionmatchingvaluesinsideastringrepresentationwithaspecifiedreplacementstring.

<echo><message>citrus:translate('H.lloTestFr.mework','\.','a')</message></echo>

Notethatthesecondparameterwillbearegularexpression.Thethirdparameterwillbeasimplereplacementstringvalue.

CitrusReferenceGuide

459Functions

Page 460: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Functionoutput:

HelloTestFramework

substringBefore()

Thefunctionwillsearchforthefirstoccurrenceofaspecifiedstringandwillreturnthesubstringbeforethatoccurrence.Letushaveacloserlookinasimpleexample:

<echo><message>citrus:substringBefore('Test/Framework','/')</message></echo>

Inthespecificexamplethefunctionwillsearchforthe‘/’characterandreturnthestringbeforethatindex.

Functionoutput:

Test

substringAfter()

Thefunctionwillsearchforthefirstoccurrenceofaspecifiedstringandwillreturnthesubstringafterthatoccurrence.Letusclarifythiswithasimpleexample:

<echo><message>citrus:substringAfter('Test/Framework','/')</message></echo>

SimilartothesubstringBeforefunctionthe‘/’characterisfoundinthestring.Butnowtheremainingstringisreturnedbythefunctionmeaningthesubstringafterthischaracterindex.

Functionoutput:

Framework

round()

CitrusReferenceGuide

460Functions

Page 461: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thisisasimplemathematicfunctionthatwillrounddecimalnumbersrepresentationstotheirnearestnondecimalnumber.

<echo><message>citrus:round('3.14')</message></echo>

Functionoutput:

3

floor()

Thisfunctionwillrounddowndecimalnumbervalues.

<echo><message>citrus:floor('3.14')</message></echo>

Functionoutput:

3.0

ceiling()

Similartofloorfunction,butnowthefunctionwillroundupthedecimalnumbervalues.

<echo><message>citrus:ceiling('3.14')</message></echo>

Functionoutput:

4.0

randomNumber()

Therandomnumberfunctionwillprovideyoutheopportunitytogeneraterandomnumberstringscontainingpositivenumberletters.ThereisasingularBooleanparameterforthatfunctiondescribingwhetherthegeneratednumbershouldhaveexactlytheamountofdigits.Defaultvalueforthispaddingflagwillbetrue.

CitrusReferenceGuide

461Functions

Page 462: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Nextexamplewillshowthefunctionusage:

<variables><variablename="rndNumber1"value="citrus:randomNumber(10)"/><variablename="rndNumber2"value="citrus:randomNumber(10,true)"/><variablename="rndNumber2"value="citrus:randomNumber(10,false)"/><variablename="rndNumber3"value="citrus:randomNumber(3,false)"/></variables>

Functionoutput:

89546387655003485980638765065

randomString()

Thisfunctionwillgeneratearandomstringrepresentationwithadefinedlength.Asecondparameterforthisfunctionwilldefinethecaseofthegeneratedletters(UPPERCASE,LOWERCASE,MIXED).Thelastparameterallowsalsodigitcharactersinthegeneratedstring.Bydefaultdigitcharatersarenotallowed.

<variables><variablename="rndString0"value="$citrus:randomString(10)"/><variablename="rndString1"value="citrus:randomString(10)"/><variablename="rndString2"value="citrus:randomString(10,UPPERCASE)"/><variablename="rndString3"value="citrus:randomString(10,LOWERCASE)"/><variablename="rndString4"value="citrus:randomString(10,MIXED)"/><variablename="rndString4"value="citrus:randomString(10,MIXED,true)"/></variables>

Functionoutput:

HrGHOdfAerAgSSwedetGJSDFUTTRKUdtkhirtsuzVt567JkA32

randomEnumValue()

CitrusReferenceGuide

462Functions

Page 463: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thisfunctionreturnsoneofitssuppliedarguments.Furthermoreyoucanspecifyacustomfunctionwithaconfiguredlistofvalues(theenumeration).Thefunctionwillrandomlyreturnanentrywhencalledwithoutarguments.Thispromotescodereuseandfacilitatesrefactoring.

InthenextsamplethefunctionisusedtosetahttpStatusCodevariabletooneofthegivenHTTPstatuscodes(200,401,500)

<variablename="httpStatusCode"value="citrus:randomEnumValue('200','401','500')"/>

Asmentionedbeforeyoucandefineacustomfunctionforyourveryspecificneedsinordertoeasilymanagealistofpredefinedvalueslikethis:

<citrus:function-libraryid="myCustomFunctionLibrary"prefix="custom:"><citrus-functionname="randomHttpStatusCode"ref="randomHttpStatusCodeFunction"/></citrus:function-library>

<beanid="randomHttpStatusCodeFunction"class="com.consol.citrus.functions.core.RandomEnumValueFunction"<propertyname="values"><list><value>200</value><value>500</value><value>401</value></list></property></bean>

Wehaveaddedacustomfunctionlibrarywithacustomfunctiondefinition.Thecustomfunction"randomHttpStatusCode"randomlychoosesanHTTPstatuscodeeachtimeitiscalled.Insidethetestyoucanusethefunctionlikethis:

<variablename="httpStatusCode"value="custom:randomHttpStatusCode()"/>

currentDate()

Thisfunctionwilldefinitelyhelpyouwhenaccessingthecurrentdate.Someexampleswillshowtheusageindetail:

<echo><message>citrus:currentDate()</message></echo><echo><message>citrus:currentDate('yyyy-MM-dd')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss')</message></echo>

CitrusReferenceGuide

463Functions

Page 464: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<echo><message>citrus:currentDate('yyyy-MM-dd'T'hh:mm:ss')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1y')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1M')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1d')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1h')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1m')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','+1s')</message></echo><echo><message>citrus:currentDate('yyyy-MM-ddHH:mm:ss','-1y')</message></echo>

NotethatthecurrentDatefunctionprovidestwoparameters.Firstparameterdescribesthedateformatstring.Thesecondwilldefineadateoffsetstringcontainingyear,month,days,hours,minutesorsecondsthatwillbeaddedorsubtractedtoorfromtheactualdatevalue.

Functionoutput:

01.09.20092009-09-012009-09-0112:00:002009-09-01T12:00:00

upperCase()

Thisfunctionconvertsanystringtouppercaseletters.

<echo><message>citrus:upperCase('HelloTestFramework')</message></echo>

Functionoutput:

HELLOTESTFRAMEWORK

lowerCase()

Thisfunctionconvertsanystringtolowercaseletters.

<echo><message>citrus:lowerCase('HelloTestFramework')</message></echo>

Functionoutput:

CitrusReferenceGuide

464Functions

Page 465: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

hellotestframework

average()

Thefunctionwillsumupallspecifiednumbervaluesanddividetheresultthroughthenumberofvalues.

<variablename="avg"value="citrus:average('3','4','5')"/>

avg=4.0

minimum()

Thisfunctionreturnstheminimumvalueinasetofnumbervalues.

<variablename="min"value="citrus:minimum('3','4','5')"/>

min=3.0

maximum()

Thisfunctionreturnsthemaximumvalueinasetofnumbervalues.

<variablename="max"value="citrus:maximum('3','4','5')"/>

max=5.0

sum()

Thefunctionwillsumupallnumbervalues.Thenumbervaluescanalsobenegative.

<variablename="sum"value="citrus:sum('3','4','5')"/>

sum=12.0

absolute()

Thefunctionwillreturntheabsolutenumbervalue.

CitrusReferenceGuide

465Functions

Page 466: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<variablename="abs"value="citrus:absolute('-3')"/>

abs=3.0

mapValue()

Thisfunctionimplementationmapsstringkeystostringvalues.Thisisveryhelpfulwhentheusedkeyisrandomlychosenatruntimeandthecorrespondingvalueisnotdefinedduringthedesigntime.

ThefollowingfunctionlibrarydefinesacustomfunctionformappingHTTPstatuscodestothecorrespondingmessages:

<citrus:function-libraryid="myCustomFunctionLibrary"prefix="custom:"><citrus-functionname="getHttpStatusMessage"ref="getHttpStatusMessageFunction"/></citrus:function-library>

<beanid="getHttpStatusMessageFunction"class="com.consol.citrus.functions.core.MapValueFunction"<propertyname="values"><map><entrykey="200"value="OK"/><entrykey="401"value="Unauthorized"/><entrykey="500"value="InternalServerError"/></map></property></bean>

InthisexamplethefunctionsetsthevariablehttpStatusMessagetothe'InternalServerError'stringdynamicallyatruntime.ThetestonlyknowstheHTTPstatuscodeanddoesnotcareaboutspellingandmessagelocales.

<variablename="httpStatusCodeMessage"value="custom:getHttpStatusMessage('500')"/>

randomUUID()

ThefunctionwillgeneratearandomJavaUUID.

<variablename="uuid"value="citrus:randomUUID()"/>

uuid=98fbd7b0-832e-4b85-b9d2-e0113ee88356

CitrusReferenceGuide

466Functions

Page 467: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

encodeBase64()

Thefunctionwillencodeastringtobinarydatausingbase64hexadecimalencoding.

<variablename="encoded"value="citrus:encodeBase64('HalloTestframework')"/>

encoded=VGVzdCBGcmFtZXdvcms=

decodeBase64()

Thefunctionwilldecodebinarydatatoacharactersequenceusingbase64hexadecimaldecoding.

<variablename="decoded"value="citrus:decodeBase64('VGVzdCBGcmFtZXdvcms=')"/>

decoded=HalloTestframework

escapeXml()

IfyouwanttodealwithescapedXMLinyourtestcaseyoumaywanttousethisfunction.ItautomaticallyescapesallXMLspecialcharacters.

<echo><message><![CDATA[citrus:escapeXml('<Message>HalloTestFramework</Message>')]]></message></echo>

<Message>HalloTestFramework</Message>

cdataSection()

UsuallyweuseCDATAsectionstodefinemessagepayloaddatainsideatestcase.WemightrunintoproblemswhenthepayloaditselfcontainsCDATAsectionsasnestedCDATAsectionsareprohibitedbyXMLnature.Inthiscasethenextfunctionshipsveryusefull.

CitrusReferenceGuide

467Functions

Page 468: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<variablename="cdata"value="citrus:cdataSection('payload')"/>

cdata=<![CDATA[payload]]>

digestAuthHeader()

Digestauthenticationisacommonlyusedsecurityalgorithm,especiallyinHttpcommunicationandSOAPWebServices.CitrusoffersafunctiontogenerateadigestauthenticationprincipleusedintheHttpheadersectionofamessage.

<variablename="digest"value="citrus:digestAuthHeader('username','password','authRealm','acegi','POST','http://127.0.0.1:8080','citrus','md5')"/>

Apossibledigestauthenticationheadervaluelookslikethis:

<Digestusername=foo,realm=arealm,nonce=MTMzNT,uri=http://127.0.0.1:8080,response=51f98c,opaque=b29a30,algorithm=md5>

YoucanusethesedigestheadersinmessagessentbyCitruslikethis:

<header><elementname="citrus_http_Authorization"value="vflig:digestAuthHeader('$username','$password','$authRealm','$nonceKey','POST','$uri','$opaque','$algorithm')"/></header>

ThiswillsetaHttpAuthorizationheaderwiththerespectivedigestintherequestmessage.Soyourtestisreadyforclientdigestauthentication.

localHostAddress()

Testcasesmayusethelocalhostaddressforsomereason(e.g.usedasauthenticationprinciple).Asthetestsmayrunondifferentmachinesatthesametimewecannotusestatichostaddresses.TheprovidedfunctionlocalHostAddress()readsthelocalhostnamedynamicallyatruntime.

<variablename="address"value="citrus:localHostAddress()"/>

CitrusReferenceGuide

468Functions

Page 469: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ApossiblevalueiseitherthehostnameasusedinDNSentryoranIPaddressvalue:

address=<192.168.2.100>

changeDate()

Thisfunctionworkswithdatevaluesandmanipulatesthoseatruntimebyaddingorremovingadatevalueoffset.Youcanmanipulateseveraldatefieldssuchas:year,month,day,hour,minuteorsecond.

Letusclarifythiswithasimpleexampleforthisfunction:

<echo><message>citrus:changeDate('01.01.2000','+1y+1M+1d')</message></echo><echo><message>citrus:changeDate(citrus:currentDate(),'-1M')</message></echo>

Functionoutput:

02.02.200113.04.2013

Asyoucanseethechangedatefunctionworksonstaticdatevaluesordynamicvariablevaluesorfunctionslikecitrus:currentDate().Bydefaultthechangedatefunctionrequiresadateformatsuchasthecurrentdatefunction('dd.MM.yyyy').Youcanalsodefineacustomdateformat:

<echo><message>citrus:changeDate('2000-01-10','-1M-1d','yyyy-MM-dd')</message></echo>

Functionoutput:

1999-12-09

Withthisyouareabletomanipulatealldatevaluesofstaticordynamicnatureattestruntime.

readFile()

CitrusReferenceGuide

469Functions

Page 470: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThereadFilefunctionreadsafileresourcefromgivenfilepathandloadsthecompletefilecontentasfunctionresult.Thefilepathcanbeasystemfilepathaswellasaclasspathfileresource.Thefilepathcanhavetestvariablesaspartofthepathorfilename.Inadditiontothatthefilecontentcanalsohavetestvariablevaluesandotherfunctions.

Let'sseethisfunctioninaction:

<echo><message>citrus:readFile('classpath:some/path/to/file.txt')</message></echo><echo><message>citrus:readFile($filePath)</message></echo>

Thefunctionreadsthefilecontentandplacesthecontentatthepositionwherethefunctionhasbeencalled.ThismeansthatyoucanalsousethisfunctionaspartofStringsandmessagepayloadsforinstance.Thisisaverypowerfulwaytoextractlargemessagepartstoseparatefileresources.JustaddthereadFilefunctionsomewheretothemessagecontentandCitruswillloadtheextrafilecontentandplaceitrightintothemessagepayloadforyou.

message()

WhenmessagesareexchangedinCitrusthecontentisautomaticallysavedtoaninmemorystorageforfurtheraccessinthetestcase.Thatmeansthatfunctionsandtestactionscanaccessthemessagesthathavebeensentorreceivedwithinthetestcase.Themessagefunctionloadsamessagecontentfromthatmessagestore.Themessageisidentifiedbyitsname.Receiveandsendactionsusuallydefinethemessagename.Nowwecanloadthemessagepayloadwiththatname.

Let'sseethisfunctioninaction:

<echo><message>citrus:message(myRequest.payload())</message></echo>

ThefunctionaboveloadsthemessagenamedmyRequestfromthelocalmemorystore.Thisrequiresasendorreceiveactiontohavehandledthemessagebeforeinthesametestcase.

CitrusReferenceGuide

470Functions

Page 471: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLDSL

<sendendpoint="someEndpoint"><messagename="myRequest"><payload>Somepayload</payload></message></send>

JavaDSL

send("someEndpoint").name("myRequest").payload("Somepayload");

Thenameofthemessageisimportant.Otherwisethemessagecannotbefoundinthelocalmessagestore.Note:amessagecaneitherbereceivedorsentwithanameinordertobestoredinthelocalmessagestore.Themessagefunctionisthenabletoaccessthemessagebyitsname.Inthefirstexamplethepayload()hasbeenloaded.Ofcoursewecanalsoaccessheaderinformation.

<echo><message>citrus:message(myRequest.header('Operation'))</message></echo>

ThesampleaboveloadstheheaderOperationofthemessage.

InJavaDSLthemessagestoreisalsoaccessibleovertheTestContext.

xpath()

ThexpathfunctionevaluatesaXpathexpressionsonsomeXMLsourceandreturnstheexpressionresultasString.

<echo><message><![CDATA[citrus:xpath('<message><id>1000</id></text>Sometextcontent</text></message>','/message/id')]]></echo>

TheXMLsourceisgivenasfirstfunctionparameterandcanbeloadedindifferentways.IntheexampleaboveastaticXMLsourcehasbeenused.WecouldloadtheXMLcontentfromexternalfileorjustuseatestvariable.

CitrusReferenceGuide

471Functions

Page 472: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<echo><message><![CDATA[citrus:xpath(citrus:readFile('some/path/to/file.xml'),'/message/id')]]></echo>

Alsoaccessingthelocalmessagestoreisvalidhere:

<echo><message><![CDATA[citrus:xpath(citrus:message(myRequest.payload()),'/message/id')]]></message</echo>

Thiscombinationisquitepowerfulasallpreviouslyexchangedmessagesinthetestareautomaticallystoredtothelocalmessagestore.Reusingdynamicmessagevaluesfromothermessagesbecomesveryeasythen.

jsonPath()

ThejsonPathfunctionevaluatesaJsonPathexpressionsonsomeJSONsourceandreturnstheexpressionresultasString.

<echo><message><![CDATA[citrus:jsonPath('"message":"id":1000,"text":"Sometextcontent"','$.message.id')]]></echo>

TheJSONsourceisgivenasfirstfunctionparameterandcanbeloadedindifferentways.IntheexampleaboveastaticJSONsourcehasbeenused.WecouldloadtheJSONcontentfromexternalfileorjustuseatestvariable.

<echo><message><![CDATA[citrus:jsonPath($jsonSource,'$.message.id')]]></message></echo>

Alsoaccessingthelocalmessagestoreisvalidhere:

<echo><message><![CDATA[citrus:jsonPath(citrus:message(myRequest.payload()),'$.message.id')]]></echo>

CitrusReferenceGuide

472Functions

Page 473: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thiscombinationisquitepowerfulasallpreviouslyexchangedmessagesinthetestareautomaticallystoredtothelocalmessagestore.Reusingdynamicmessagevaluesfromothermessagesbecomesveryeasythen.

CitrusReferenceGuide

473Functions

Page 474: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ValidationmatcherMessagevalidationinCitrusisessential.Theframeworkoffersseveralvalidationmechanismsfordifferentmessagetypesandformats.Withtestvariablesweareabletocheckforsimplevalueequality.Weensurethatmessageentriesareequaltopredefinedexpectedvalues.Validationmatcheraddpowerfulassertionfunctionalityontopofthat.YoujustcanusethepredefinedvalidationmatcherfunctionalitiesinordertoperformmorecomplexassertionslikecontainsorisNumberinyourvalidationstatements.

ThefollowingsectionsdescribetheCitrusdefaultvalidationmatcherimplementationsthatarereadyforusage.Thematcherimplementationsshouldcoverthebasicassertionsoncharactersequencesandnumbers.Ofcourseyoucanaddcustomvalidationmatcherimplementationsinordertomeetyourveryspecificvalidationassertions,too.

Firstofallletushavealookatavalidationmatcherstatementinactionsoweunderstandhowtousetheminatestcase.

<message><payload><RequestMessage><MessageBody><Customer><Id>@greaterThan(0)@</Id><Name>@equalsIgnoreCase('foo')@</Name></Customer></MessageBody></RequestMessage></payload></message>

Thelistingabovedescribesanormalmessagevalidationblockinsideareceivetestaction.WeusesomeinlinemessagepayloadtemplateasCDATA.AsyouknowCitruswillcomparetheactualmessagepayloadtothisexpectedtemplateinDOMtreecomparison.Inadditiontothatyoucansimplyincludevalidationmatcherstatements.ThemessageelementIdisautomaticallyvalidatedtobeanumbergreaterthanzeroandtheNamecharactersequenceissupposedtomatch'foo'ignoringcasespellingconsiderations.

CitrusReferenceGuide

474ValidationMatchers

Page 475: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Pleasenotethespecialvalidationmatchersyntax.Thestatementsaresurroundedwith'@'markersandareidentifiedbysomeuniquename.Theoptionalparameterspassedtothematcherimplementationstatetheexpectedvaluestomatch.

TipYoucanusevalidationmatcherwithallvalidationmechanisms-notonlywithXMLvalidation.Plaintext,JSON,SQLresultsetvalidationarealsosupported.

Asetofvalidationmatcherimplementationsisusuallycombinedtoavalidationmatcherlibrary.Thelibraryhasaprefixthatwillidentifythevalidationmatcherinsidethetestcase.Thedefaulttestframeworkvalidationmatcherlibraryusesadefaultprefix(citrus).Youcanwriteyourownvalidationmatcherlibraryusingyourownprefixinordertoextendthetestframeworkfunctionalitywheneveryouwant.

ThelibraryisbuiltintheSpringconfigurationandcontainsasetofvalidationmatcherthatareofpublicuse.

<citrus:validationmatcher-libraryid="testMatcherLibrary"prefix="foo:"><citrus:matchername="isNumber">class="com.consol.citrus.validation.matcher.core.IsNumberValidationMatcher"/><citrus:matchername="contains">class="com.consol.citrus.validation.matcher.core.ContainsValidationMatcher"/><citrus:matchername="customMatcher">ref="customMatcherBean"/>...</citrus:validationmatcher-library>

AsyoucanseethelibrarydefinesonetomanyvalidationmatchermemberseitherreferencedasnormalSpringbeanorbyitsimplementingJavaclassname.Citrusconstructsthelibraryandyouareabletousethevalidationmatcherinyourtestcasewiththeleadinglibraryprefixjustlikethis:

@foo:isNumber()@@foo:contains()@@foo:customMatcher()@

TipYoucanaddcustomvalidationmatcherimplementationsandcustomvalidationmatcherlibraries.Justuseacustomprefixforyourlibrary.ThedefaultCitrusvalidationmatcherlibraryusesnoprefix.SeenowthefollowingsectionsdescribingthedefaultvalidationvalidationmatcherinCitrus.

matchesXml()

CitrusReferenceGuide

475ValidationMatchers

Page 476: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheXMLvalidationmatcherimplementationisthepossiblymostexcitingone,aswecanvalidatenestedXMLwithfullvalidationpower(e.g.ignoringelements,variablesupport).ThematcherchecksanestedXMLfragmenttocompareagainstexpectedXML.ForinstancewereceivefollowingXMLmessagepayloadforvalidation:

<GetCustomerMessage><CustomerDetails><Id>5</Id><Name>Christoph</Name><Configuration><![CDATA[<config><premium>true</premium><last-login>2012-02-24T23:34:23</last-login><link>http://www.citrusframework.org/customer/5</link></config>]]></Configuration></CustomerDetails></GetCustomerMessage>

AsyoucanseethemessagepayloadcontainssomeconfigurationasnestedXMLdatainaCDATAsection.WecouldvalidatethisCDATAsectionasstaticcharactersequencecomparison,true.Butthetimestampchangesitsvaluecontinuously.ThisbreaksthestaticvalidationforCDATAelementsinXML.FortunatelythenewXMLvalidationmatcherprovidesasolutionforus:

<message><payload><GetCustomerMessage><CustomerDetails><Id>5</Id><Name>Christoph</Name><Configuration>citrus:cdataSection('@matchesXml('<config><premium>$isPremium</premium><last-login>@ignore@</last-login><link>http://www.citrusframework.org/customer/5</link></config>')@')</Configuration></CustomerDetails></GetCustomerMessage></payload></message>

WiththevalidationmatcheryouareabletovalidatethenestedXMLwithfullvalidationpower.IgnoringelementsispossibleandwecanalsousevariablesinourcontrolXML.

CitrusReferenceGuide

476ValidationMatchers

Page 477: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NoteNestedCDATAelementswithinotherCDATAsectionsarenotallowedbyXMLstandard.ThisiswhywecreatethenestedCDATAsectionontheflywiththefunctioncdataSection().###equalsIgnoreCase()

Thismatcherimplementationchecksforequalitywithoutanycasespellingconsiderations.Thematcherexpectsasingleparameterastheexpectedcharactersequencetocheckfor.

<value>@equalsIgnoreCase('foo')@</value>

contains()

Thismatchersearchesforacharactersequenceinsidetheactualvalue.Ifthecharactersequenceisnotfoundsomewherethematcherstartscomplaining.

<value>@contains('foo')@</value>

Thevalidationmatcheralsoexistinacaseinsensitivevariant.

<value>@containsIgnoreCase('foo')@</value>

startsWith()

Thematcherimplementationassertsthatthegivenvaluestartswithacharactersequenceotherwisethematcherwillarisesomeerror.

<value>@startsWith('foo')@</value>

endsWith()

Endswithmatchervalidatesavaluetoendwithagivencharactersequence.

<value>@endsWith('foo')@</value>

matches()

Youcancheckavaluetomeetaregularexpressionwiththisvalidationmatcher.Thisisforinstanceveryusefulforemailaddressvalidation.

CitrusReferenceGuide

477ValidationMatchers

Page 478: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<value>@matches('[a-z0-9]')@</value>

matchesDatePattern()

Datevaluesarealwaysdifficulttocheckforequality.Especiallywhenyouhavemillisecondtimestampstodealwith.Thereforethedatepatternvalidationmatchershouldhavesomeimprovementforyou.Yousimplyvalidatethedateformatpatterninsteadofcheckingfortotalequality.

<value>@matchesDatePattern('yyyy-MM-dd')@</value>

Theexamplelistingusesadateformatpatternthatisexpected.Theactualdatevalueisparsedaccordingtothispatternandmaycauseerrorsincasethevalueisnovaliddatematchingthedesiredformat.

isNumber()

Checkingonvaluestobeofnumericnatureisessential.Theactualvaluemustbeanumericnumberotherwisethematcherraiseserrors.Thematcherimplementationdoesnotevaluateanyparameters.

<value>@isNumber()@</value>

lowerThan()

Thismatcherchecksanumbertobelowerthanagiventhresholdvalue.

<value>@lowerThan(5)@</value>

greaterThan()

Thematcherimplementationwillcheckonnumericvaluestobegreaterthanaminimumvalue.

<value>@greaterThan(5)@</value>

isWeekday()

CitrusReferenceGuide

478ValidationMatchers

Page 479: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thematcherworksondatevaluesandchecksthatagivendateevaluatestotheexpecteddayoftheweek.Theuserdefinestheexpecteddaybyitsnameinuppercasecharacters.Thematcherfailsincasethegivendateisanotherweekdaythanexpected.

<someDate>@isWeekday('MONDAY')@</someDate>

Possiblevaluesfortheexpecteddayoftheweekare:MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAYorSUNDAY.

Thefieldvaluehastobeadatevalueotherwisethematcherwillfailtoparsethedate.Thematcherrequiresadateformatwhichisdd.MM.yyyybydefault.Youcanchangethisdateformatasfollows:

<someDate>@isWeekday(MONDAY('yyyy-MM-dd'))@</someDate>

Nowthematcherusesthecustomdateformatinordertoparsethedatevalueforevaluation.Thevalidationmatcheralsoworkswithdatetimevalues.Inthiscaseyouhavetogiveavaliddatetimeformatrespectively(e.g.FRIDAY('yyyy-MM-dd'T'hh:mm:ss')).

variable()

Thisisaveryspecialvalidationmatcher.Insteadofperformingavalidationlogicyoucansavetheactualvaluepassedtothevalidationmatcherasnewtestvariable.Thiscomesveryhandyasyoucanusethematcherwhereveryouwant:JSONmessagepayloads,XMLmessagepayloads,headersandsoon.

<value>@variable('foo')@</value>

Thevalidationmatchercreatesanewvariablefoowiththeactualelementvalueasvariablevalue.Whenleavingoutthecontrolvaluethefieldnameitselfisusedasvariablename.

<date>@variable()@</date>

Thiscreatesanewvariabledatewiththeactualelementvalueasvariablevalue.

dateRange()

CitrusReferenceGuide

479ValidationMatchers

Page 480: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thematcherworksondatevaluesandchecksthatagivendateiswithintheexpecteddaterange.Theuserdefinestheexpecteddaterangebyspecifyingafrom-date,ato-dateandoptionallyadateformat.Thematcherfailswhenthegivendateliesoutsidetheexpecteddaterange.

<someDate>@dateRange('01-12-2015','31-12-2015','dd-MM-yyyy')@</someDate>

Possiblevalidvalueswouldbe'somedate'>='01-12-2015'and'somedate'<='31-12-2015'

Thedate-formatisoptionalandwhenomitteditisassumedthatalldatesmatchthedefaultdateformatyyyy-MM-dd.Whenspecifyingacustomdateformatusejava'sdateformatasareferenceforvaliddateformats.Onlydateswereusedintheexampleabovebutwecouldjustaseasilyuseddateandtimeasshownintheexamplebelow

<someDate>@dateRange('2015.12.0107:00:00','2015.12.0119:00:00','yyyy.MM.ddHH:mm:ss')@</someDate

assertThat()

Hamcrestisaverypowerfulmatcherlibrarywithextraordinarymatcherimplementations.YoucanuseHamcrestmatchersalsoasCitrusvalidationmatcher.

<someValue>@assertThat(equalTo(foo))@</someValue>

InthelistingaboveweareusingtheequalTo()matcher.AllHamcrestmatchersaresurroundedbyaassertThatexpression.YouareabletocombineseveralHamcrestmatcherstheninordertoconstructverypowerfulvalidationlogic.Seethefollowingexamplesonwhatispossiblethen:

<someValue>@assertThat(equalTo(value))@</someValue><someValue>@assertThat(not(equalTo(other))@</someValue><someValue>@assertThat(is(not(other))@</someValue><someValue>@assertThat(not(is(other))@</someValue><someValue>@assertThat(equalToIgnoringCase(VALUE)@</someValue><someValue>@assertThat(containsString(lue)@</someValue><someValue>@assertThat(not(containsString(other))@</someValue><someValue>@assertThat(startsWith(val)@</someValue><someValue>@assertThat(endsWith(lue)@</someValue><someValue>@assertThat(anyOf(startsWith(val),endsWith(lue))@</someValue><someValue>@assertThat(allOf(startsWith(val),endsWith(lue))@</someValue>

CitrusReferenceGuide

480ValidationMatchers

Page 481: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<someValue>@assertThat(isEmptyString()@</someValue><someValue>@assertThat(not(isEmptyString())@</someValue><someValue>@assertThat(isEmptyOrNullString()@</someValue><someValue>@assertThat(nullValue()@</someValue><someValue>@assertThat(notNullValue()@</someValue><someValue>@assertThat(empty()@</someValue><someValue>@assertThat(not(empty())@</someValue><someValue>@assertThat(greaterThan(4)@</someValue><someValue>@assertThat(allOf(greaterThan(4),lessThan(6),not(lessThan(5)))@</someValue><someValue>@assertThat(is(not(greaterThan(5)))@</someValue><someValue>@assertThat(greaterThanOrEqualTo(5)@</someValue><someValue>@assertThat(lessThan(5)@</someValue><someValue>@assertThat(not(lessThan(1))@</someValue><someValue>@assertThat(lessThanOrEqualTo(4)@</someValue><someValue>@assertThat(hasSize(5))@</someValue>

Citruswillautomaticallyperformvalidationmatchersontheelementvalue.Onlyifallmatchersaresatisfiedthevalidationwillpass.

CitrusReferenceGuide

481ValidationMatchers

Page 482: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DatadictionariesDatadictionariesinCitrusprovideanewwaytomanipulatemessagepayloaddatabeforeamessageissentorreceived.Thedictionarydefinesasetofkeysandrespectivevalues.Justlikeeveryotherdictionaryitisusedtotranslatethings.Inourcasewetranslatemessagedataelements.

Youcantranslatecommonmessageelementsthatareusedwidelythroughoutyourdomainmodel.AsCitrusdealswithdifferenttypesofmessagedata(e.g.XML,JSON)wehavedifferentdictionaryimplementationsthataredescribedinthenextsections.

XMLdatadictionaries

XMLdatadictionariesdoapplytoXMLmessageformatpayloads,ofcourse.IngeneralweaddadictionarytothebasicCitrusSpringapplicationcontextinordertomakethedictionaryvisibletoalltestcases:

<citrus:xml-data-dictionaryid="nodeMappingDataDictionary"><citrus:mappings><citrus:mappingpath="TestMessage.MessageId"value="$messageId"/><citrus:mappingpath="TestMessage.CorrelationId"value="$correlationId"/><citrus:mappingpath="TestMessage.User"value="Christoph"/><citrus:mappingpath="TestMessage.TimeStamp"value="citrus:currentDate()"/></citrus:mappings></citrus:xml-data-dictionary>

AsyoucanseethedictionaryisnothingbutanormalSpringbeandefinition.TheNodeMappingDataDictionaryimplementationreceivesamapofkeyvaluepairswherethekeyisamessageelementpathexpression.ForXMLpayloadsthemessageelementtreeistraversedsothepathexpressionisbuiltforanexactmessageelementinsidethepayload.Ifmatchedtherespectivevalueissetaccordinglythroughthedictionary.

Besidesdefiningthedictionarykeyvaluemappingsaspropertymapinsidethebeandefinitionwecanextractthemappingdatatoanexternalfile.

<citrus:xml-data-dictionaryid="nodeMappingDataDictionary"><citrus:mapping-filepath="classpath:com/consol/citrus/sample.dictionary"/></citrus:xml-data-dictionary>

ThemappingfilecontentjustlookslikeanormalpropertyfileinJava:

CitrusReferenceGuide

482Datadictionary

Page 483: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestMessage.MessageId=$messageIdTestMessage.CorrelationId=$correlationIdTestMessage.User=ChristophTestMessage.TimeStamp=citrus:currentDate()

YoucansetanymessageelementvalueinsidetheXMLmessagepayload.ThepathexpressionalsosupportsXMLattributes.Justusetheattributenameaslastpartofthepathexpression.LetushaveacloserlookatasampleXMLmessagepayloadwithattributes:

<TestMessage><Username="Christoph"age="18"/></TestMessage>

WiththissampleXMLpayloadgivenwecanaccesstheattributesinthedatadictionaryasfollows:

<citrus:mappingpath="TestMessage.User.name"value="$userName"/><citrus:mappingpath="TestMessage.User.age"value="$userAge"/>

TheNodeMappingDataDictionaryimplementationiseasytouseandfitsthebasicneedsforXMLdatadictionaries.Themessageelementpathexpressionsareverysimpleanddofitbasicneeds.HoweverwhenmorecomplexXMLpayloadsapplyfortranslationwemightreachtheboundarieshere.

FormorecomplexXMLmessagepayloadsXPathdatadictionariesareveryeffective:

<citrus:xpath-data-dictionaryid="xpathMappingDataDictionary"><citrus:mappings><citrus:mappingpath="//TestMessage/MessageId"value="$messageId"/><citrus:mappingpath="//TestMessage/CorrelationId"value="$correlationId"/><citrus:mappingpath="//TestMessage/User"value="Christoph"/><citrus:mappingpath="//TestMessage/User/@id"value="123"/><citrus:mappingpath="//TestMessage/TimeStamp"value="citrus:currentDate()"/></citrus:mappings></citrus:xpath-data-dictionary>

AsexpectedXPathmappingexpressionsarewaymorepowerfulandcanalsohandleverycomplexscenarioswithXMLnamespaces,attributesandnodelists.JustlikethenodemappingdictionarytheXPathmappingdictionarydoesalsosupportvariables,functionsandanexternalmappingfile.

CitrusReferenceGuide

483Datadictionary

Page 484: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XPathworksfinewithnamespaces.IngeneralitisgoodpracticetodefineanamespacecontextwhereyoumapnamespaceURIvalueswithprefixvalues.SoyourXPathexpressionisalwaysexactandevaluationisstrict.InCitrustheNamespaceContextBuilderwhichisalsoaddedasnormalSpringbeantotheapplicationcontextmanagesnamespacesusedinyourXPathexpressions.SeeourXMLandXPAthchaptersinthisdocumentationfordetaileddescriptionhowtoaccomplishfailsafeXPathexpressionswithnamespaces.

ThiscompletestheXMLdatadictionaryusageinCitrus.Lateronwewillseesomemoreadvanceddatadictionaryscenarioswherewewilldiscusstheusageofdictionaryscopesandmappingstrategies.ButbeforethatletushavealookatothermessageformatslikeJSONmessages.

JSONdatadictionaries

JSONdatadictionariescomplementwithXMLdatadictionaries.AsusualwehavetoaddtheJSONdatadictionarytothebasicSpringapplicationcontextfirst.OncethisisdonethedatadictionaryautomaticallyappliesforallJSONmessagepayloadsinCitrus.ThismeansthatallJSONmessagessentandreceivedgettranslatedwiththeJSONdatadictionaryimplementation.

Citrususesmessagetypesinordertoevaluatewhichdatadictionarymayfittothemessagethatiscurrentlyprocessed.Asusualyoucandefinethemessagetypedirectlyinyourtestcaseasattributeinsidethesendingandreceivingmessageaction.

LetusseeasimpledictionaryforJSONdata:

<citrus:json-data-dictionaryid="jsonMappingDataDictionary"><citrus:mappings><citrus:mappingpath="TestMessage.MessageId"value="$messageId"/><citrus:mappingpath="TestMessage.CorrelationId"value="$correlationId"/><citrus:mappingpath="TestMessage.User"value="Christoph"/><citrus:mappingpath="TestMessage.TimeStamp"value="citrus:currentDate()"/></citrus:mappings></citrus:json-data-dictionary>

ThemessagepathexpressionsdolookverysimilartothoseusedinXMLdatadictionaries.HerethepathexpressionkeysdoapplytotheJSONobjectgraph.SeethefollowingsampleJSONdatawhichperfectlyappliestothedictionaryexpressionsabove.

"TestMessage":"MessageId":"1122334455",

CitrusReferenceGuide

484Datadictionary

Page 485: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

"CorrelationId":"100000001","User":"Christoph","TimeStamp":1234567890

ThepathexpressionswillmatchaveryspecificmessageelementinsidetheJSONobjectgraph.Thedictionarywillautomaticallysetthemessageelementvaluesthen.ThepathexpressionsareeasytouseasyoucantraversetheJSONobjectgraphveryeasy.

Ofcoursethedatadictionarydoesalsosupporttestvariables,functions.AlsoveryinterestingistheusageofJSONarrays.AJSONarrayelementisreferencedinadatadictionarylikethis:

<citrus:mappingpath="TestMessage.Users[0]"value="Christoph"/><citrus:mappingpath="TestMessage.Users[1]"value="Julia"/>

TheUserselementisaJSONarray,sowecanaccesstheelementswithindex.NestingJSONobjectsandarraysisalsosupportedsoyoucanalsohandlemorecomplexJSONdata.

TheJsonMappingDataDictionaryimplementationiseasytouseandfitsthebasicneedsforJSONdatadictionaries.Themessageelementpathexpressionsareverysimpleanddofitbasicneeds.HoweverwhenmorecomplexJSONpayloadsapplyfortranslationwemightreachtheboundarieshere.

FormorecomplexJSONmessagepayloadsJsonPathdatadictionariesareveryeffective:

<citrus:json-path-data-dictionaryid="jsonMappingDataDictionary"><citrus:mappings><citrus:mappingpath="$.TestMessage.MessageId"value="$messageId"/><citrus:mappingpath="$..CorrelationId"value="$correlationId"/><citrus:mappingpath="$..Users[0]"value="Christoph"/><citrus:mappingpath="$.TestMessage.TimeStamp"value="citrus:currentDate()"/></citrus:mappings></citrus:json-path-data-dictionary>

JsonPathmappingexpressionsarewaymorepowerfulandcanalsohandleverycomplexscenarios.YoucanapplyforallelementsnamedCorrelationIdinonesingleentryforinstance.

Dictionaryscopes

CitrusReferenceGuide

485Datadictionary

Page 486: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NowthatwehavelearnedhowtoadddatadictionariestoCitrusweneedtodiscusssomeadvancedtopics.Datadictionaryscopesdodefinetheboundarieswherethedictionarymayapply.Bydefaultdatadictionariesareglobalscopedictionaries.ThismeansthatthedatadictionaryappliestoallmessagessentandreceivedwithCitrus.OfcoursemessagetypesareconsideredsoXMLdatadictionariesdoonlyapplytoXMLmessagetypes.Howeverglobalscopedictionarieswillbeactivatedthroughoutalltestcasesandactions.

Youcanoverwritethedictionaryscope.Forinstanceinordertouseanexplicitscope.Whenthisisdonethedictionarywilnotapplyautomaticallybuttheuserhastoexplicitlysetthedatadictionaryinsendingorreceivingtestaction.Thiswayyoucanactivatethedictionarytoaveryspecialsetoftestactions.

<citrus:xml-data-dictionaryid="specialDataDictionary"global-scope="false"><citrus:mapping-filepath="classpath:com/consol/citrus/sample.dictionary"/></citrus:xml-data-dictionary>

Wesettheglobalscopepropertytofalsesothedictionaryishandledinexplicitscope.Thismeansthatyouhavetosetthedatadictionaryexplicitlyinyourtestactions:

XMLDSL

<sendendpoint="myEndpoint"><messagedata-dictionary="specialDataDictionary"><payload><TestMessage>HelloCitrus"/TestMessage></payload></message></send>

JavaDSLdesignerandrunner

@CitrusTestpublicvoiddictionaryTest()send(myEndpoint).payload("<TestMessage>HelloCitrus"/TestMessage>").dictionary("specialDataDictionary");

Thesampleaboveisasendingtestactionwithanexplicitdatadictionaryreferenceset.Beforesendingthemessagethedictionaryisaskedfortranslation.Soallmatchingmessageelementvalueswillbesetbythedictionaryaccordingly.Otherglobaldata

CitrusReferenceGuide

486Datadictionary

Page 487: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

dictionariesdoalsoapplyforthismessagebuttheexplicitdictionarywillalwaysoverwritethemessageelementvalues.

Pathmappingstrategies

Anotheradvancedtopicaboutdatadictionariesisthepathmappingstrategy.WhenusingsimplepathexpressionsthedefaultstrategyisalwaysEXACT.Thismeansthatthepathexpressionhastoevaluateexactlytoamessageelementwithinthepayloaddata.Andonlythisexactmessageelementistranslated.

Youcansetyourownpathmappingstrategyinordertochangethisbehavior.ForinstanceanothermappingstrategywouldbeSTARS_WITH.Allelementsaretranslatedthatstartwithacertainpathexpression.Letusclarifythiswithanexample:

<citrus:xml-data-dictionaryid="nodeMappingDataDictionary"mapping-strategy="STARTS_WITH"><citrus:mappings><citrus:mappingpath="TestMessage.Property"value="citrus:randomString()"/></citrus:mappings></citrus:xml-data-dictionary>

NowwiththepathmappingstrategysettoSTARS_WITHallmessageelementpathexpressionsstartingwithTestMessage.Propertywillfindtranslationinthisdictionary.Followingsamplemessagepayloadwouldbetranslatedaccordingly:

<TestMessage><Property>XXX</Property><PropertyName>XXX</PropertyName><PropertyValue>XXX</PropertyValue></TestMessage>

AllchildelementsofTestMessagestartingwithPropertywillbetranslatedwiththisdatadictionary.IntheresultingmessagepayloadCitruswillusearandomstringasvaluefortheseelementsasweusedthecitrus:randomString()functioninthedictionarymapping.

ThenextmappingstrategywouldbeENDS_WITH.Nosurpriseshere-thismappingstrategylooksformessageelementsthatendwithacertainpathexpression.Againasimpleexamplewillclarifythisforyou.

<citrus:xml-data-dictionaryid="nodeMappingDataDictionary"mapping-strategy="ENDS_WITH"><citrus:mappings><citrus:mappingpath="Id"value="citrus:randomNumber()"/>

CitrusReferenceGuide

487Datadictionary

Page 488: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

</citrus:mappings></citrus:xml-data-dictionary>

Againletusseesomesamplemessagepayloadforthisdictionaryusage:

<TestMessage><RequestId>XXX</RequestId><Properties><Property><PropertyId>XXX</PropertyId><PropertyValue>XXX</PropertyValue></Property><Property><PropertyId>XXX</PropertyId><PropertyValue>XXX</PropertyValue></Property></Properties></TestMessage>

InthissampleallmessageelementsendingwithIdwouldbetranslatedwitharandomnumber.Nomatterwhereinthemessagetreetheelementsarelocated.Thisisquiteusefulbutalsoverypowerful.Sobecarefultousethisstrategyinglobaldatadictionariesasitmaytranslatemessageelementsthatyouwouldnotexpectinthefirstplace.

CitrusReferenceGuide

488Datadictionary

Page 489: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestactorsTheconceptoftestactorscametoourmindwhenreusingCitrustestcasesinend-to-endtestscenarios.UsuallyCitrussimulatesallinterfacepartnerswithinatestcasewhichisgreatforcontinuousintegrationtesting.Inend-to-endintegrationtestscenariossomeofourinterfacepartnersmayberealandalive.SomeotherinterfacepartnersstillrequireCitrussimulationlogic.

ItwouldbegreatifwecouldreusetheCitrusintegrationtestsinthistestsetupaswehavethecompletetestflowofmessagesavailableintheCitrustests.Weonlyhavetoremovethesimulatedsend/receiveactionsforthoserealinterfacepartnerapplicationswhichareavailableinourend-to-endtestsetup.

Withtestactorswehavetheopportunitytolinktestactions,inparticularsend/receivemessageactions,toatestactor.Thetestactorcanbedisabledinconfigurationveryeasyandfollowingfromthatalllinkedsend/receiveactionsaredisabled,too.OneCitrustestcaseisrunnablewithdifferenttestsetupscenarioswheredifferentpartnerapplicationsontheonehandareavailableasreallifeapplicationsandontheotherhandmyrequiresimulation.

Definetestactors

FirstthingtodoistodefineoneormoretestactorsinCitrusconfiguration.Atestactorrepresentsaparticipatingparty(e.g.interfacepartner,backendapplication).WewritethetestactorsintothecentralSpringapplicationcontext.WecanuseaspecialCitrusSpringXMLschemasodefinitionsarequiteeasy:

<citrus:actorid="travelagency"name="TRAVEL_AGENCY"/><citrus:actorid="royalairline"name="ROYAL_AIRLINE"/><citrus:actorid="smartariline"name="SMART_AIRLINE"/>

Thelistingabovedefinesthreetestactorsparticipatinginourtestscenario.AtravelagencyapplicationwhichissimulatedbyCitrusasacallingclient,thesmartairlineapplicationandaroyalairlineapplication.Nowwehavethetestactorsdefinedwecanlinkthosetomessagesender/receiverinstancesand/ortestactionswithinourtestcase.

Linktestactors

CitrusReferenceGuide

489Testactors

Page 490: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Weneedtolinkthetestactorstomessagesendandreceiveactionsinourtestcases.Wecandothisintwodifferentways.Firstwecansetatestactorreferenceonamessagesenderandmessagereceiver.

<citrus-jms:sync-endpointid="royalAirlineBookingEndpoint"destination-name="$royal.airline.request.queue"actor="royalairline"/>

Nowalltestactionsthatareusingthesemessagereceiverandmessagesenderinstancesarelinkedtothetestactor.Inadditiontothatyoucanalsoexplicitlylinktestactionstotestactorsinatest.

<receiveendpoint="royalAirlineBookingEndpoint"actor="royalairline"><message>[...]</message></receive>

<sendendpoint="royalAirlineBookingEndpoint"actor="royalairline"><message>[...]</message></send>

Thisexplicitlylinkstestactorstotestactionssoyoucandecidewhichlinkshouldbesetwithouthavingtorelyonthemessagereceiverandsenderconfiguration.

Disabletestactors

Usuallybothairlineapplicationsaresimulatedinourintegrationtests.Butthistimewewanttochangethisbyintroducingaroyalairlineapplicationwhichisonlineasarealapplicationinstance.SoweneedtoskipallsimulatedmessageinteractionsfortheroyalairlineapplicationinourCitrustests.Thisiseasyaswehavelinkedallsend/receiveactionstooneofourtestactors.Sowencandisabletheroyalairlinetestactorinourconfiguration:

<citrus:actorid="royalairline"name="ROYAL_AIRLINE"disabled="true"/>

Anytestactionlinkedtothistestactorisnowskipped.Asweintroducedarealroyalairlineapplicationinourtestscenariotherequestsgetansweredandthetestshouldbesuccessfulwithinthisend-to-endtestscenario.Thetravelagencyandthesmartairline

CitrusReferenceGuide

490Testactors

Page 491: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

stillgetsimulatedbyCitrus.ThisisaperfectwayofreusingintegrationtestsindifferenttestscenarioswhereyouenableanddisablesimulatedparticipatingpartiesinCitrus.

ImportantServerportsmaybeofspecialinterestwhendealingwithdifferenttestscenarios.YoumayhavetoalsodisableaCitrusembeddedJettyserverinstanceinordertoavoidportbindingconflictsandyoumayhavetowireendpointURIsaccordinglybeforeexecutingatest.ThereallifeapplicationmaynotusethesameportandipastheCitrusembeddedserversforsimulation.

CitrusReferenceGuide

491Testactors

Page 492: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TestsuiteactionsAtestframeworkshouldalsoprovidethefunctionalitytodosomeworkbeforeandafterthetestrun.Youcouldthinkofpreparing/deletingthedatainadatabaseorstarting/stoppingaserverinthissectionbefore/afteratestrun.ThesetasksfitbestintotheinitializationandcleanupphasesofCitrus.

NoteItisimportanttonoticethattheCitrusconfigurationcomponentsthatwearegoingtouseinthenextsectionbelongtoaseparateXMLnamespacecitrus-test.WehavetoaddthenamespacedeclarationtotheXMLrootelementofourXMLconfigurationfileaccordingly.

<spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:citrus-test="http://www.citrusframework.org/schema/testcase"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">

[...]

</beans>

Beforesuite

Youcaninfluencethebehaviorofatestrunintheinitializationphaseactuallybeforethetestsareexecuted.Seethenextcodeexampletofindouthowitworkswithactionsthattakeplacebeforethefirsttestisexecuted:

XMLConfig

<citrus:before-suiteid="actionsBeforeSuite"><citrus:actions><!--listofactionsbeforesuite--></citrus:actions></citrus:before-suite>

CitrusReferenceGuide

492Testsuite

Page 493: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheCitrusconfigurationcomponentholdsalistofCitrustestactionsthatgetexecutedbeforethetestsuiterun.YoucanaddallCitrustestactionshereasyouwoulddoinanormaltestcasedefinition.

XMLConfig

<citrus:before-suiteid="actionsBeforeSuite"><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>CREATETABLEPERSON(IDinteger,NAMEchar(250))</citrus-test:statement</citrus-test:sql></citrus:actions></citrus:before-suite>

NotethatwemustusetheCitrustestcasenamespaceforthenestedtestactiondefinitions.WeaccessthedatabaseandcreateatablePERSONwhichisobviouslyneededinourtestcases.Youcanthinkofseveralactionsheretopreparethedatabaseforinstance.

TipCitrusoffersspecialstartupandshutdownactionsthatmaystartandstopserverimplementationsautomatically.ThismightbehelpfulwhendealingwithHttpserversorWebServicecontainerslikeJetty.Youcanalsothinkofstarting/stoppingaJMSbrokerbeforeatestrun.

SofarwehaveusedXMLDSLactionsinbeforesuiteconfiguration.NowifyouexclusivelywanttouseJavaDSLyoucandothesamewithaddingacustomclassthatextendsTestDesignerBeforeSuiteSupportorTestRunnerBeforeSuiteSupport.

JavaDSLdesigner

publicclassMyBeforeSuiteextendsTestDesignerBeforeSuiteSupport@OverridepublicvoidbeforeSuite(TestDesignerdesigner)designer.echo("Thisactionshouldbeexecutedbeforesuite");

ThecustomimplementationextendsTestDesignerBeforeSuiteSupportandthereforehastoimplementthemethodbeforeSuite.ThismethodaddsomeJavaDSLdesignerlogictothebeforesuite.Thedesignerinstanceisinjectedasmethodargument.Youcan

CitrusReferenceGuide

493Testsuite

Page 494: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

useallJavaDSLmethodstothisdesignerinstance.Citruswillautomaticallyfindandexecutethebeforesuitelogic.WeonlyneedtoaddthisclasstotheSpringbeanapplicationcontext.Youcandothisexplicitly:

<beanid="myBeforeSuite"class="my.company.citrus.MyBeforeSuite"/>

OfcourseyoucanalsouseotherSpringbeanmechanismssuchascomponent-scansheretoo.TherespectivetestrunnerimplementationextendstheTestRunnerBeforeSuiteSupportandgetsatestrunnerinstanceasmethodargumentinjected.

JavaDSLrunner

publicclassMyBeforeSuiteextendsTestRunnerBeforeSuiteSupport@OverridepublicvoidbeforeSuite(TestRunnerrunner)runner.echo("Thisactionshouldbeexecutedbeforesuite");

Youcanhavemanybefore-suiteconfigurationcomponentswithdifferentidsinaCitrusproject.Bydefaultthecontainersarealwaysexecuted.Butyoucanrestricttheaftersuiteactioncontainerexecutionbydefiningasuitename,testgroupnames,environmentorsystempropertiesthatshouldmatchaccordingly:

XMLConfig

<citrus:before-suiteid="actionsBeforeSuite"suites="databaseSuite"groups="e2e"><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>CREATETABLEPERSON(IDinteger,NAMEchar(250))</citrus-test:statement</citrus-test:sql></citrus:actions></citrus:before-suite>

TheabovebeforesuitecontainerisonlyexecutedwiththetestsuitecalleddatabaseSuiteorwhenthetestgroupe2eisdefined.TestgroupsandsuitenamesareonlysupportedwhenusingtheTestNGunittestframework.UnfortunatelyJUnitdoesnotallowtohookintosuiteexecutionaseasilyasTestNGdoes.Thisiswhyaftersuite

CitrusReferenceGuide

494Testsuite

Page 495: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

actioncontainersarenotrestrictedinexecutionwhenusingCitruswiththeJUnittestframework.Youcandefinemultiplesuitenamesandtestgroupswithcommadelimitedstringsasattributevalues.

WhenusingtheJavaDSLbeforesuitesupportyoucansetsuitenamesandtestgroupfiltersbysimplycallingtherespectivesettermethodsinyourcustomimplementation.

<beanid="myBeforeSuite"class="my.company.citrus.MyBeforeSuite"><propertyname="suiteNames"><list><value>databaseSuite</value></list></property><propertyname="testGroups"><list><value>e2e</value></list></property></bean>

Environmentorsystempropertiesaredefinedaslistofkey-valuepairs.Whenspecifiedthepropertieshavetobepresentwithrespectivevalue.Incasethepropertyvalueisleftoutinconfigurationthepropertymustsimplyexistsonthesysteminordertoenablethebeforesuitesequenceinthattestrun.

XMLConfig

<citrus:before-suiteid="actionsBeforeSuite"suites="databaseSuite"groups="e2e"><citrus:env><citrus:propertyname="USER"/></citrus:env><citrus:system><citrus:propertyname="test-stage"value="e2e"/></citrus:system><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>CREATETABLEPERSON(IDinteger,NAMEchar(250))</citrus-test:statement</citrus-test:sql></citrus:actions></citrus:before-suite>

IntheexampleabovethesuitesequencewillonlyapplyonenvironmentswithUSERpropertysetandthesystempropertytest-stagemustbesettoe2e.Otherwisethesequenceexecutionisskipped.

CitrusReferenceGuide

495Testsuite

Page 496: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Aftersuite

Atestrunmayrequirethetestenvironmenttobeclean.ThereforeitisagoodideatopurgeallJMSdestinationsorcleanupthedatabaseafterthetestruninordertoavoiderrorsinfollow-uptestruns.Justlikewepreparedsomedatainactionsbeforesuitewecancleanupthetestruninactionsafterthetestsarefinished.TheSpringbeansyntaxhereisnotsignificantlydifferenttothoseinbeforesuitesection:

XMLConfig

<citrus:after-suiteid="actionsAfterSuite"><citrus:actions><!--listofactionsaftersuite--></citrus:actions></citrus:after-suite>

Againwegivetheaftersuiteconfigurationcomponentauniqueidwithintheconfigurationandputonetomanytestactionsasnestedconfigurationelementstothelistofactionsexecutedafterthetestsuiterun.

XMLConfig

<citrus:after-suiteid="actionsAfterSuite"><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>DELETEFROMTABLEPERSON</citrus-test:statement></citrus-test:sql></citrus:actions></citrus:after-suite>

WehavetousetheCitrustestcaseXMLnamespacewhendefiningnestedtestactionsinaftersuitelist.Wejustremovealldatafromthedatabasesowedonotinfluencefollow-uptests.Quitesimpleisn'tit!?

OfcoursewecanalsodefineJavaDSLaftersuiteactions.YoucandothisbyaddingacustomclassthatextendsTestDesignerAfterSuiteSupportorTestRunnerAfterSuiteSupport.

JavaDSLdesigner

publicclassMyAfterSuiteextendsTestDesignerAfterSuiteSupport@OverridepublicvoidafterSuite(TestDesignerdesigner)designer.echo("Thisactionshouldbeexecutedaftersuite");

CitrusReferenceGuide

496Testsuite

Page 497: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThecustomimplementationextendsTestDesignerAfterSuiteSupportandthereforehastoimplementthemethodafterSuite.ThismethodaddsomeJavaDSLdesignerlogictotheaftersuite.Thedesignerinstanceisinjectedasmethodargument.YoucanuseallJavaDSLmethodstothisdesignerinstance.Citruswillautomaticallyfindandexecutetheaftersuitelogic.WeonlyneedtoaddthisclasstotheSpringbeanapplicationcontext.Youcandothisexplicitly:

<beanid="myAfterSuite"class="my.company.citrus.MyAfterSuite"/>

OfcourseyoucanalsouseotherSpringbeanmechanismssuchascomponent-scansheretoo.TherespectivetestrunnerimplementationextendstheTestRunnerAfterSuiteSupportandgetsatestrunnerinstanceasmethodargumentinjected.

JavaDSLrunner

publicclassMyAfterSuiteextendsTestRunnerAfterSuiteSupport@OverridepublicvoidafterSuite(TestRunnerrunner)runner.echo("Thisactionshouldbeexecutedaftersuite");

Youcanhavemanyafter-suiteconfigurationcomponentswithdifferentidsinaCitrusproject.Bydefaultthecontainersarealwaysexecuted.Butyoucanrestricttheaftersuiteactioncontainerexecutionbydefiningasuitename,testgroupnames,environmentorsystempropertiesthatshouldmatchaccordingly:

XMLConfig

<citrus:after-suiteid="actionsAfterSuite"suites="databaseSuite"groups="e2e"><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>DELETEFROMTABLEPERSON</citrus-test:statement></citrus-test:sql></citrus:actions></citrus:after-suite>

CitrusReferenceGuide

497Testsuite

Page 498: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheaboveaftersuitecontainerisonlyexecutedwiththetestsuitecalleddatabaseSuiteorwhenthetestgroupe2eisdefined.TestgroupsandsuitenamesareonlysupportedwhenusingtheTestNGunittestframework.UnfortunatelyJUnitdoesnotallowtohookintosuiteexecutionaseasilyasTestNGdoes.ThisiswhyaftersuiteactioncontainersarenotrestrictedinexecutionwhenusingCitruswiththeJUnittestframework.

Youcandefinemultiplesuitenamesandtestgroupswithcommadelimitedstringsasattributevalues.

WhenusingtheJavaDSLbeforesuitesupportyoucansetsuitenamesandtestgroupfiltersbysimplycallingtherespectivesettermethodsinyourcustomimplementation.

<beanid="myAfterSuite"class="my.company.citrus.MyAfterSuite"><propertyname="suiteNames"><list><value>databaseSuite</value></list></property><propertyname="testGroups"><list><value>e2e</value></list></property></bean>

Environmentorsystempropertiesaredefinedaslistofkey-valuepairs.Whenspecifiedthepropertieshavetobepresentwithrespectivevalue.Incasethepropertyvalueisleftoutinconfigurationthepropertymustsimplyexistsonthesysteminordertoenablethebeforesuitesequenceinthattestrun.

XMLConfig

<citrus:after-suiteid="actionsBeforeSuite"suites="databaseSuite"groups="e2e"><citrus:env><citrus:propertyname="USER"/></citrus:env><citrus:system><citrus:propertyname="test-stage"value="e2e"/></citrus:system><citrus:actions><citrus-test:sqldataSource="testDataSource"/><citrus-test:statement>DELETEFROMTABLEPERSON</citrus-test:statement></citrus-test:sql></citrus:actions></citrus:after-suite>

CitrusReferenceGuide

498Testsuite

Page 499: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IntheexampleabovethesuitesequencewillonlyapplyonenvironmentswithUSERpropertysetandthesystempropertytest-stagemustbesettoe2e.Otherwisethesequenceexecutionisskipped.

Beforetest

BeforeeachtestisexecuteditalsomightsoundreasonabletopurgeallJMSqueuesforinstance.IncaseaprevioustestfailssomemessagesmightbeleftintheJMSqueues.Alsothedatabasemightbeindirtystate.Thefollow-uptestthenwillbeconfrontedwiththeseinvalidmessagesanddata.PurgingallJMSdestinationsbeforeatestisthereforeagoodidea.Justlikewepreparedsomedatainactionsbeforesuitewecancleanupthedatabeforeateststartstoexecute.

XMLConfig

<citrus:before-testid="defaultBeforeTest"><citrus:actions><!--listofactionsbeforetest--></citrus:actions></citrus:before-test>

Thebeforetestconfigurationcomponentreceivesauniqueidandalistoftestactionsthatgetexecutedbeforeatestcaseisstarted.Thecomponentreceivesusualtestactiondefinitionsjustlikeyouwouldwritetheminanormaltestcasedefinition.Seetheexamplebelowhowtoaddtestactions.

XMLConfig

<citrus:before-testid="defaultBeforeTest"><citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedbeforeeachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:before-test>

NotethatwemustusetheCitrustestcaseXMLnamespaceforthenestedtestactiondefinitions.YouhavetodeclaretheXMLnamespacesaccordinglyinyourconfigurationrootelement.Theechotestactionisnowexecutedbeforeeachtestinourtestsuiterun.

CitrusReferenceGuide

499Testsuite

Page 500: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Alsonoticethatwecanrestrictthebeforetestcontainerexecution.Wecanrestrictexecutionbasedonthetestname,package,testgroupsandenvironmentorsystemproperties.Seefollowingexamplehowthisworks:

XMLConfig

<citrus:before-testid="defaultBeforeTest"test="*_Ok_Test"package="com.consol.citrus.longrunning.*"<citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedbeforeeachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:before-test>

Theabovebeforetestcomponentisonlyexecutedfortestcasesthatmatchthenamepattern*_Ok_Testandthatmatchthepackagecom.consol.citrus.longrunning.*.Alsowecouldjustusethetestnamepatternorthepackagenamepatternexclusively.Andtheexecutioncanberestrictedbasedontheincludedtestgroupsinourtestsuiterun.Thisenablesustospecifybeforetestactionsinvariousways.Ofcourseyoucanhavemultiplebeforetestconfigurationcomponentsatthesametime.Citruswillpicktherightcontainersandputittoexecutionwhennecessary.

Environmentorsystempropertiesaredefinedaslistofkey-valuepairs.Whenspecifiedthepropertieshavetobepresentwithrespectivevalue.Incasethepropertyvalueisleftoutinconfigurationthepropertymustsimplyexistsonthesysteminordertoenablethebeforesuitesequenceinthattestrun.

XMLConfig

<citrus:before-testid="specialBeforeTest"><citrus:env><citrus:propertyname="USER"/></citrus:env><citrus:system><citrus:propertyname="test-stage"value="e2e"/></citrus:system><citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedbeforeeachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:before-test>

CitrusReferenceGuide

500Testsuite

Page 501: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IntheexampleabovethetestsequencewillonlyapplyonenvironmentswithUSERpropertysetandthesystempropertytest-stagemustbesettoe2e.Otherwisethesequenceexecutionisskipped.

WhenusingtheJavaDSLweneedtoimplementthebeforetestlogicinaseparateclassthatextendsTestDesignerBeforeTestSupportorTestRunnerBeforeTestSupport

JavaDSLdesigner

publicclassMyBeforeTestextendsTestDesignerBeforeTestSupport@OverridepublicvoidbeforeTest(TestDesignerdesigner)designer.echo("Thisactionshouldbeexecutedbeforeeachtest");

AsyoucanseetheclassimplementsthemethodbeforeTestthatisprovidedwithatestdesignerargument.YousimplyaddthebeforetestactionstothedesignerinstanceasusualbycallingJavaDSLmethodsonthedesignerobject.Citruswillautomaticallyexecutetheseoperationsbeforeeachtestisexecuted.ThesamelogicappliestothetestrunnervariationthatextendsTestRunnerBeforeTestSupport:

JavaDSLrunner

publicclassMyBeforeTestextendsTestRunnerBeforeTestSupport@OverridepublicvoidbeforeTest(TestRunnerrunner)runner.echo("Thisactionshouldbeexecutedbeforeeachtest");

ThebeforetestimplementationsareaddedtotheSpringbeanapplicationcontextforgeneralactivation.YoucandothiseitherasexplicitSpringbeandefinitionorviapackagecomponent-scan.Hereisasampleforaddingthebeanimplementationexplicitlywithsomeconfiguration

<beanid="myBeforeTest"class="my.company.citrus.MyBeforeTest"><propertyname="packageNamePattern"value="com.consol.citrus.e2e"></property></bean>

CitrusReferenceGuide

501Testsuite

Page 502: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WecanaddfilterpropertiestothebeforetestJavaDSLactionssotheyappliedtospecificpackagesortestnamepatterns.Theaboveexamplewillonlyapplytotestsinpackagecom.consol.citrus.e2e.Leavethesepropertiesemptyfordefaultactionsthatareexecutedbeforealltests.

Aftertest

Thesamelogicthatappliestothebefore-testconfigurationcomponentcanbedoneaftereachtest.Theafter-testconfigurationcomponentdefinestestactionsexecutedaftereachtest.Justlikewepreparedsomedatainactionsbeforeatestwecancleanupthedataafteratesthasfinishedexecution.

XMLConfig

<citrus:after-testid="defaultAfterTest"><citrus:actions><!--listofactionsaftertest--></citrus:actions></citrus:after-test>

Theaftertestconfigurationcomponentreceivesauniqueidandalistoftestactionsthatgetexecutedafteratestcaseisfinished.Noticethattheaftertestactionsareexecutednomatterwhatresultsuccessorfailuretheprevioustestcasecameupto.Thecomponentreceivesusualtestactiondefinitionsjustlikeyouwouldwritetheminanormaltestcasedefinition.Seetheexamplebelowhowtoaddtestactions.

XMLConfig

<citrus:after-testid="defaultAfterTest"><citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedaftereachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:after-test>

PleasebeawareofthefactthatwemustusetheCitrustestcaseXMLnamespaceforthenestedtestactiondefinitions.YouhavetodeclaretheXMLnamespacesaccordinglyinyourconfigurationrootelement.Theechotestactionisnowexecutedaftereachtestinourtestsuiterun.Ofcoursewecanrestricttheaftertestcontainerexecution.Supportedrestrictionsarebasedonthetestname,package,testgroupsandenvironmentorsystemproperties.Seefollowingexamplehowthisworks:

CitrusReferenceGuide

502Testsuite

Page 503: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

XMLConfig

<citrus:after-testid="defaultAfterTest"test="*_Error_Test"package="com.consol.citrus.error.*"<citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedaftereachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:after-test>

Theaboveaftertestcomponentisobviouslyonlyexecutedfortestcasesthatmatchthenamepattern*_Error_Testandthatmatchthepackagecom.consol.citrus.error.*.Alsowecouldjustusethetestnamepatternorthepackagenamepatternexclusively.Andtheexecutioncanberestrictedbasedontheincludedtestgroupsinourtestsuiterun.Thisenablesustospecifyaftertestactionsinvariousways.Ofcourseyoucanhavemultipleaftertestconfigurationcomponentsatthesametime.Citruswillpicktherightcontainersandputittoexecutionwhennecessary.

Environmentorsystempropertiesaredefinedaslistofkey-valuepairs.Whenspecifiedthepropertieshavetobepresentwithrespectivevalue.Incasethepropertyvalueisleftoutinconfigurationthepropertymustsimplyexistsonthesysteminordertoenablethebeforesuitesequenceinthattestrun.

XMLConfig

<citrus:after-testid="specialAfterTest"><citrus:env><citrus:propertyname="USER"/></citrus:env><citrus:system><citrus:propertyname="test-stage"value="e2e"/></citrus:system><citrus:actions><citrus-test:echo><citrus-test:message>Thisisexecutedaftereachtest!</citrus-test:message></citrus-test:echo></citrus:actions></citrus:after-test>

IntheexampleabovethetestsequencewillonlyapplyonenvironmentswithUSERpropertysetandthesystempropertytest-stagemustbesettoe2e.Otherwisethesequenceexecutionisskipped.

CitrusReferenceGuide

503Testsuite

Page 504: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WhenusingtheJavaDSLweneedtoimplementtheaftertestlogicinaseparateclassthatextendsTestDesignerAfterTestSupportorTestRunnerAfterTestSupport

JavaDSLdesigner

publicclassMyAfterTestextendsTestDesignerAfterTestSupport@OverridepublicvoidafterTest(TestDesignerdesigner)designer.echo("Thisactionshouldbeexecutedaftereachtest");

AsyoucanseetheclassimplementsthemethodafterTestthatisprovidedwithatestdesignerargument.YousimplyaddtheaftertestactionstothedesignerinstanceasusualbycallingJavaDSLmethodsonthedesignerobject.Citruswillautomaticallyexecutetheseoperationsaftereachtestisexecuted.ThesamelogicappliestothetestrunnervariationthatextendsTestRunnerAfterTestSupport:

JavaDSLrunner

publicclassMyAfterTestextendsTestRunnerAfterTestSupport@OverridepublicvoidafterTest(TestRunnerrunner)runner.echo("Thisactionshouldbeexecutedaftereachtest");

TheaftertestimplementationsareaddedtotheSpringbeanapplicationcontextforgeneralactivation.YoucandothiseitherasexplicitSpringbeandefinitionorviapackagecomponent-scan.Hereisasampleforaddingthebeanimplementationexplicitlywithsomeconfiguration

<beanid="myAfterTest"class="my.company.citrus.MyAfterTest"><propertyname="packageNamePattern"value="com.consol.citrus.e2e"></property></bean>

WecanaddfilterpropertiestotheaftertestJavaDSLactionssotheyappliedtospecificpackagesortestnamepatterns.Theaboveexamplewillonlyapplytotestsinpackagecom.consol.citrus.e2e.Leavethesepropertiesemptyfordefaultactionsthatareexecutedafteralltests.

CitrusReferenceGuide

504Testsuite

Page 505: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

505Testsuite

Page 506: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CustomizemetainformationTestcasesinCitrusareusuallyprovidedwithsomemetainformationliketheauthor’snameorthedateofcreation.InCitrusyouareabletoextendthistestcasemetainformationwithyourownveryspecificcriteria.

Bydefaultatestcasecomesshippedwithmetainformationthatlookslikethis:

<testcasename="PwdChange_OK_1_Test"><meta-info><author>Christoph</author><creationdate>2010-01-18</creationdate><status>FINAL</status><last-updated-by>Christoph</last-updated-by><last-updated-on>2010-01-18T15:00:00</last-updated-on></meta-info>

[...]</testcase>

Youcanquiteeasilyadddatatothissectioninordertomeetyourindividualtestingstrategy.Letushaveasimpleexampletoshowhowitisdone.

FirstofallwedefineacustomXSDschemadescribingthenewelements:

<?xmlversion="1.0"encoding="UTF-8"?><schemaxmlns="http://www.w3.org/2001/XMLSchema"xmlns:tns="http://www.citrusframework.org/samples/my-testcase-info"targetNamespace="http://www.citrusframework.org/samples/my-testcase-info"elementFormDefault="qualified">

<elementname="requirement"type="string"/><elementname="pre-condition"type="string"/><elementname="result"type="string"/><elementname="classification"type="string"/></schema>

Wehavefoursimpleelements(requirement,pre-condition,resultandclassification)alltypedasstring.Thesenewelementslatergointothetestcasemetainformationsection.

CitrusReferenceGuide

506Metainfo

Page 507: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AfterweaddedthenewXMLschemafiletotheclasspathofourprojectweneedtoannouncetheschematoSpring.AsyoumightknowalreadyaCitrustestcaseisnothingelsebutasimpleSpringconfigurationfilewithcustomizedXMLschemasupport.IfweaddnewelementstoatestcaseSpringneedstoknowtheXMLschemaforparsingthetestcaseconfigurationfile.Seethespring.schemasfileusuallyplacedintheMETA-INF/spring.schemasinyourproject.

Thefilecontentforourexamplewilllooklikefollows:

http://www.citrusframework.org/samples/my-testcase-info/my-testcase-info.xsd=com/consol/citrus/schemas/my-testcase-info.xsd

Sonowwearefinallyreadytousethenewmeta-infoelementsinsidethetestcase:

<?xmlversion="1.0"encoding="UTF-8"?><spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:spring="http://www.springframework.org/schema/beans"xmlns:custom="http://www.citrusframework.org/samples/my-testcase-info"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsdhttp://www.citrusframework.org/samples/my-testcase-infohttp://www.citrusframework.org/samples/my-testcase-info/my-testcase-info.xsd">

<testcasename="PwdChange_OK_1_Test"><meta-info><author>Christoph</author><creationdate>2010-01-18</creationdate><status>FINAL</status><last-updated-by>Christoph</last-updated-by><last-updated-on>2010-01-18T15:00:00</last-updated-on><custom:requirement>REQ10001</custom:requirement><custom:pre-condition>Existinguser,sufficientrights</custom:pre-condition><custom:result>Passwordresetindatabase</custom:result><custom:classification>PasswordChange</custom:classification></meta-info>

[...]</testcase></spring:beans>

NoteWeuseaseparatenamespacedeclarationwithacustomnamespaceprefix“custom”inordertodeclarethenewXMLschematoourtestcase.Ofcourseyoucanpickanamespaceurlandprefixthatfitsbestforyourproject.Asyouseeitisquiteeasy

CitrusReferenceGuide

507Metainfo

Page 508: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

toaddcustommetainformationtoyourCitrustestcase.Thecustomizedelementsmaybepreciousforautomaticreporting.XSLtransformationsforinstanceareabletoreadthosemetainformationelementsinordertogenerateautomatictestreportsanddocumentation.

YoucanalsodeclareournewXMLschemaintheEclipsepreferencessectionasuserspecificXMLcatalogentry.TheneventheschemacodecompletioninyourEclipseXMLeditorwillbeavailableforourcustomizedmeta-infoelements.

CitrusReferenceGuide

508Metainfo

Page 509: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Tracingincoming/outgoingmessagesAswedealwithmessagebasedinterfacesCitruswillsendandreceivealotofmessagesduringatestrun.NowwemaywanttoseethesemessagesinchronologicalorderastheywereprocessedbyCitrus.WecanenablemessagetracinginCitrusinordertosavemessagestothefilesystemforfurtherinvestigations.

Citrusoffersaneasywaytodebugallreceivedmessagestothefilesystem.YouneedtoenablesomespecificloggersandinterceptorsintheSpringapplicationcontext.

<beanclass="com.consol.citrus.report.MessageTracingTestListener"/>

JustaddthisbeantotheSpringconfigurationandCitruswilllistenforsentandreceivedmessagesforsavingthosetothefilesystem.Youwillfindfilesliketheseinthedefaulttest-outputfolderafterthetestrun:

Forexample:

logs/trace/messages/MyTest.msgslogs/trace/messages/FooTest.msgslogs/trace/messages/SomeTest.msgs

EachCitrustestwritesa.msgsfilecontainingallmessagesthatwentoverthewireduringthetest.Bydefaultthedebugdirectoryissettologs/trace/messages/relativetotheprojecttestoutputdirectory.Butyoucansetyourownoutputdirectoryintheconfiguration

<beanclass="com.consol.citrus.report.MessageTracingTestListener"><propertyname="outputDirectory"value="file:/path/to/folder"/></bean>

NoteAsthefilenamesdonotchangewitheachtestrunmessagetracingfilesmaybeoverwritten.Soyoueventuallyneedtosavethegeneratedmessagedebugfilesbeforerunninganothergroupoftestcases.

LetsseesomesampleoutputforatestcasewithmessagecommunicationoverSOAPHttp:

SendingSOAPrequest:

CitrusReferenceGuide

509Messagetracing

Page 510: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<?xmlversion="1.0"encoding="UTF-8"?><SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"<SOAP-ENV:Header><Operationxmlns="http://citrusframework.org/test">sayHello</Operation></SOAP-ENV:Header><SOAP-ENV:Body><ns0:HelloRequestxmlns:ns0="http://www.consol.de/schemas/samples/sayHello.xsd"><ns0:MessageId>0857041782</ns0:MessageId><ns0:CorrelationId>6915071793</ns0:CorrelationId><ns0:User>Christoph</ns0:User><ns0:Text>HelloWebServer</ns0:Text></ns0:HelloRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>

======================================================================

ReceivedSOAPresponse:<?xmlversion="1.0"encoding="UTF-8"?><SOAP-ENV:Envelopexmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"<SOAP-ENV:Header/><SOAP-ENV:Body><ns0:HelloResponsexmlns:ns0="http://www.consol.de/schemas/samples/sayHello.xsd"><ns0:MessageId>0857041782</ns0:MessageId><ns0:CorrelationId>6915071793</ns0:CorrelationId><ns0:User>WebServer</ns0:User><ns0:Text>HelloChristoph</ns0:Text></ns0:HelloResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

Forthismessagetracingtoworkweneedtoaddlogginglistenerstooursenderandreceivercomponentsaccordingly.

<citrus-ws:clientid="webServiceClient"request-url="http://localhost:8071"message-factory="messageFactory"interceptors="clientInterceptors"/>

<util:listid="clientInterceptors"><beanclass="com.consol.citrus.ws.interceptor.LoggingClientInterceptor"/></util:list>

ImportantBeawareofaddingtheSpringutilXMLnamespacetotheapplicationcontextwhenusingtheutil:listconstruct.

CitrusReferenceGuide

510Messagetracing

Page 511: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

511Messagetracing

Page 512: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ReportingandtestresultsTheframeworkgeneratesdifferentreportsandresultsafteratestrunforyou.Thesereportandresultpageswillhelpyoutogetanoverviewofthetestcasesthatwereexecutedandwhichonewerefailing.

Consolelogging

Duringthetestruntheframeworkwillprovideahugeamountofinformationthatisprintedouttotheconsole.Thisincludescurrenttestprogress,validationresultsanderrorinformation.Thisenablestheusertoquicklysupervisethetestrunprogress.Failuresintestswillbeprintedtotheconsolejustthetimetheerroroccurred.Thedetailedstacktraceinformationandthedetailederrormessagesarehelpfultogettheideawhatwentwrong.

Astheconsoleoutputmightbelimitedtoadefinedbufferlimit,theusermaynotbeabletofollowtheoutputtotheverybeginningofthetestrun.Thereforetheframeworkadditionallyprintsallinformationtoalogfileaccordingtotheloggingconfiguration.

TheloggingmechanismusestheSLF4Jloggingframework.SLF4Jisindependentofloggingframeworkimplementationsonthemarket.SoincaseyouuseLog4Jloggingframeworkthespecifiedlogfilepathaswellaslogginglevelscanbefreelyconfiguredintherespectivelog4j.xmlfileinyourproject.Attheendofatestrunthecombinedtestresultsgetprintedtobothconsoleandlogfile.Theoveralltestresultslooklikefollowingexample:

CITRUSTESTRESULTS

[...]HelloService_Ok_1:SUCCESSHelloService_Ok_2:SUCCESSEchoService_Ok_1:SUCCESSEchoService_Ok_2:SUCCESSEchoService_TempError_1:SUCCESSEchoService_AutomacticRetry_1:SUCCESS[...]

Found175testcasestoexecuteSkipped0testcases(0.0%)Executed175testcasesTestsfailed:0(0.0%)

CitrusReferenceGuide

512Reporting

Page 513: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Testssuccessfully:175(100.0%)

Failedtestswillbemarkedasfailedintheresultlist.Theframeworkwillgiveashortdescriptionoftheerrorcausewhilethedetailedstacktraceinformationcanbefoundinthelogmessagesthatweremadeduringthetestrun.

HelloService_Ok_3:failed-ExceptionisActiontimedout

JUnitreports

AstestsareexecutedasTestNGtestcases,theframeworkwillalsogenerateJUnitcompliantXMLandHTMLreports.JUnittestreportsareverypopularandfindsupportinmanybuildmanagementanddevelopmenttools.IngeneraltheCitrustestreportsgiveyouanoverallpictureofalltestsandtellyouwhichofthemwerefailing.

BuildmanagementtoolslikeJenkinscaneasilyimportanddisplaythegeneratedJUnitXMLresults.PleasehavealookattheTestNGandJUnitdocumentationformoreinformationaboutthistopicaswellasthebuildmanagementtools(e.g.Jenkins)tofindouthowtointegratethetestsresults.

HTMLreports

CitruscreatesHTMLreportsaftereachtestrun.Thereportprovidesdetailedinformationonthetestrunwithasummaryofalltestresults.Youcanfindthereportafteratestruninthedirectory$project.build.directory/test-output/citrus-reports.

Thereportconsistsoftwoparts.Thetestsummaryontopshowsthetotalnumberexecutedtests.Themainpartlistsalltestcaseswithdetailedinformation.Withthisreportyouimmediatelyidentifyallteststhatwerefailing.Eachtestcaseismarkedincoloraccordingtoitsresultoutcome.

ThefailedtestsgivedetailederrorinformationwitherrormessagesandJavaStackTraceinformation.InadditiontothatthereporttriestofindthetestactioninsidetheXMLtestpartthatfailedinexecution.Withthefailingcodesnippetyoucanseewheretheteststopped.

NoteJavaScriptshouldbeactiveinyourwebbrowser.Thisistoenablethedetailedinformationwhichcomestoyouinformoftooltipsliketestauthorordescription.IfyouwanttoaccessthetooltipsJavaScriptshouldbeenabledinyourbrowser.

CitrusReferenceGuide

513Reporting

Page 514: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheHTMLreportsarecustomizablebysystemproperties.Usefollowingpropertiese.g.inyourcitrus.propertiesfile:

citrus.html.report.enabled:Enables/disablesHTMLreportgeneration(default=true).citrus.html.report.directory:Outputdirectorypath(default=$project.build.directory/test-output/citrus-reports).citrus.html.report.file:Filenameforthereportfile(default=citrus-test-results.html).citrus.html.report.template:TemplateHTMLfilewithplaceholdersforreportresults.citrus.html.report.detail.template:Templatefilefordetailedtestresults.citrus.html.report.logo:FileresourcepathpointingtoaimagethatisaddedtotopofHTMLreport.

TheHTMLreportisbasedonatemplatefilethatiscustomizabletoyourspecialneeds.Thedefaulttemplatescanbefoundinhttps://github.com/christophd/citrus/tree/master/modules/citrus-core/src/main/resources/com/consol/citrus/report.

CitrusReferenceGuide

514Reporting

Page 515: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SamplesThischaptergivessomesampleswhereyoucanseeCitrusinaction.

samples-flightbooking

CitrusReferenceGuide

515Samples

Page 516: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheFlightBookingsample

AsimpleprojectexampleshouldgiveyoutheideahowCitrusworks.Thesystemundertestisaflightbookingservicethathandlestravelrequestsfromatravelagency.Atravelrequestconsistsofacompletetravelrouteincludingseveralflights.TheFlightBookingServiceapplicationwillsplitthecompletetravelbookingintoseparateflightbookingsthataresenttotherespectiveairlinesincharge.Thebookingandcustomerdataispersistedinadatabase.

Theairlineswillconfirmordenytheflightbookings.TheFlightBookingServiceapplicationconsolidatesallincomingflightconfirmationsandcombinesthemtoacompletetravelconfirmationordenialthatissentbacktothetravelagency.Nextpicturetriestoputthearchitectureintographics:

InourexampletwodifferentairlinesareconnectedtotheFlightBookingServiceapplication:theSmartArilineoverJMSandtheRoyalAirlineoverHttp.

Theusecase

Theusecasethatwewouldliketotestisquitesimple.Thetestshouldhandleasimpletravelbookingandexpectapositiveprocessingtotheend.Thetestcaseneithersimulatesbusinesserrorsnortechnicalproblems.Nextpictureshowstheusecaseasasequencediagram.

CitrusReferenceGuide

516FlightBookingSample

Page 517: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Thetravelagencyputsatravelbookingrequesttowardsthesystem.Thetravelbookingcontainstwoseparateflights.Theflightrequestsarepublishedtotheairlines(SmartAirlineandRoyalAirline).Bothairlinesconfirmtheflightbookingswithapositiveanswer.Theconsolidatedtravelbookingresponseisthensentbacktothetravelagency.

Configurethesimulatedsystems

Citrussimulatesallsurroundingapplicationsintheirbehaviorduringthetest.Thesimulatedapplicationsare:TravelAgency,SmartAirlineandRoyalAirline.ThesimulatedsystemshavetobeconfiguredintheCitrusconfigurationfirst.TheconfigurationisdoneinSpringXMLconfigurationfiles,asCitrususesSpringtoglueallitsservicestogether.

FirstofallwehavealookattheTravelAgencyconfiguration.TheTravelAgencyisusingJMStoconnecttoourtestedsystem,soweneedtoconfigurethisJMSconnectioninCitrus.

<beanname="connectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory"><propertyname="brokerURL"value="tcp://localhost:61616"/></bean>

<citrus-jms:endpointid="travelAgencyBookingRequestEndpoint"destination-name="$travel.agency.request.queue"/>

<citrus-jms:endpointid="travelAgencyBookingResponseEndpoint"destination-name="$travel.agency.response.queue"/>

CitrusReferenceGuide

517FlightBookingSample

Page 518: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ThisisallCitrusneedstosendandreceivemessagesoverJMSinordertosimulatetheTravelAgency.BydefaultallJMSmessagesendersandreceiversneedaconnectionfactory.ThereforeCitrusissearchingforabeannamed"connectionFactory".IntheexampleweconnecttoaActiveMQmessagebroker.AconnectiontootherJMSbrokerslikeTIBCOEMSorApacheActiveMQispossibletoobysimplychangingtheconnectionfactoryimplementation.

Theidentifiersofthemessagesendersandreceiversareveryimportant.Weshouldthinkofsuitableidsthatgivethereaderafirsthintwhatthesender/receiverisusedfor.AswewanttosimulatetheTravelAgencyincombinationwithsendingbookingrequestsouridis"travelAgencyBookingRequestEndpoint"forexample.

ThesenderandreceiversdoalsoneedaJMSdestination.Herethedestinationnamesareprovidedbypropertyexpressions.TheSpringIoCcontainerresolvesthepropertiesforus.AllweneedtodoispublishthepropertyfiletotheSpringcontainerlikethis.

<beanname="propertyLoader"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><propertyname="locations"><list><value>citrus.properties</value></list></property><propertyname="ignoreUnresolvablePlaceholders"value="true"/></bean>

Thecitrus.propertiesfileislocatedinourproject'sresourcesfolderanddefinestheactualqueuenamesbesidesotherpropertiesofcourse:

#JMSqueuestravel.agency.request.queue=Travel.Agency.Request.Queuetravel.agency.response.queue=Travel.Agency.Response.Queuesmart.airline.request.queue=Smart.Airline.Request.Queuesmart.airline.response.queue=Smart.Airline.Response.Queueroyal.airline.request.queue=Royal.Airline.Request.Queue

WhatelsedoweneedinourSpringconfiguration?TherearesomebasicbeansthatarecommonlydefinedinaCitrusapplicationbutIdonotwanttoboreyouwiththesedetails.SoifyouwanttohavealookattheSpringapplicationcontextfileintheresourcesfolderandseehowthingsaredefinedthere.

CitrusReferenceGuide

518FlightBookingSample

Page 519: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WecontinuewiththefirstairlinetobeconfiguredtheSmartAirline.TheSmartAirlineisalsousingJMStocommunicatewiththeFlightBookingService.Sothereisnothingnewforus,wesimplydefineadditionalJMSmessagesendersandreceivers.

<citrus-jms:endpointid="smartAirlineBookingRequestEndpoint"destination-name="$smart.airline.request.queue"/>

<citrus-jms:endpointid="smartAirlineBookingResponseEndpoint"destination-name="$smart.airline.response.queue"/>

WedonotdefineanewJMSconnectionfactorybecauseTravelAgencyandSmartAirlineareusingthesamemessagebrokerinstance.Incaseyouneedtohandlemultipleconnectionfactoriessimplydefinetheconnectionfactorywiththeattribute"connection-factory".

<citrus-jms:endpointid="smartAirlineBookingRequestEndpoint"destination-name="$smart.airline.request.queue"connection-factory="smartAirlineConnectionFactory"/>

<citrus-jms:endpointid="smartAirlineBookingResponseEndpoint"destination-name="$smart.airline.response.queue"connection-factory="smartAirlineConnectionFactory"/>

ConfiguretheHttpadapter

TheRoyalAirlineisconnectedtooursystemusingHttprequest/responsecommunication.ThismeanswehavetosimulateaHttpserverinthetestthatacceptsclientrequestsandprovidesproperresponses.CitrusoffersaHttpserverimplementationthatwilllistenonaportforclientrequests.TheadapterforwardsincomingrequesttothetestengineoverJMSandreceivesaproperresponsethatisforwardedasaHttpresponsetotheclient.Thenextpictureshowsthismechanismindetail.

TheRoyalAirlineadapterreceivesclientrequestsoverHttpandsendsthemoverJMStoamessagereceiveraswealreadyknowit.Thetestenginevalidatesthereceivedrequestandprovidesaproperresponsebacktotheadapter.Theadapterwilltransform

CitrusReferenceGuide

519FlightBookingSample

Page 520: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

theresponsetoHttpagainandpublishesittothecallingclient.CitrusoffersthesekindofadaptersforHttpandSOAPcommunication.BywritingyourownadapterslikethisyouwillbeabletoextendCitrussoitworkswithprotocolsthatarenotsupportedyet.

LetusdefinetheHttpadapterintheSpringconfiguration:

<citrus-http:serverid="royalAirlineHttpServer"port="8091"uri="/flightbooking"endpoint-adapter="jmsEndpointAdapter"/>

<citrus-jms:endpoint-adapterid="jmsEndpointAdapterdestination-name="$royal.airline.request.queue"/>connection-factory="connectionFactory"/>timeout="2000"/>

<citrus-jms:sync-endpointid="royalAirlineBookingEndpoint"destination-name="$royal.airline.request.queue"/>

WeneedtoconfigureaHttpserverinstancewithaport,arequestURIandtheendpointadapter.WedefinetheJMSendpointadaptertohandlerequestasdescribed.InAdditiontotheendpointadapterwealsoneedsynchronousJMSmessagesenderandreceiverinstances.That'sit!WeareabletoreceiveHttprequestinordertosimulatetheRoyalAirlineapplication.Whatismissingnow?Thetestcasedefinitionitself.

Thetestcase

ThetestcasedefinitionisalsoaSpringconfigurationfile.CitrusoffersacustomizedXMLsyntaxtodefineatestcase.ThisXMLtestdefininglanguageissupposedtobeeasytounderstandandmorespecifictothedomainwearedealingwith.Nextlistingshowsthewholetestcasedefinition.Keepinmindthatatestcasedefineseverystepintheusecase.Sowedefinesendingandreceivingactionsoftheusecaseasdescribedinthesequencediagramwesawearlier.

<?xmlversion="1.0"encoding="UTF-8"?><spring:beansxmlns="http://www.citrusframework.org/schema/testcase"xmlns:spring="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.citrusframework.org/schema/testcasehttp://www.citrusframework.org/schema/testcase/citrus-testcase.xsd"><testcasename="FlightBookingTest"><meta-info>

CitrusReferenceGuide

520FlightBookingSample

Page 521: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<author>ChristophDeppisch</author><creationdate>2009-04-15</creationdate><status>FINAL</status><last-updated-by>ChristophDeppisch</last-updated-by><last-updated-on>2009-04-15T00:00:00</last-updated-on></meta-info><description>Testflightbookingservice.</description><variables><variablename="correlationId"value="citrus:concat('Lx1x','citrus:randomNumber(10)')"/><variablename="customerId"value="citrus:concat('Mx1x',citrus:randomNumber(10))"/></variables><actions><sendendpoint="travelAgencyBookingRequestEndpoint"><message><data><![CDATA[<TravelBookingRequestMessagexmlns="http://www.consol.com/schemas/TravelAgency"><correlationId>$correlationId</correlationId><customer><id>$customerId</id><firstname>John</firstname><lastname>Doe</lastname></customer><flights><flight><flightId>SM1269</flightId><airline>SmartAirline</airline><fromAirport>MUC</fromAirport><toAirport>FRA</toAirport><date>2009-04-15</date><scheduledDeparture>11:55:00</scheduledDeparture><scheduledArrival>13:00:00</scheduledArrival></flight><flight><flightId>RA1780</flightId><airline>RoyalAirline</airline><fromAirport>FRA</fromAirport><toAirport>HAM</toAirport><date>2009-04-15</date><scheduledDeparture>16:00:00</scheduledDeparture><scheduledArrival>17:10:00</scheduledArrival></flight></flights></TravelBookingRequestMessage>]]></data></message>

CitrusReferenceGuide

521FlightBookingSample

Page 522: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<header><elementname="correlationId"value="$correlationId"/></header></send>

<receiveendpoint="smartAirlineBookingRequestEndpoint"><message><data><![CDATA[<FlightBookingRequestMessagexmlns="http://www.consol.com/schemas/AirlineSchema"><correlationId>$correlationId</correlationId><bookingId>???</bookingId><customer><id>$customerId</id><firstname>John</firstname><lastname>Doe</lastname></customer><flight><flightId>SM1269</flightId><airline>SmartAirline</airline><fromAirport>MUC</fromAirport><toAirport>FRA</toAirport><date>2009-04-15</date><scheduledDeparture>11:55:00</scheduledDeparture><scheduledArrival>13:00:00</scheduledArrival></flight></FlightBookingRequestMessage>]]></data><ignorepath="//:FlightBookingRequestMessage/:bookingId"/></message><header><elementname="correlationId"value="$correlationId"/></header><extract><messagepath="//:FlightBookingRequestMessage/:bookingId"variable="$smartAirlineBookingId"/></extract></receive>

<sendendpoint="smartAirlineBookingResponseEndpoint"><message><data><![CDATA[<FlightBookingConfirmationMessagexmlns="http://www.consol.com/schemas/AirlineSchema"><correlationId>$correlationId</correlationId><bookingId>$smartAirlineBookingId</bookingId><success>true</success><flight><flightId>SM1269</flightId>

CitrusReferenceGuide

522FlightBookingSample

Page 523: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<airline>SmartAirline</airline><fromAirport>MUC</fromAirport><toAirport>FRA</toAirport><date>2009-04-15</date><scheduledDeparture>11:55:00</scheduledDeparture><scheduledArrival>13:00:00</scheduledArrival></flight></FlightBookingConfirmationMessage>]]></data></message><header><elementname="correlationId"value="$correlationId"/></header></send>

<receiveendpoint="royalAirlineBookingEndpoint"><message><data><![CDATA[<FlightBookingRequestMessagexmlns="http://www.consol.com/schemas/FlightBooking/AirlineSchema"><correlationId>$correlationId</correlationId><bookingId>???</bookingId><customer><id>$customerId</id><firstname>John</firstname><lastname>Doe</lastname></customer><flight><flightId>RA1780</flightId><airline>RoyalAirline</airline><fromAirport>FRA</fromAirport><toAirport>HAM</toAirport><date>2009-04-15</date><scheduledDeparture>16:00:00</scheduledDeparture><scheduledArrival>17:10:00</scheduledArrival></flight></FlightBookingRequestMessage>]]></data><ignorepath="//:FlightBookingRequestMessage/:bookingId"/></message><header><elementname="correlationId"value="$correlationId"/></header><extract><messagepath="//:FlightBookingRequestMessage/:bookingId"variable="$royalAirlineBookingId"/></extract></receive>

CitrusReferenceGuide

523FlightBookingSample

Page 524: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<sendendpoint="royalAirlineBookingEndpoint"><message><data><![CDATA[<FlightBookingConfirmationMessagexmlns="http://www.consol.com/schemas/AirlineSchema"><correlationId>$correlationId</correlationId><bookingId>$royalAirlineBookingId</bookingId><success>true</success><flight><flightId>RA1780</flightId><airline>RoyalAirline</airline><fromAirport>FRA</fromAirport><toAirport>HAM</toAirport><date>2009-04-15</date><scheduledDeparture>16:00:00</scheduledDeparture><scheduledArrival>17:10:00</scheduledArrival></flight></FlightBookingConfirmationMessage>]]></data></message><header><elementname="correlationid"value="$correlationId"/></header></send>

<receiveendpoint="travelAgencyBookingResponseEndpoint"><message><data><![CDATA[<TravelBookingResponseMessagexmlns="http://www.consol.com/schemas/TravelAgency"><correlationId>$correlationId</correlationId><success>true</success><flights><flight><flightId>SM1269</flightId><airline>SmartAirline</airline><fromAirport>MUC</fromAirport><toAirport>FRA</toAirport><date>2009-04-15</date><scheduledDeparture>11:55:00</scheduledDeparture><scheduledArrival>13:00:00</scheduledArrival></flight><flight><flightId>RA1780</flightId><airline>RoyalAirline</airline><fromAirport>FRA</fromAirport><toAirport>HAM</toAirport><date>2009-04-15</date><scheduledDeparture>16:00:00</scheduledDeparture>

CitrusReferenceGuide

524FlightBookingSample

Page 525: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

<scheduledArrival>17:10:00</scheduledArrival></flight></flights></TravelBookingResponseMessage>]]></data></message><header><elementname="correlationId"value="$correlationId"/></header></receive>

</actions></testcase></spring:beans>

Similartoasequencediagramthetestcasedescribeseverystepoftheusecase.Attheverybeginningthetestcasegetsnameanditsmetainformation.Followingwiththevariablevaluesthatareusedalloverthetest.HereitisthecorrelationIdandthecustomerIdthatareusedastestvariables.Insidemessagetemplatesheadervaluesthevariablesarereferencedseveraltimesinthetest

<correlationId>$correlationId</correlationId><id>$customerId</id>

Thesending/receivingactionsuseapreviouslydefinedmessagesender/receiver.ThisisthelinkbetweentestcaseandbasicSpringconfigurationwehavedonebefore.

sendendpoint="travelAgencyBookingRequestEndpoint"

Thesendingactionchoosesamessagesendertoactuallysendthemessageusingamessagetransport(JMS,Http,SOAP,etc.).Aftersendingthisfirst"TravelBookingRequestMessage"requestthetestcaseexpectsthefirst"FlightBookingRequestMessage"messageontheSmartAirlineJMSdestination.Incasethismessageisnotarrivingintimethetestwillfailwitherrors.InpositivecaseourFlightBookingServiceworkswellandthemessagearrivesintime.Thereceivedmessageisvalidatedagainstadefinedexpectedmessagetemplate.Onlyincaseallcontentvalidationstepsaresuccessfulthetestcontinueswiththeactionchain.Andsothetestcaseproceedsandworksthroughtheusecaseuntileverymessageissentrespectivelyreceivedandvalidated.Theusecaseisdoneautomaticallywithouthumaninteraction.Citrussimulatesallsurroundingapplicationsandprovidesdetailedvalidationpossibilitiesofmessages.

CitrusReferenceGuide

525FlightBookingSample

Page 526: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusReferenceGuide

526FlightBookingSample

Page 527: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

AppendixThischaptergivesabriefoverviewofallarchivedchanges.

Changes2.5Changes2.4Changes2.3Changes2.2Changes2.1Changes2.0Changes1.4Changes1.3Changes1.2

CitrusReferenceGuide

527Appendix

Page 528: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.6Citrus2.6comeswithasetofnewmodulesthatenablecompletelynewaspectsofintegrationtesting.NamelythesearethenewmodulesforCucumberbehaviordrivendevelopmentandZookeepersupport.Justhavealookatthefollowingfeaturesthatareshippedwithinthe2.6box.

Gzipcompression

CitrusnowsupportsGzipmessagecompression.ForHttpclientserverendpointsweintroducedspecialcompressionfiltersthatautomaticallytakcareoncompressionwhenthehttpheaderAccept-Encoding=gziporContent-Encoding=gzipisset.Forotherendpointsweintroducedthemessagetypegzipandthemessagevalidatorgzip-base64whichautomaticallycompressesanddecompressesmessagepayloadsandenablesbase64Stringcomparisonforvalidationpurpose.Thenewcompressionfeaturesaredescribedinhttpandvalidation-gzip.

Customservletfilters

TheCitrushttpservercomponentnowacceptscustomservletfilterimplementations.Thisisusefulforimplementingcustomlogiconrequest/responseprocessingsuchasautomaticmessagecompressionorcaching.Youcansetoneormanycustomfilterimplementationsandmapthosetorequestpathsfortheserver.Readaboutthisinchapterhttp.

Escapetestvariablesyntax

Citrususestestvariablesandlooksfortheexpressionsoftype$variable-name.NowwhenthissamesyntaxispartofamessagecontentwerunintoerrorsasCitruswantstofindatestvariable.AttheendCitruscomplainsabouttheunknownvariable.ThereforeweintroducedanescapesyntaxforvariablessoyoucanskiptheCitrusvariableexpressionevaluation.Youcandothisbyusing$//escaped//syntax.Readmoreaboutthisintest-variables.

ConfigurableXMLserializer

CitrusReferenceGuide

528Changes2.6

Page 529: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WeoftendealwithXMLmessageformatandthereforeneedtoparseandserializeXMLdata.ThedefaultXMLserializerusesprettyprintformatandcdatasectionsupport.Nowsometimesitismandatorytocustomizethesesettingswhichispossiblewiththenewversion.YoucanaddacustomXMLserializerintheSpringapplicationcontextandCitruswillautomaticallyusethisimplementationandconfiguration.Youcanseehowitworksinchaptervalidation-xml.

Localmessagestore

Weintroducedalocalmessagestorethatautomaticallysavesallexchangedmessages(inboundandoutbound).Thismessagestorecanbeusedtogetexchangedmessagesduringandafterthetest.Testactionsaswellastestlistenerscanaccessthelocalmessagestore.Readmoreaboutthisinchaptersendpoints,actions-sendandactions-receive.

Waitmessagecondition

Thewaittestactionhasanewcondition.Besideswaitingforfilestoexistandhttprequeststoberespondedyoucannowwaitformessagesinthelocalmessagestore.Thiswayyoucanwaitforacertainmessagetoarrive.Thisisdescribedinchapteractions-wait.

XpathandJsonPathFunction

TherearenewfunctionsavailabletoevaluatesomeXpathorJsonPathexpressiononaXML/Jsonsource.Thesourcecanbeastaticstructurecomingfromanexternalfileoramessagepayloadstoredinthelocalstorage.Seehowtousethisfunctionsinchapterfunctions.

Staticresponseadaptervariablessupport

Servercomponentscanusestaticresponseadaptersthatautomaticallysendresponsemessagestoanycallingclient.Theresponseadapterisnowabletousetestvariablesandfunctions.InadditiontothatyoucanmapvaluesfromtheactualrequestmessagethathastriggeredtheresponseadapterbyusingthelocalmessagestoreincombinationwithXpathorJsonPath.Readaboutthisinendpoint-adapter.

CucumberBDDsupport

CitrusReferenceGuide

529Changes2.6

Page 530: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Behaviordrivendevelopmentismoreandmorecomingupalsointheintegrationtestingenvironment.CucumberisafantasticbehaviordrivendevelopmentlibrarythatprovidessupportforBDDconceptswithGherkin.ThenewCitrusintegrationwithCucumberenablesthemixofGherkinsyntaxfeaturescenarioswithCitrustestcaseexecution.YouwritefeaturestoriesasusualandcreateCitrustestcaseswithlotsofactionsfortheintegrationtest.Seedetailsforthisfeatureincucumber.

Zookeepersupport

ZookeeperfromApacheletsyoumanageconfigurationwithdistributedcoordination.AsauseryoucreateandeditvaluesonaZookeeperserver.Otherclientsthencanretrievethisinformation.WithCitrusyouareabletoaccessthisinformationfromwithinatestcase.TheZookeeperCitrusclientletsyoumanageinformationontheZookeeperserver.Seedetailsforthisfeatureinzookeeper.

SpringRestdocssupport

RestdocsisafantasticwayofgeneratingdocumentationforRESTfulAPIs.Whileexchangingrequest/responsedatawiththeserverRestdocscreatesdocumentationinformationonthedata.Thedocumentationincludesfielddescriptions,headersandsnippetsforbodycontent.WithnewCitrusversionHttpclientsinCitruscanaddRestdocinterceptorsthatgeneratethedocumentationwhileexecutingthetestcases.Thiswayyouareabletodocumentwhatmessageswereexchangedintests.TheRestdocssupportisalsoavailablefortheSOAPHttpclientinCitrus.Seedetailsinrestdocs.

Hamcrestmatcherconditions

IteratingtestactioncontainersinCitrusevaluatebooleanexpressionsfordeterminationofhowtoexecutethenestedactionsinaloop.Alsotheconditionalcontainerexecutesnestedactionsbasedonbooleanexpressionevaluation.TheCitrusbooleanexpressionsupportislimitedtoverybasicoperationssuchaslowerthanorgreaterthan.Furthermorethecombinationofbooleanexpressionswithvariableshasnotbeensupported.FollowingfromthatwehaveimprovedthebooleanexpressionevaluationmechanismwithextensiontoHamcrestmatchers.Sonowyoucanevaluatematchersiniteratingconditions.Thisfeatureisdescribedincontainers-conditionalandcontainers-iterate.

SOAPJavaDSL

CitrusReferenceGuide

530Changes2.6

Page 531: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusprovidesanewJavafluentAPIforsendingandreceivingSOAPrelatedmessagecontent.TheJavaDSLenhancementsarebasedonthoseofHttp.NowyoucandefineSOAPmessageswithspecialSOAPactionheadersmoreeasily.OntopofthatyoucanhandleSOAPfaultsonclientandserverwiththefluentAPI.Checkoutsoap-webservicesfordetails.

Refactoring

Refactoringintermsofsimplificationandstandardizationispartofourdailylifeasadeveloper.WehavebeenworkingonimprovingtheJavaDSLfluentAPIforSOAP.Wealsointroducedamorecommonwayofhandlingthetestactioncontainerslikeiterate,parallelandsoon.Thisleadstosomeclassesandmethodsthatweremarkedasdeprecated.SopleasehavealookatyourJavaDSLcodeandifyouseesomeusageofdeprecatedstuffpleaseusethenewapproachesassoonaspossible.Thedeprecatedstuffwilldefinitelydisappearinupcomingreleases.

Someofthechangesthatwehavemademighthityourightaway.Thesechangesare:

ws:assertelementinSOAPtestcaseschemahasbeenrenamedtows:assert-fault.Thiswasdoneforbetterinteroperabilityreasonswithassertactionincoreschemaandtobecomplianttosend-faultaction.

JavaDSLmodulehashadMavendependenciestoseveralothermodulesinCitrus(e.g.citrus-jms,citrus-soap).Thesedependenciesweredeclaredascompiledependencies,whichisnotveryniceasyoumightnotneedJMSorSOAPfunctionalitiesinyourproject.Wehaveaddedoptionalandprovidedmarkerstothatdependencieswhichmeansthatyouhavetodecideinyourprojectwhichofthemodulestoinclude.

YoumayfacesomemissingdependencieserrorswhenrunningtheMavenproject.AsaresultyouneedtoincludetheCitrusmodules(e.g.citrus-http,citrus-docker,andsoon)inyourprojectMavenPOMexplicitly.

CitrusReferenceGuide

531Changes2.6

Page 532: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.5WehaveaddedlotsofnewfeaturesandimprovementswithCitrus2.5.NamelythesearethenewmodulesforRMIandJMXsupport,anewx-www-form-urlencodedmessagevalidatorandnewfunctionsanctestactions.Justhavealookatthefollowingfeaturesthatmadeittothebox.

Hamcrestmatchersupport

Hamcrestisaverypowerfulmatcherlibrarythatprovidesafantasticsetofmatcherimplementationsformessagevalidationpurpose.CitrusnowsupportsthesematcherscomingfromHamcrestlibrary.OntheonehandyoucanuseHamcrestmatchersasaCitrusvalidationmatcherasdescribedinvalidation-matcher-hamcrest.OntheotherhandyoucanuseHamcrestmatchersnowdirectlyusingtheCitrusJavaDSL.Seedetailsforthisfeatureinjson-path-validate.

Binarybase64messagevalidator

Thereisanewmessagevalidatorimplementationthatautomaticallyconvertsbinarymessagecontenttoabase64encodedStringrepresentationforcomparison.Thisistheeasiestwaytocomparebinarymessagecontentwithanexpectedmessagepayload.Seevalidation-binaryhowthisisworkingforyou.

RMIsupport

RemotemethodinvocationisastandardJavatechnologyandAPIforcallingmethodsonremoteobjectsacrossdifferentJVMinstances.AlthoughRMIhaslostitspopularityitisstillusedinlegacycomponents.TestingRMIbeaninvocationisahardthingtodo.NowCitrusprovidesclientandserversupportforremoteinterfaceinvocation.Seermifordetails.

JMXsupport

SimilartoRMIJMXcanbeusedtoconnecttoremotebeaninvocation.ThistimeweexposesomebeanstoamanagedbeanserverinordertobemanagedbyJMXoperationsforreadandwrite.WithCitrus2.5wehaveaddedaclientandserversupportforcallingandprovidingmanagedbeansonambeanserver.Seejmxfordetails.

CitrusReferenceGuide

532Changes2.5

Page 533: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Resourceinjection

With2.5wehaveaddedmechanismsforinjectingCitruscomponentstoyourJavaDSLtestmethods.ThisisveryusefulwhenneedingaccesstotheCitrustestcontextforinstance.Alsoweareabletousenewinjectionoftestdesignerandrunnerinstancesinordertosupportparalleltestexecutionwithmultiplethreads.Seetheexplanationsintestcase-resource-injectionandtestcase-context-injection.

Httpx-www-form-urlencodedmessagevalidator

HTMLformdatacanbetransmittedwithdifferentmethodsandcontenttypes.Oneofthemostcommonwaysistousex-www-form-urlencodedformdatacontent.Asvalidationcanbetrickywehaveaddedaspecialmessagevalidatorforthat.Seehttp-www-form-urlencodedfordetails.

Daterangevalidationmatcher

Addedanewvalidationmatcherimplementationthatisabletocheckthatadatevalueisbetweenacertaindaterange(fromandto)Thedaterangeisabletofocusondaysaswellasadditionaltime(hour,minute,second)specifications.Seevalidation-matcher-daterangefordetails.

Readfileresourcefunction

Anewfunctionimplementationoffersyouthepossibilitiestoreadfileresourcecontentsasinlinedata.Thefunctioniscalledandreturnsthefilecontentasreturnvalue.Thefilecontentisthenplacedrightwherethefunctionwascallede.g.insideofamessagepaylaodelementorasmessageheadervalue.Seefunctions-read-filefordetails.

Timercontainer

Thenewtimertestactioncontainerrepeatsitsexecutionbasedonatimeexpression(e.g.every5seconds).Withthistimerwecanrepeattestactionswithafixedtimedelayorconstantlyexecutetestactionswithtimeschedule.Seecontainers-timerandactions-stop-timerfordetails.

UpgradetoVert.x3.2.0

CitrusReferenceGuide

533Changes2.5

Page 534: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

TheVert.xmodulewasupgradedtouseVert.x3.2.0version.TheCitrusmoduleimplementationwasupdatedtoworkwiththisnewVert.xversion.LearnmoreabouttheVert.xintegrationinCitruswithvertx.

CitrusReferenceGuide

534Changes2.5

Page 535: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.4Citrus2.4comeswithasetofnewfeaturesespeciallyregardingApacheCamelandDockerintegrations.Bugfixesofcoursearealsopartofthepackage.Seethefollowingoverviewonwhathaschanged.

Dockersupport

DockerandMicroservicesarefrequenttopicsinsoftwaredevelopmentrecently.WehaveaddedinteractionwithDockerinCitrussotheusercanmanageDockercontainerswithinatestcase.CitrusnowprovidesspecialDockertestactionsforbuilding,starting,stoppingandinspectingDockerimagesandcontainersinatest.Seedockerfordetails.

HttpRESTactions

WehavesignificantlyimprovedtheHttpRESTsupportinCitrus.ThefocusisonsimplifyingtheHttpRESTusageinCitrustestcases.WithnewHttpspecifictestactionsonclientandserverwecansendandreceiveHttpRESTmessagesveryeasy.Seehttpfordetails.

Waittestaction

Withthenewwaittestactionwecanexplicitlywaitforsomeremoteconditiontobecometrueinsideofatestcase.TheconditionssupportedatthemomentareHttpurlrequestsandfilebasedconditions.AusercaninvokeaHttpserverurlandwaitforittoreturnasuccessHttp200OKresponse.Thisisanawesomefeaturewhenwaitingforaservertostartupbeforethetestcontinues.WecanalsothinkofwaitingforaDockercontainertostartupbeforecontinuing.Oryoucanwaituntilafileispresentonthelocalfilesystem.Seeactions-waitfordetails.

Camelactions

CitrushasalreadyhadsupportforApacheCamelroutesandCamelcontextloading.Nowwith2.4versionwehaveaddedsomespecialApacheCameltestactionsforinteractingwithaCamelcontextanditsroutes.ThisenablesthetestertocreateanduseanewCamelrouteontheflyinsideatestcase.AlsoCitrusisnowabletointeractwith

CitrusReferenceGuide

535Changes2.4

Page 536: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

theCamelcontrolbusaccessingroutestatisticsandstatusinformation.Alsopossiblearestart,stop,suspend,resumeoperationsonaCamelroute.Seecamel-actionsandcamel-controlbusfordetails.

Purgeendpointsaction

PurgingJMSqueuesandinmemorychannelsattestruntimehasbecomeawidelyusedfeatureespeciallywhenaimingtomaketestsmorestableintermsofindependenttests.Wehaveaddedapurgeendpointtestactionthatworksonanyconsumerendpoint.Soyoudonotneedtoseparatebetweenendpointimplementationsanymoreandmoreimportantyoucanpurgeserverinmemorychannelcomponentsveryeasy.Seeactions-purge-endpointsfordetails.

ReleasetoMavenCentral

Thisisnotanewfeaturebutalsoworthtotellhereasitisasignificantimprovementonthewholeframeworkproject.WecannowreleasetheCitrusartifactstoMavencentralrepository.Soyoudonotneedtheadditionallabs.consol.derepositoryinyourMavenPOManymore.Thelabs.consol.derepositorywillcontinuetoexistthoughaswewillreleaseSNAPSHOTversionsofCitrushereinfuture.

CitrusReferenceGuide

536Changes2.4

Page 537: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.3WewanttogiveyouashortoverviewofwhathaschangedinCitrus2.3.Thereleaseaddssomenewfeaturesandimprovementstothebox.Bugfixesofcoursearealsopartofthepackage.Seethefollowingoverviewonwhathaschanged.

Testrunnerandtestdesigner

OneofthebiggestissueswiththeCitrusJavaDSListhefactthattheCitrusJavaDSLmethodsfirstbuildthewholetestcasetogetherbeforetheactualexecutiontakesplace.SocallingaJavaDSLmethodsendforinstancejustpreparesthesendingtestaction.Theactualsendingofthemessagetakesplacetoalatertimewhenalltestactionsaresetupandthetestcaseisreadytorun.ThisseparationofdesigntimeandruntimeofatestcaseleadstomisunderstandingsasaJavadeveloperisusedtoworkwithstatementsandmethodcallsthatperformimmediately.BasedonthatthemixtureofCitrusJavaDSLmethodcallsandnormalJavacodelogicinyourtestmayhaveleadtounexpectedbehavior.FollowingfromthatwedecidedtorefactortheJavaDSLmethodexecution.TheresultisanewTestRunnerconceptthatexecutesallJavaDSLmethodcallsimmediately.TheoldwayofbuildingthewholetestcasebeforeexecutionisrepresentedwithTestDesignerconcept.Sobothworldsarenowavailabletoyou.Seetestcasefordetails.

WebSocketsupport

TheWebSocketmessageprotocolbuildsontopofHttpstandardandbringsbidirectionalcommunicationtotheHttpclient-serverworld.WiththisreleaseCitrususersareabletosendandreceivemessageswithWebSocketconnections.TheHttpserverimplementationisnowabletodefinemultipleWebSocketendpoints.ThenewCitrusWebSocketclientisabletopublishmessagestotheserverviabidirectionalWebSocketprotocol.Seehttp-websocketfordetails.

JSONPathsupport

CitrusisabletoworkwithXpathexpressionsinseveralfieldswithinthetestingdomain(overwriteelements,ignoreelements,extractvaluesfrompayloads).NowthissupportofmanipulatingmessagepayloadsviaexpressionsisextendedwithJSONPath.SimilartoXpaththeJSONPathexpressionstatementsenableyoutofindelementsandvalues

CitrusReferenceGuide

537Changes2.3

Page 538: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

withinamessagepayload.NotverysurprisingtheJSONPathexpressionsworkwithJsonmessagepayloads.Withthenewreleaseyoucanoverwrite,ignoreandmanipulateJsonelementsusingJSONPathexpressions.Seejson-pathfordetails.

Customizemessagevalidators

TheframeworkoffersseveralmessagevalidatorimplementationsfordifferentmessageformatslikeXML,JSON,plaintextandsoon.InadditiontothatCitrushasasetofGroovyscriptmessagevalidators.AllthesevalidatorimplementationsareactivebydefaultsoyouareabletovalidateincomingmessagesaccordinglyinCitrus.Nowwiththisreleaseweaddedamorecomfortablewayofchangingtheframeworkvalidationfunctionality,particularwhenaddingnewcustomizedmessagevalidatorimplementations.Seevalidationfordetails.

Libraryupgrades

WehaveupgradedtheversionsofthemajordependencylibrariesofCitrus.ThisincludesTestNG,JUnit,SpringFramework,SpringWS,SpringIntegration,ApacheCamel,Arquillian,Jettyandmore.SoCitrusisnowworkingwithup-to-dateversionsofthewholemessagingandmiddlewareintegrationgang.

UpgradefromCitrus2.2

AlongwithnewfeaturesandimprovementswerefactoredandchangedsomepartsofCitrussoyoumighthavetosetthingsstraightwhenupgradingto2.3.Seethefollowinglistofthingsthatmightbebroughtuptoyou:

@CitrusTestannotation:Wehavemovedthe@CitrusTestannotationtoamorecommonpackage.Theoldpackagewascom.consol.citrus.dsl.annotations.CitrusTest.Thenewpackageiscom.consol.citrus.annotations.CitrusTest.SoyouhavetochangetheJavaimportstatementsinyourTestclasseswhenupgrading.

TestResult:WechangedtheTestResultinstantiationwhengeneratingthetestreports.TheTestResultclassnowworkswithstaticinstantiationmethodsforsuccess,skippedandfailedtests.Thisonlyaffectsyourcodewhenyouhavecreatedcustomtestreporters.

CitrusReferenceGuide

538Changes2.3

Page 539: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusTestBuilderdeprecation:AmajorrefactoringwasdoneintheTestBuilderJavaDSLcode.com.consol.citrus.dsl.TestBuilderandallitssubclassesweremarkedasdeprecatedandwilldisappearinnextversions.Soinsteadwenowsupportcom.consol.citrus.dsl.design.TestDesignerwhichbasicallyoffersthesamefunctionalityasformerTestBuilder.InadditionthatrefactoringbroughtanewwayofexecutingtheJavaDSLtestcases.Insteadofbuildingthewholetestcasebeforeexecutionisdoneasawholeyoucannowusethecom.consol.citrus.dsl.runner.TestRunnerimplementationinordertoexecuteeachtestactionintheJavaDSLimmediately.ThisisamoreJavalikewayofwritingCitrustestcasesasyoucanmixCitrustestactionexecutionwithnormalJavastatementsasusual.Readmoreaboutthenewapproachintestcase

Bugfixes

Bugsarepartofoursoftwaredevelopersworldandfixingthemispartofyourdailybusiness,too.FindingandsolvingissuesmakesCitrusbettereveryday.ForadetailedlistingofallbugfixespleaserefertothecompletechangeslogofeachreleaseinJIRA(http://www.citrusframework.org/changes-report.html).

CitrusReferenceGuide

539Changes2.3

Page 540: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.2Citrus2.2isareleasemostlyaddingnewfeaturesaswellasimprovementstogivenCitrusfeatures.Bugfixesofcoursearealsopartofthepackage.Seethefollowingoverviewonwhathaschanged.

Arquilliansupport

ArquillianisawellknownintegrationtestframeworkthatcomeswithagreatfeaturesetwhenitcomestoJavaEEtestinginsideofafullqualifiedapplicationserver.WithArquiliianyoucandeployyourJavaEEservicesinarealapplicationserverofyourchoiceandexecutethetestsinsidetheapplicationserverboundaries.ThismakesitveryeasytotestyourJavaEEservicesinscopewithproperJNDIresourceallocationandotherresourcesprovidedbytheapplicationserver.CitrusisabletoconnectwiththeArquilliantestcase.SpeakinginmoredetailyourArquilliantestisabletouseaCitrusextensioninordertousetheCitrusfeaturesetinsidetheArquillianboundaries.Seearquillianfordetails.

JUnitsupport

CitrussupportsbothmajorplayersinunittestingTestNGandJUnit.UnfortunatelywedidnotofferthesamefeaturesupportforJUnitasitwasdoneforTestNG.NowwithCitrus2.2weimprovedtheJUnitsupportinCitrussoyouareabletouseallfeatureswithbothframeworks.Thisisespeciallyrelatedtousingthe@CitrusTestand@CitrusXmlTestmethodannotationsintestclasses.Seerun-junithowitworks.

Start/Stopserveraction

CitruswasmissingadedicatedtestactiontostartandstopCitrusservercomponentsattetruntime.Withthenewlyaddedtestactionsyouareabletostartandstopservercomponentsasyoulikewithinyourtestcase.Seeactions-manage-serverwithadetaileddescription.

CitrusAnttasks

CitrusReferenceGuide

540Changes2.2

Page 541: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

WediscontinuetosupporttheCitrusAnttasks.TheAnttaskswerenotverystableanlackedfullfeaturesupportwhenexecutingtestcaseswithJUnitinApacheAnt.InsteadweaddedabriefdescriptiononhowtoexecuteCitrustestswiththewelldocumentedandstabledefaultJUnitandTestNGanttasks.Seesetup-using-anthowitworks.

Bugfixes

Bugsarepartofoursoftwaredevelopersworldandfixingthemispartofyourdailybusiness,too.FindingandsolvingissuesmakesCitrusbettereveryday.ForadetailedlistingofallbugfixespleaserefertothecompletechangeslogofeachreleaseinJIRA(http://www.citrusframework.org/changes-report.html).

CitrusReferenceGuide

541Changes2.2

Page 542: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.1Citrus2.1addssomeenhancementstotheCitrusfeaturesetaswellasbugfixesandimprovements.Seethefollowingoverviewonwhathaschanged.

SOAPMTOMsupport

SOAPMTOMstandsforMessageTransmissionOptimizationMechanismwhichallowsyoutosendandreceivelargeSOAPattachmentcontentsstreamedwithoptimizedresourceallocationonserverandclient.Manythankstocommunitycontributions(github/stonator)thatmadethishappenwithCitrusSOAPclientandserver.AsauseryoucanshoosetosendandreceiveSOAPattachmentswithMTOMoptimization.Seesoap-attachment-mtomfordetails.

SOAPenvelopehandling

InitsdefaultbehaviorCitruswillremovetheSOAPenvelopeforincomingSOAPrequestsjustprovidingtheSOAPbodyasmessagepayload.Thisismorestraightforwardinatestcasetoperformfurthervalidationsteps.HoweveritmightbemandatorytoseethewholeSOAPenvelopeinsidethetestcaseforspecialvalidation.AsauseryoucannowchoosehowtohandleincomingSOAPenvelopebydefinigthekeep-soap-envelopesettingontheCitrusSOAPservercomponents.Seesoap-keep-envelopefordetails.

SOAP1.2messagefactory

TheCitrusSOAPservercomponentwasmissingasettingfortheSOAPmessagefactorytouse.TheSOAPmessagefactoryimplementationdecideswhichSOAPversiontouse1.1or1.2.NowyoucansetthemessagefactoryontheservercomponentanddefinetheSOAPversiontouse.Seesoap-12fordetails.

TestNGdataproviderhandling

WeimprovedtheTestNGdataproviderhandlinginCitrus.NowyoucanusetheusualTestNGdataproviderannotationsinyourtestmethods.TestNGwillcalltheCitrustestcaseseveraltimeswithrespectiveparametersprovidedastestvariables.Thisreplaces

CitrusReferenceGuide

542Changes2.1

Page 543: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

theoldcitrusDataProvidermechanismthattriedtomakethingsworkinginakindofworkaround.Thenewproviderhandlingalsosupportsmultipledataprovidersinatestclass.run-testng-data-providersdescribeshowthisisworkingforyou.

Mailmessagenamespace

TheCitrusmailcomponentsenablemessageexchangeasmailclientandserver.ForvalidationpurposethecomponentsofferaXMLmailmessagerepresentation.Wehaveaddedatargetnamespacexmlns="http://www.citrusframework.org/schema/mail/message"andaXSDschemaforthisXMLmailmessagerepresentation.FromnowonyouhavetousethenamespaceaccordinglyinyourmailmessagepayloadswhensendingandreceivingmailmessagesinCitrus.SeemailhowtousethenewXMLmailmessagenamespace.

Sshmessagenamespace

WhensendingandreceivingmessagesviasshCitrusprovidesaXMLrepresentationforrequestandresponsedata.Thesesshmessagesfollowanewtargetnamespacexmlns="http://www.citrusframework.org/schema/ssh/message"andaXSDschema.ThismeansyouhavetousethenamespaceaccordinglyinyoursshmessagepayloadswhensendingandreceivingsshmessagesinCitrus.Seesshforfurtherdetails.

CitrusReferenceGuide

543Changes2.1

Page 544: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus2.0Citrus2.0isamajorversionupgradeandthereforebigthingsshouldbehappening.InthefollowingsectionsweshortlydescribetheCitrusevolution.WewantyoutogetaquickoverviewofwhathashappenedandallthenewthingsinCitrus.Sohopefullyyoucanspotyourfavoritenewfeature.

Refactoring

InCitrus1.4webegantorefactortheconfigurationcomponentsinCitrus.ThisrefactoringwasfinalizedinCitrus2.0whichmeansthatalldeprecatedclassesandapiarenolongersupported.Theclasseswereremovedsoyougetcompilationerrorswhenusingthoseoldstuff.Ifyoustillusetheoldconfigurationseethishttp://citrusframework.org/migration-sheet.htmlinordertolearnhowtoupgradetothenewconfiguration.Itisworthtodoso!Inadditiontothatwedidrefactoringinfollowingfields:

ReplymessagecorrelationInsynchronouscommunicationscenariosCitrusoptionallycorrelatedmessagesacrosssendandreceivetestactions.Indefaultsettingthemessagecorrelationwasdisabled.With2.0releasewechangedthisbehaviortotheopposite.Nowmessagecorrelationisdonebydefaultwithadefaultcorrelationalgorithm.SoincaseyouusedtheDefaultReplyMessageCorrelatorinCitrusbeforeyouwillnothavetodosoinfutureasthisisdonebydefault.Themessagecorrelationgivesusmorerobusttestsespeciallywhenexecutingtestsinparallel.Inthetestcaseyoudonothavetodoanythingforpropermessagecorrelation.

CitrusmessageAPIWehaverefactoredtheCitrusmessageAPItousecustommessageobjectsinendpoints,consumersandproducers.Thishasnoaffectonyourtestsorconfigurationunlessyouhavewrittenendpointextensionsorcustomendpointsonyourown.Youmighthavetorefectoryourcodeaccordingly.HavealookattheCitrusendpointimplementationsinordertoseehowthenewmessageAPIworksforyou.

SleeptimeinmillisecondsThisissomethingthatwedefinitelycarryaroundsincethebeginningofCitrus.Thetimevaluesinsleeptestactionweredoneinseconds,whichisinconvenientwhenusingtimeperiodsbelowonesecondornonnatural

CitrusReferenceGuide

544Changes2.0

Page 545: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

numbers.Nowyoucanchoosetousemillisecondswhichismorelikelyhowyoushouldconfiguretimeperiodsanyway.Seeactions-sleepfordetails

AutosleeptimeinmillisecondsWeusedsecondswhenusingautosleepinrepeatonerrorcontainer.Thisledtothefactthatwewerenotabletosleeptimeperiodsbelowonesecond.Alsoitwasnotpossibletospecifynonnaturalnumberssuchas1.5secondsautosleeptime.Wechangedtomillisecondswhichismorelikelyhowyouareusedtoconfiguretimeperiodsanyway.Seecontainers-repeat-onerrorfordetails

Messagehandlervs.endpointadapterInpreviousreleasespriorto1.4wehadmessagehandlersonserversidethatwereabletoforwardincomingrequeststomessagechannelsorjmsdestinations.Theoldmessagehandlerimplementationswereremovedin2.0.Insteadyoushouldusetheendpoint-adapterimplementations.Seeendpoint-adapterhowthatworks.

XMLendpointreferenceattributeInaXMLtestcaseyoureferencethemessageendpointinthesendandreceiveactionswithaspecialattributecalledwith.Thisattributeisnolongersupportedandwasremoved.InsteadyoushouldusetheendpointattributewhichwasintroducedinCitrus1.4andhastheexactsamefunctionality.

Removedcitrus-adaptermoduleTheMavenmodulecitrus-adapterwasremoved.ClassesandAPImovedtocitrus-coremodule.ForendpointadaptersdousethenewconfigurationcomponentsthatwereintroducedinCitrus1.4.Seeendpoint-adapterfordetails.

WebServiceEndpointclassrenamedIntermsofpackagerefactoringthecom.consol.citrus.ws.WebServiceEndpointwasrenamedtocom.consol.citrus.ws.server.WebServiceEndpoint

Springframework4.x

IntermsofupgradingtheCitrusAPIdependenciesweintroducedSpring4.xversions.ThisincludesthecoreSpringframeworklibrariesaswellastheSpringIntegrationandSpringWebServiceprojectartifacts.SowiththemajorversionupgradelotsofAPIchangeswerealsodoneinCitruscodeinordertomeetthenewSpring4.xAPI.SowerecommendforyoutoalsouseSpring4.xversioninyourCitrusprojects.

FTPsupport

CitrusReferenceGuide

545Changes2.0

Page 546: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NewmemberoftheCitrusfamilydealswithFTPconnectivity.Thenewcitrus-ftpmoduleprovidesaneatftpserverandclientimplementationsoyoucansendandreceivemessagesvieFTPmessagetransport.ftpdescribesthenewfunctionalityindetail.

Functionswithtestcontextaccess

Functionsarenowabletoaccessthetestcontext.Thisenablesyoutoaccessalltestvariablesandothercentraltestrelatedcomponentsinafunctionimplementation.ThereforethefunctionJavainterfacehasnowanadditionaltestcontextparameter.Refactoryourcustomwrittenfunctionsaccordinglytomeetthenewinterfacerules.Seehttp://www.citrusframework.org/tutorials-functions.htmlfordetails.

Validationmatcherwithtestcontextaccess

Justlikefunctionsnowvalidationmatchersareabletoaccessthetestcontext.Thisenablesyoutoaccessalltestvariablesandothercentraltestrelatedcomponentsinavalidationmatcherimplementation.ThevalidationmatcherJavainterfacehaschangedaccordinglywithanadditionaltestcontextparameter.Refactoryourcustomwrittenmatcherimplementationaccordinglytomeetthenewinterfacerules.

Messagelistenerwithtestcontextaccess

Messagelistenersdonowalsohaveaccesstothetestcontext.Thisismorepowerfulasyoucanaccesstestvariablesandothercentralcomponentswithinthetestcontext.

SOAPoverJMS

SOAPoverJMSwassupportedinCitrusfromtheverybeginning.UnfortunatelyyouhadtoalwaysspecifythewholeSOAPenvelopeinyourtestcase.SOAPenvelopehandlingisnowdoneautomaticallybyCitruswhenusingthenewSoapJmsMessageConverter.TheconvertertakescareonconstructingaproperSOAPenvelopemessage.Seejms-soapfordetails.

MultipleSOAPattachments

WhensendingandreceivingSOAPmessageswithCitrusasclientorserveryoucanaddonetomanyattachmentstothemessage.Beforeitwasonlypossibletohaveonesingleattachmentinamessage.NowyouhavenolimitsindefiningSOAPattachments.

CitrusReferenceGuide

546Changes2.0

Page 547: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Seesoap-webservicesfordetails.

MultipleSOAPXMLheaderfragments

TheSOAPheadercanholdmultipleXMLheaderfragmentswithdifferentnamespacesandcontent.WithCitrus2.0youareabletoconstructsuchaSOAPmessagewithmultipleheadercontents.Seesoap-webservicesfordetails.

Createvariablevalidationmatcher

Anewvalidationmatcherimplementationisabletocreateanewvariableonthefly.Theactualfieldnameisusedasvariablenameandtheelementvalueasvariablevalue.Thevariablenamecanslobecustomizedwithoptionalvalidationmatcherparameter.ThisisagreatalternativetotheXPathexpressionevaluatingvariableextraction.AlsoveryhandsometousethisvalidationmatcherinJsonmessagepayloads.Seevalidation-matcher-variablefordetails.

Newconfigurationcomponents

AmajorpartoftheCitrusconfigurationisdoneinaSpringbeanapplicationcontext.CentralCitruscomponentsandfeaturesareaddedasSpringbeanstotheapplicationcontext.NowwithCitrus2.0wehaveaddedspecialconfigurationcomponentsforalmostallfeatures.ThismeansthatyoucaneasilyaddconfigurationusingthenewXMLschemacomponents.Seewhichcomponentsareavailable:

FunctionlibraryCustomfunctionlibrarieswithcustomfunctionimplementationsarenowconfiguredwiththefunction-libraryXMLschemacomponentsintheSpringapplicationcontextconfiguration.Seefunctionsfordetails.

ValidationmatcherlibraryCustomvalidationmatcherimplementationsarenowconfiguredwiththevalidation-matcher-libraryXMLschemacomponentsintheSpringapplicationcontextconfiguration.Seevalidation-matchersfordetails.

DatadictionaryDatadictionariesapplytoallmessagessendandreceivedintestcases.Youcandefinemultipledictionariesusingthedata-dictionaryXMLschemacomponentsintheSpringapplicationcontextconfiguration.Seedata-dictionaryfordetails.

CitrusReferenceGuide

547Changes2.0

Page 548: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

NamespacecontextConfigurationofaglobalnamespacecontextisnecessaryforXMLmessagepayloadsandXPathexpressionsusedinthetestcases.Thenamespace-contextXMLschemacomponentisusedintheSpringapplicationcontextconfigurationandsimplifiestheconfiguration.Seexpathfordetails.

Before/aftersuitecomponents

Whenexecutingtestactionsbeforetheactualtestrunyoucanusethesequencebeforesuitecomponents.WehaveimprovedthesecomponentstouseaspecialXMLschema.Thisenableseasyconfigurationofbothbeforeandaftersuiteactions.Inadditiontothatyoucanbindthesuiteactionstospecialpackages,testnamesorsuitenames.Soyoucannowhavemorethanonesequencebeforesuiteatthesametime.Accordingtotheenvironmentsettingsthebeforesuiteactionsareexecutedorleftout.Lastnotleastwehavedonethesameimprovementtothebeforetestactionsandwehaveintroducedaaftertestsequencecomponentforexecutionaftereachtest.Seehowthisisdoneintestsuite.

CitrusJMSmodule

JMSsupporthasbeenamajorpartofCitrusfromtheverybeginning.UptonowtheJMSfeatureswerelocatedincitrus-coreMavenmodule.WithCitrus2.0weintroducedaseparatecitrus-jmsMavenmodule.ThismeansthatyoumighthavetoaddproperMavendependencyofthisnewmoduleinyourexistingprojectwhenusingJMS.Seehowthisisdoneinjms.

CitrusReferenceGuide

548Changes2.0

Page 549: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus1.4.x

Refactoring

ItwastimeforustodosomecoderefactoringinCitrus.ManyusersstruggledwiththeconfigurationoftheCitruscomponentsandprojectsetupwastooverboseinsomeareas.ThisiswhywetriedtoimprovethingswithworkingoverthebasicconceptsandcomponentsinCitrus.

TheoutcomeisanewCitrus1.4whichhasnewconfigurationcomponentsforsendingandreceivingmessages.AlsotheclientandservercomponentsforHTTPandSOAPhavechangedintermsofsimplification.Unfortunatelyrefactoringcomesalongwithcodedeprecation.Thisiswhyyouhavetoalsochangeyourprojectcodeandconfigurationinthefuture.ThisisespeciallywhenyouhavemadecodeadjustmentsandextensionstotheCitrusAPI.

ThegoodnewsnowisthatwithCitrus1.4botholdandnewconfigurationworksfine,soyoudonothavetochangeyourexistingprojectconfigurationwhencomingfromCitrus1.3.xandearlierversions.ButthereisalotofcodemarkedasdeprecatedinCitrus1.4.HavealookatwhathasbeenmarkedasdeprecatedandupdateyourcodetousethenewAPI.

WehavesetupamigrationsheetforuserscomingfromCitrus1.3.xandearlierversionsinordertofindaquickoverviewofwhathaschangedandhowtousethenewconfigurationcomponents:http://citrusframework.org/migration-sheet.html

Datadictionaries

Datadictionariesdefinedynamicplaceholdersformessagepayloadelementvaluesingeneralmanner.Intermsofsettingthesamemessageelementacrossalltestcasesandalltestactionsthedictionaryprovidesaneasykey-valueapproach.

WhendealingwithanykindofmessagepayloadCitruswillaskthedatadictionaryforpossibletranslationofthemessageelementscontained.ThedictionarykeysdomatchtoaspecificmessageelementdefinedbyXPathexpressionordocumentpathexpressionforinstance.TherespectivevalueisthensetonallmessagesinCitrus(inboundandoutbound).

CitrusReferenceGuide

549Changes1.4

Page 550: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

DictionariesdoapplytoXMLorJSONmessagedataandcanbedefinedinglobalorspecificscope.Findoutmoredetailedinformationaboutthistopicindata-dictionary

Mailadapter

Withthenewmailadapteryouareabletobothsendandreceivemailmessageswithinatestcase.ThenewCitrusmailclientproducesamailmimepartmessagewithusualmailheadersandatextbodypart.Optionalattachmentpartsaresupported,too.

OntheserversideCitrusprovidesaSMTPservertoacceptclientmailmessages.Theincomingmailmessagescanhavemultipletextpartsandattachmentparts.AsusualyoucanvalidatetheincomingmailmessagesregardingheadersandpayloadwiththewellknownvalidationcapabilitiesinCitrus.

Readmoreaboutthenewmailmoduleinmail

Endpointadapter

EndpointadaptershelptocustomizethebehaviorofaCitrusserversuchasHTTPorSOAPwebservers.Theendpointadapterisresponsibleofcreatinganendpointthatrespondstoinboundrequests.YoucancustomizethebehaviorsotheCitrusserverhandlesincomingrequestsasyoulike.

BydefaulttheCitrusserverusesachannelendpointadaptersoincomingmessagesgetforwardedtoaninmemorymessagechannel.Thereareseveralotherimplementationsavailableasendpointadapter.Readmoreaboutthatinendpoint-adapter

Globalvariablescomponent

WeaddedaglobalvariablesXMLconfigurationcomponentformorecomfortableusageinbasicSpringapplicationcontextconfiguration.ThecomponentisabletocreatenewglobalvariablesthatarevalidacrossallCitrustestcases.Thiscanalsobedonebyloadingapropertyfilefromanexternalfileresource.Findouthowtousitintestcase-global-variables

Jsontextvalidatormode

TheJsontextvalidatorisnowabletooperateintwodifferentmodes.Thestrictmodeisthedefaultmodeandvalidationincludesalsoastrictcheckonallsub-objectsandJSONarrayelements.Soifthereisanobjectmissingthevalidationwillfailimmediately.

CitrusReferenceGuide

550Changes1.4

Page 551: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

SometimesitmaybeaccuratetoonlyvalidateasubsetofallJSONobjectsinthedatastructure.Thereforethenon-strictmodedoesnotcheckonobjectattributecounts.Seemoredescriptioninvalidation-json

HTTPRESTspecificJavaDSLoptions

WhensendingandreceivingHTTPmessagesonRESTAPIsyoucannowuseinterfacespecificoptionsintheJavaDSL.Thisreferstorequesturi,contextpath,queryparametersandHTTPstatuscodesforinstance.WiththisenhancementyouarenowmorecomfortableinhandlingRESTAPIcallsinCitrus.Findouthowtousitinhttp

SOAPHTTPvalidation

WhilereceivingSOAPmessagesoverHTTPwearenowabletoalsoverifytheusedHTTPuri,context-pathandqueryparameters.YoucanexpectclientstousethosevaluesinyourreceiveactionasyouwoulddoinnormalHTTPcommunicationwithinCitrus.ThiscompletestheHTTPservervalidationwhenusingSOAPoverHTTP.Readmoreaboutitinsoap-webservices

ApacheCamelintegration

ApacheCamelisagreatenterpriseintegrationplatformthatimplementstheenterpriseintegrationpatternsforbuildingpowerfulmediationandroutingrulesformessagebasedintegrationapplications.WiththenewsupportforcamelendpointsinCitrusyoucaninteractwithApacheCamelcomponentsforsendingandreceivingmessages.ApacheCameloffersafinesupportfordifferentmessagetransportsthatnowcanbeusedinCitrusalso.InadditiontothatyoucanputyourCamelapplicationtothetestwithloadingoftheApacheCamelcontextwithallyourroutedefinitions.Citrusisabletointeractwiththeseroutesinasynchronousandsynchronouscommunicationscenarios.Readaboutitincamel.

Vert.xintegration

Vert.xisaverypowerfulapplicationplatformthatprovidesscalablemessagingforseveralmessagetransportssuchasHTTP,WebSockets,TCPandmore.Vert.xalsohasadistributedeventbusthatconnectsmultipleVert.xinstancesacrossthenetwork.WithCitrus1.4theVert.xplatformisintegratedwithCitruseventbusendpoints.Soyou

CitrusReferenceGuide

551Changes1.4

Page 552: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

canparticipateincommunicatingtotheVert.xeventbusfromCitrustestcase.ThisenablesyoutoaddautomatedintegrationteststotheVert.xplatform.Readaboutthatinvertx.

Dynamicendpointcomponents

EndpointsrepresentthebasecomponentinCitrusforsendingandreceivingmessages.TheendpointusuallyisdefinedinsidetheCitrusSpringapplicationcontextasSpringbeancomponent.Nowitisalsopossibletocreatedynamicendpointdefinitionsattestruntime.ThiscomesinveryhandywhenyoujustwanttosendorreceiveamessagewithCitrusasis.Youdonotneedtoaddthecompleteendpointconfigurationbutonlyuseaspecialendpointuripattern.Citruswillcreatetheendpointatruntimeautomatically.Learnhowtousethedynamicendpointpatterninendpoint-components.

CitrusReferenceGuide

552Changes1.4

Page 553: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus1.3.x

@CitrusTestand@CitrusXmlTestannotations

WiththenewJavaDSLcapabilitiesCitruscreatednewwaysofexecutingtestcaseswithinaTestNGorJUnittestclass.Nowweevenimprovedtheusageherewithtwonewannotations@CitrusTestandCitrusXmlTest.Theintegrationintotheunittestclasshasneverbeeneasierforyou.

ThenewCitrusannotationsgodirectlytoyourunittestmethods.WiththisenhancementyoucanhavemultipleCitrustestcasesinonesingleJavaclassandtheCitrustestsnowareabletocoexistwithotherunittestmethods.YoucanevenmixJavaDSLandXMLCitrustestcasesinasingleJavaclass.

TheXMLCitrustestscanbegroupedtoasingleJavaclasswithmultipleXMLfilesloadedduringexecution.ThereisevenapackagescanforallCitrusXMLfileswithinadirectorystructuresoyoudonothavetocreateaJavaclassforeachtestcaseanymore.

Wehavechangedthedocumentationinthisguidesoyoucanseehowtousethenewannotations.Fordetailedoverviewseerun-config-testng.AlsoseeourCitrusblogwherewecontinuouslydescribethemanypossibilitiesthatyouhavewiththenewannotations.

@CitrusParametersannotation

CitrusisabletousethefabulousTestNGdataprovidercapabilitiesinordertoexecuteatestcaseseveraltimeswithdifferentdataprovidedformexternalresources.Thenew@CitrusParametersannotationhelpstosetparameternameswhichareusedastestvariablenamesinthetestcase.

Schemarepositoryconfigurationcomponents

Definingschemarepositoriesandschemas(xsd,wsdl)iscommonuseinCitrus.WehaveaddedXMLbeandefinitionparserssodefiningthosecomponentsislessverbose.YoucanusetheCitruscitrus:schema-repositoryandcitrus:schemacomponentsinyourSpringapplicationcontextconfiguration.Thecomponentsdoreceiveseveralattributesforfurtherconfiguration.XSD,WSDLandschemacollectionsaresupportedhere.

CitrusReferenceGuide

553Changes1.3

Page 554: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Checkoutxsd-validationforexampleshowtousethenewconfigurationcomponents.

Changedatefunction

WehaveaddedanewCitrusfunctioncitrus:changeDate()thatisavailableforyoubydefault.Thefunctionchangesagivendatevalueaddingorremovingadatetimeoffset(e.g.year,month,day,hour,minute,second).Soyoucanmanipulateeachdatevaluealsothoseofdynamicnaturecomingwithsomemessage.

Seefunctions-changedateforexamplesanddetailedsyntaxusageofthisfunction.

Weekdayvalidationmatcher

Thenewweekdayvalidationmatcheralsoworksondatevalues.Thematcherchecksthatagivendatevalueevaluatestoaexpecteddayoftheweek.Sotheusercancheckthatadatefieldisalwaysasaturdayforinstance.Thisisveryhelpfulwhencheckingthatagivendatevalueisnotaworkingdayforexample.

Seevalidation-matcher-weekdayforsomemoredetaileddescriptionofthematcher'scapabilities.

JavaDSL

Citrususers,inparticularthosewithdevelopmentexperience,dooftentellmeaboutthenastyXMLcodetheyhavetodealwithforwritingCitrustestdefinitions.DeveloperswanttowriteJavacoderatherthanXML.AlthoughIpersonallydoliketheCitrusXMLtestsyntaxwehaveintroducedaJavaDSLlanguageforwritingCitrustestswithJavaonly.

WehaveintroducedtheJavaDSLtoallmajortestactionfeaturesinCitrussoyoucanswitchwithouthavingtoworryaboutloosingfunctionality.DetailscanbeseeninthetestactionsectionwhereweaddedJavaDSLexamplesalmosteverywhere(actions).ThebasicJavaDSLsetupisdescribedintestcase.

XHTMLmessagevalidation

MessagevalidationforHtmlcodewasnotreallycomfortableasHtmldoesnotconfirmtobewellformedandvalidXMLsyntax.XHTMLtriestoclosethisgapbyautomaticallyresolvingallHtmlspecificXMLsyntaxruleviolations.WithCitrus1.3weintroducedaXHTMLmessagevalidatorwhichdoesthemagicforconvertingHtmlcodetoproper

CitrusReferenceGuide

554Changes1.3

Page 555: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

wellformedandvalidXML.InatestcaseyoucanthenusethefullXMLvalidationpowerinCitrusinordertovalidateincomingHtmlmessages.Sectionvalidation-xhtmldealswiththenewvalidationcapabilitiesforHtml.

MultipleSOAPfaultdetailsupport

SOAPfaultmessagescanholdmanySOAPfaultdetailelements.CitruswasabletohandleasingleSOAPfaultdetailonsendingandreceivingtestactionsfromtheverybeginningbutmultipleSOAPfaultdetailelementswerenotsupported.FortunatelythingsaregettingbetterandyoucansendandreceiveasmanyfaultdetailelementsasyoulikeinCitrus1.3.ForeachSOAPfaultdetailyoucanspecifyindividualvalidationrulesandexpectations.Seesoap-faultsfordetaileddescription.

Jettyserversecurityhandler

WithourJettyservercomponentyoucansetupHttpmockserversveryeasy.TheserverisautomaticallyconfiguredtoacceptHttpclientconnectionsandtoloadaSpringapplicationcontextonstartup.Nowyoucanalsosetsomemoredetailsonthisautomaticserverconfiguration(e.g.servercontextpath,servletnamesorservletmappings).Inadditiontothatyoucanaccessthesecuritycontextofthewebcontainer.Thisenablesyoutosetupsecurityconstraintssuchasbasicauthenticationonserverresources.Clientsarethenforcedtoauthenticateproperlywhenaccessingtheserver.Unauthorizeduserswillget401accessdeniederrorsimmediately.Seehttp-basic-auth-serverfordetails.OfcoursethisalsoappliestoourSOAPWebServiceJettyservercomponents(soap-basic-auth-server).

Testactors

Weintroducedanewconceptoftestactorsforsendingandreceivingtestactions.Thisenablesyoutolinkatestactor(e.g.interfacepartnerapplication,backendapplication)toatestactioninyourtest.Followingfromthatyoucanenable/disabletestactorsandalllinkedtestactionsveryeasy.ThisenablesustoreuseCitrustestcasesinend-to-endtestscenarioswherenotallinterfacepartnersgetsimulatedbyCitrus.Ifyouliketoreadmoreaboutthisconceptfollowthedetailedinstructionintest-actors.

SimulateHttperrorcodeswithSOAP

CitrusReferenceGuide

555Changes1.3

Page 556: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

CitrusprovidesSOAPWebServicesserversimulationwithclientsconnectingtotheserversendingSOAPrequests.AsaserverCitrusisnowabletosimulateHttperrorcodeslike404Notfoundand500Internalservererror.BeforethattheCitrusSOAPserverhadtoalwaysrespondwithaproperSOAPresponseorSOAPfault.Seesoap-http-errorsfordetails.

SSHserverandclient

TheCitrusfamilyhasraisedanewmemberinaddingSSHconnectivity.WiththenewSSHmoduleyouareabletoprovideafullstackSSHserver.TheSSHserveracceptsclientconnectionsandyouasatestercansimulateanySSHserverfunctionalitywithpropervalidationasitisknowntoCitrusSOAPandHTTPmodules.InadditiontothatyoucanalsousetheCitrusSSHclientinordertoconnecttoanexternalSSHserver.YoucanexecuteSSHcommandsontheSSHserverandvalidatetherespectiveresponsedata.Thefulldescriptionisprovidedinssh.

ANTruntestaction

WiththisnewtestactionyoucancallANTbuildsfromyourtestcase.TheactionexecutesoneormoreANTbuildtargetsonabuild.xmlfile.YoucanspecifybuildpropertiesthatgetpassedtotheANTbuildandyoucanaddacustombuildlistener.IncasetheANTbuildrunfailsthetestfailsaccordinglywiththebuildexception.Seeactions-antrunfordetails.

Pollingintervalforreplyhandlers

WithsynchronouscommunicationinCitruswealwayshaveacombinationofasynchronousmessagesenderandareplyhandlercomponent.Thesetwoperformahandshakewhenpassingsynchronousreplymessagestothetestforfurtherprocessingsuchasmessagevalidation.Whilethesenderiswaitingforthesynchronousresponsetoarrivethereplyhandlerpollsforthereplymessage.Thispollingforreplymessageswasdoneinastaticwaywhichoftenledtotimedelaysaccordingtolongpollingintervals.NowwithCitrus1.3youcansetthepolling-intervalforthereplyhandlerasyoulike.ThissettingisvalidforallreplyhandlercomponentsinCitrus(citrus-jms,citrus-http,citrus-ws,citrus-channel,citrus-shh,andsoon).

Upgradingfromversion1.2

CitrusReferenceGuide

556Changes1.3

Page 557: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

IfyouarecomingfromCitrus1.2youmayhavetolookatthefollowingpointsinordertohaveasmoothupgradetothenewreleaseversion.

JettyversionupgradeWeareusingJettyalotforstartingHttpservermockswithinCitrus.InordertostayuptodateweupgradedtoJetty8.1.7versionwiththisCitrusrelease.ThisimpliesthatpackagenamesdidchangeforJettyAPI.IngeneralthereisnoconflictforyouasaCitrususer,butyoumaywanttoadjustyourloggingconfigurationaccordingtonewJettypackages.Jettypackagenamesdidchangefromord.mortbaytoorg.eclipse.jetty.

SpringversionupgradeStayinguptodatewiththeversionsof3rdlibrarydependenciesisquiteimportantforus.Soweupgradeourdependenciestonewerversionswitheachrelease.Aswedidonlyupgrademinorversionsthereisnosignificantchangeorproblemstobeexpected.HoweveryoumaytakecareonversionsandreleasechangesintheSpringworldfordetailsandmigration.

TIBCOmoduleWedecidedtoputtheTIBCOmoduleseparatelyasitisaveryspecialconnectivityadapterforTIBCOsoftwareonly.SoyouwillnotfindtheTIBCOmodulewithintheCitrusdistributionanymore.WewillmaintainaTIBCOconnectivityadapterseparatelyinthefuture.

CitrusReferenceGuide

557Changes1.3

Page 558: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

ChangesinCitrus1.2

Springversionupdate

WehavesomemajorversionupgradesinourSpringdependencies.WearenowusingSpring3.1.1,SpringIntegration2.1.2andSpringWS2.1.0.Thisupgradewasoverdueforsometimeandisdefinitelyworthit.WiththeseupgradeswehadtoapplysomechangesinourAPI,too.ThisisbecauseweareusingtheSpringclassesalotinourcode.Seetheupgradeguideinthischapterforallsignificantchangesthatmightaffectyourproject.

Newgroovyfeatures

CitrusextendedthepossibilitiestoworkwithscriptlanguageslikeGroovy.YoucanuseGroovy'sMarkupBuildertocreateXMLmessagepayloads.YourGroovycodegoesrightintothetestcaseorcomesfromexternalfile.WithMarkupBuilderyoudonotneedtocareaboutXMLmessagesyntaxandoverhead.Justfocusonthepuremessagecontent.Youcanreadthedetailsingroovy-markupbuilder.

FurtherGroovyfeaturegoestothevalidationcapabilities.InsteadofworkingwithXMLDOMtreecomparisonandXPathexpressionvalidationyoucanuseGroovyXMLSlurper.ThisisveryusefulforthoseofyouwhoneedtodocomplexmessagevalidationanddonotliketheXML/XPathsyntaxatall.WithXMLSlurperyoucanaccesstheXMLDOMtreevianamedclosureoperationswhichfeelsgreat.ThisespeciallycomesinhandyforcomplexgenericXMLstructuresasyoucansearchforelements,sortelementlistandusethepowerfulcontainsoperation.Thisisalldescribedingroovy-xmlslurper.

SomeotherGroovysupportextensioncomesinSQLresultsetvalidation(actions-database-groovy).WhenreadingdatafromthedatabasesomeoneisabletovalidationtheresultingdatarowsetwithGroovyscript.ThescriptcodeeasilyaccessestherowsandcolumnswithGroovy'sout-of-the-boxlistandmaphandling.Thisaddsverypowerfulvalidationtomulti-rowdatasetsfromthedatabase.

SQLmulti-lineresultsetvalidation

CitrusReferenceGuide

558Changes1.2

Page 559: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

InthisnewCitrusversionthetestercanvalidateSQLQueryresultsthathavemultiplerows.InthepastCitruscouldonlyhandleasinglerowintheresultset.Nowthisnewreleasebringslightintothedark.SeealsothenewGroovySQLresultsetvalidationwhichfitsbrilliantforcomplexmulti-rowSQLresultsetvalidation.Thedetailscanbefoundinactions-database-query

Extendedmessageformatsupport

InpreviousversionsCitruswasprimarydesignedtohandleXMLmessagepayloads.WiththisnewreleaseCitrusisalsoabletoworkwithothermessageformatssuchasJSON,CSV,PLAINTEXT.Thisappliestosendingmessagesaswellasreceivingandparticularlyvalidatingmessagepayloads.ThetestercanspecifyseveralmessagevalidatorsinCitrusfordifferentmessageformats.Accordingtotheexpectedmessageformatthepropervalidatorischosentoperformthemessagevalidation.

WehaveimplementedaJSONmessagevalidatorcapableofignoringspecificJSONentriesandhandlingJSONArrays.Wealsoprovideaplaintextmessagevalidatorwhichisverybasictobehonest.Theframeworkisreadytoreceivenewvalidatorimplementationssoyoucanaddcustomvalidatorsforyourspecificneeds.

NewXMLfeatures

XMLnamespacehandlingistediousexpeciallyifyouhavetodealwithalotofXPathexpressionsinyourtests.BeforeyouhadneedtospecifyanamespacecontextfortheXPathexpressioneverytimeyouusetheminyourtest-nowyoucanhaveacentralnamespacecontextwhichdeclaresnamespacesyouuseinyourproject.Thesenamespacesidentifiedbysomeprefixareavailablethroughoutthetestprojectwhichismuchmoremaintainableandeasytouse.Seehowitworksinxpath-namespace.

SOAPsupportimprovements

WsAddressingstandardisnowsupportedinCitrus(soap-ws-adressing).ThismeansyoucandeclarethespecificWsAddressingmessageheadersonmessagesenderlevelinordertosendmessageswithWsAddressingfeature.TheheaderisconstructedautomaticallyforallSOAPmessagesthataresentoverthemessagesender.

DynamicSOAPendpointuriresolverenablesyoutodynamicallyaddressSOAPendpointsduringatest.SometimesamessagesendermaydynamicallyhavetochangetheSOAPurlforeachcall(e.g.addressdifferentrequesturiparts).Withaendpointuri

CitrusReferenceGuide

559Changes1.2

Page 560: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

resolversetonthemessagesenderyoucanhandlethisrequirementveryeasy.Thetipfordynamicendpointresolvingwasaddedtosoap-sender

WealsosimplifiedthesynchronousSOAPHTTPcommunicationwithintestcases.InpreviousversionsyouhadtobuildcomplexparallelandsequentialcontainerconstructsinordertocontinuewithtestexecutionwhiletheSOAPmessagesenderiswaitingforthesynchronousresponsetoarrive.NowyoucansimplyforkthemessagesendingactionandcontinuewithfurthertestactionswhilesynchronousSOAPcommunicationtakesplace.Seethesoap-fork-modefordetails

SomereallysmallchangeintroducedwiththisreleaseisthefactthatCitrusnowlogsSOAPmessagesintheirpurenature.ThismeansthatyoucanseethecompleteSOAPenvelopeofmessagesintheCitruslogfiles.Thisismorethanhelpfulwhensearchingforerrorsinsideyourtestcase.

HttpandRESTfulWebServices

WehavechangedHttpcommunicationcomponentsforfullsupportofRESTfulWebServicesonclientandserverside.TheHttpclientnowusesSpring'sRESTsupportforHttprequests(GET,PUT,DELETE,POST,etc.).Theserversidehaschanged,too.TheHttpservernowprovidesRESTfulWebServicesandiscomplianttotheexistingSOAPJettyserverimplementationinCitrus.IfyouwanttoupgradeexistingprojectstothisversionyoumayhavetoadjusttheSpringapplicationcontextconfigurationtosomeextent.

Fordetailshavealookattheupgradeguide(history-upgrading)inthischapterorfinddetailedexplanationstothenewHttpcomponentsinhttp.

HTMLreporting

CitrusprovidesHTMLreportsaftereachtestrunwithdetailedinformationonthefailedtests.Youcanimmediatelyseewhichtestsfailedinexecutionandwheretheteststopped.reporting-htmlprovidesdetailsonthisnewfeature.

Validationmatchers

Thenewvalidationmatcherswillputthemessagevalidationmechanismstoanewlevel.Withvalidationmatchersyouareabletoexecutepowerfulassertionsoneachmessagecontentelement.ForinstanceyoucanusetheisNumbervalidationmatcherforchecking

CitrusReferenceGuide

560Changes1.2

Page 561: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

thatamessagevalueisofnumericnature.Weaddedseveralmatcherimplementationsthatarereadyforusageinyourtestbutyoucanalsowriteyourcustomvalidationmatchers.Havealookatvalidation-matchersfordetails.

Conditionalcontainer

Thenewconditionaltestactioncontainerenablesyoutoexecutetestactionsonlyincaseabooleanexpressionevaluatestotrue.Sothenestedtestactionsinsidethecontainermaybenotexecutedatallincaseaconditionisnotmet.Seecontainers-conditionalfordetails.

Supportformessageselectorsonmessagechannels

SpringIntegrationmessagechannelsdonotsupportmessageselectorslikeJMSqueuesdoforexample.WithCitrus1.2weimplementedasolutionforthisissuewithaspecialmessagechannelimplementation.Soyoucanusethemessageselectorfeaturealsowhenusingmessagechannels.Gotomessage-channel-selector-supportfordetails.

Newtestactions

Weintroducedsomecompletelynewtestactionsinthisreleaseforyou.Thenewactionsarelistedbelow:

Purgemessagechannelaction()

Seeactionsfordetailedinstructionshowtousethenewactions.

Newfunctions

WeintroducedsomenewdefaultCitrusfunctionsthatwilleasethetesterslife.Thisincludescommonlyusedfunctionslikeencoding/decodingbase64bindarydata,escapingXMLandgeneratingrandomJavaUUIDvalues.Thesearethenewfunctionsinthisrelease:

citrus:randomUUID()citrus:cdataSection()citrus:encodeBase64()citrus:decodeBase64()citrus:digestAuthHeader()citrus:localHostAddress()

CitrusReferenceGuide

561Changes1.2

Page 562: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

Seefunctionsfordetaildescriptionsofeachfunction.

Upgradingfromversion1.1

IfyouarecomingfromCitrus1.1finalyoumayhavetolookatthefollowingpoints.

SpringversionupdateWedidsomemajorversionupgradesonourSpringdependencies.WearenowusingSpring3.1.1,SpringIntegration2.1.2andSpringWS2.1.0.ThesenewmajorreleasesbringsomecodechangesinourCitrusAPIwhichmightaffectyourcodeandconfiguration,too.Sopleaseupdateyourconfiguration,itisdefinitelyworthit!

SpringIntegrationheaders:With2.0.xversionSpringIntegrationhasremovedtheinternalheaderprefix("springintegration_").Soinsomecasesyoumightusethoseinternalheadernamesinyourtestcasesinordertosynchronizesynchronouscommunicationwithinternalmessageids.YourtestcasewillfailaslongasyouusetheoldSpringinternalheaderprefixinthetest.Simplyremovetheheaderprefixwhereverusedandyourtestisupandrunningagain.

Messagevalidator:YouneedtospecifyatleastonemessagevalidatorintheSpringapplicationcontext.BeforethiswasinternallyastaticXMLmessagevalidator,butnowweofferdifferentvalidatorsforseveralmessageformatslikeXMLandJSON.PleaseseetheJavaAPIdoconMessageValidatorinterfaceforavailableimplementations.IfyoujustliketokeepitasitwasbeforeaddthisbeantotheSpringapplicationcontext:

<beanid="xmlMessageValidator"class="com.consol.citrus.validation.xml.DomXmlMessageValidator"

Testsuite:Wehaveeliminated/changedtheCitrustestsuitelogicbecauseitduplicatesthosetestsuitesdefinedinTestNGorJUnit.InolderversionsthetesterhadtodefineaCitrustestsuiteinSpringapplicationcontextinordertoexecutetestactionsbefore/afterthetestrun.Nowthesetasksbeforeandafterthetestrunaredecoupledfromatestsuite.YoudefinetestsuitesexclusivelyinTestNGorJUnit.Thetestactionsbefore/afterthetestrunareseparatelydefinedinSpringapplicationcontextsoyouhavetochangethisconfigurationinyourCitrusproject.

Seetestsuitefordetailsonthisconfigurationchanges.

JUnitvs.TestNG:WesupportbothfamousunittestingframeworksJUnitandTestNG.Withthisreleaseyouarefreetochooseyourpreferedone.Inthismanner

CitrusReferenceGuide

562Changes1.2

Page 563: Table of Contents - Citrus Frameworkcitrusframework.org/citrus/reference/2.7/pdf/citrus-reference-2.7.pdf · Java 8 Citrus is now using Java 8. This is mainly because we need to move

youneedtoaddeitheraJUnitdependencyoraTestNGdependencytoyourprojectonyourown.WedonothavestaticdependenciesinourMavenPOMtoneitherofthosetwo.OnoursidethesedependenciesaredeclaredoptionalsoyoufeelfreetoaddtheoneyoulikebesttoyourMavenPOM.JustaddaJUnitorTestNGdependencytoyourMavenprojectoraddtherespectivejarfiletoyourprojectifyouuseANTinstead.

CitrusReferenceGuide

563Changes1.2