Realizzare un framework di Test con SOAPUI Rosario Turco SoapUI è uno strumento Java Open Source estremamente utile e semplice, specie in ambito architetture SOA e utilizzabile su piattaforme qualsiasi (Windows/Unix/Linux etc) grazie alla neutralità di Java ed adatto per:
test di servizi web (http/https/jms), per creare stub di servizi offerti da un server o per creare client di servizi;
test di connettività dei servizi e scambio WSDL;
verifica di correttezza;
test di un servizio sviluppato che ha bisogno di altri servizi esterni da simulare (to mock external service);
risparmio di memoria in ambienti di test, poiché gli stub ottenibili pesano pochissimo e possono essere messi su PC;
automatizzazione dei i test, a partire dalle WSDL;
predisposizione di un catena di test;
predisposizione di un Load Test.
Impiego rapido in Metodologie Agili e di Continuos Testing
Il vantaggio che fornisce è quello di rendersi indipendenti dai sistemi, che offrono i servizi e di disporre di un framework open source gratuito e robusto, che permette rapidamente di testare quanto sviluppato, sia a livello di test unitario che di test di sistema. Ci soffermeremo soprattutto sul come utilizzare in questo caso SOAPUI, ovviamente iniziate prima a scaricarvi l’ultima versione, che attualmente è 3.6.1. Vi consiglio di unzippare il tutto sotto C:\Tools\soapui-
3.6.1. In Tools io di solito metto metto tutti i prodotti Java necessari ai miei sviluppi. sviluppi. Da C:\Tools\ soapui-3.6.1\bin create uno shortcut sul desktop del file soapui.bat ed ora cliccate sul file batch e siete pronti ad iniziare. Nell'esempio di seguito viene descritto come disporre SOAPUI per testare dei servizi. Faremo due scenari di due tipi di test diversi.
SCENARIO A – Servizio Stub esposto che fornisce una Response Ipotizziamo un servizio che deve essere simulato PrepaidMobileNumberInformationQuery con l’operation getCreditoInfo e che restituisce, quindi, su http delle informazioni (sarebbe analogo anche su https o su JMS).
Predisposizione Predisposizione delle WSDL e degli XSD e controlli IP e porta Le WSDL e gli XSD relativi sono disponibili in APPENDICE per fare un semplice “past & cut” per le prove di
apprendimento. Aprirle con un editor XML/WSDL adeguato in una cartella del desktop. Innanzitutto cambiare nella WSDL, in fondo nel binding, cambiare l’IP con quello del proprio PC e controllare che sul sistema Windows o Unix (dove cioè è presente SOAPUI running) non fosse già in uso la porta 8088 che è quella di default usata da SOAPUI, altrimenti cambiarla; ad esempio sul Windows da comandi DOS con nestat nestat –a | find “
” oppure su Unix con netstat –a | grep .
1
Ovviamente il servizio chiamante sotto test deve essere configurato per chiamare l’IP del PC e la porta
prescelta.
Creazione Progetto Su SOPAUI nel menu selezionare "File" → "Nuovo soapUI Project". Impostare il nome del progetto, ad esempio "PrepaidMobileStub". Impostare l'iniziale WSDL_PrepaidMobileNumberInformationQuery_Concrete-v1.wsdl e premere OK.
Creazione MockService A
questo
punto
occorre
creare
il
mock
service,
poggiando
il
puntatore
del
mouse
su
PrepaidMobileNumberInformationQueryBinding e con tasto destro fare Generate Mock Service e accettare il nome proposto, mentre in path occorre mettere ciò che contiene la WSDL cioè: /IB/services/PrepaidMobileNumbe /IB/services/PrepaidMobileNumberInformationQuery-v1 rInformationQuery-v1
Questo deve essere visibile anche dal MockService Properties a sinistra. Spesso su PC sia per la request che per per la response (vedi MockService Properties di entrambi), occorre mettere l’alias con cui è visto i l PC e non l’IP a d esempio:
http://NBW72006184478:8088/IB/services/PrepaidMobileNumberInformationQuery-v1 Se occorresse cambiare (se il test non funziona) basta farlo con doppio click su Request che apre l’editor
per lanciare il test e una location per modificare la url (usare Edit current).
Valorizzare Request e Response Valorizzate la parte XML della request (doppio click su Request a sinistra) e della response (analogamente) riempendo dove vi sono i ?.
Start Minimized e test se OK A questo punto si può fare sul Mock Service tasto destro e Start Minimized. Per fare un test che è tutto OK mandate una request valorizzata (triangolo verde in alto) e vedrete sempre nell’editor della request (a destra, l’altra porzione di finestra la risposta) . A questo punto il vostro Mock
Service è uno STUB pronto pro nto a ricevere richieste dal servizio sviluppato reale che dovete testare. Suggerimento
Salvate sempre il progetto e accumulate i file di test da fare (le request) con un Test case, questo vi permetterà di attrezzarvi per le regressioni. Suggerimento denominate le request secondo il caso di test della vostra progettazione Request-OK-12, Request-NOK- 13 etc e alla fine l’obiettivo è di predisporre in sequenza tutti i test. E’ possibile anche predisporre il risultato atteso (lo vedremo nel prossimo esempio).
SCENARIO B – Mocking external service Ipotizziamo un servizio che calcola, ad esempio, il prezzo di un viaggio (TripPriceService) e per farlo, ad esempio, sono invocati un servizio che restituisce i prezzi per le camere d'albergo (HotelPriceService) ed un servizio che restituisce i prezzi dei soli voli (FlightPriceService). 2
Qua ipotizziamo che TripPriceService è un servizio da voi effettivamente sviluppato e deployato, mentre gli altri due servizi sono servizi elementari esterni da richiamare da TripPriceService e da cui ottenere risposta e sono quest’ultimi da sottoporre a mocking. La formula del calcolo sarà semplicemente: price = duration * rooms * getRoomPrice() + adults * getFlightPrice(from, to)
In sostanza il prezzo da pagare dipende dalla durata (o numero di giorni), dal numero di stanze, dal prezzo per stanza, dal numero di adulti e il prezzo del volo da una partenza ad una destinazione. In APPENDICE sono mostrate le WSDL dei tre servizi: TripPriceService.wsdl, HotelPriceService.wsdl, FlightPriceService.wsdl. Per la prova, basterà per voi fare “past & cut” di esse dall’APPENDICE e creare le WSDL in una cartella sul desktop con un opportuno editor che tratta XML/WSDL a grid e text Dall’APPENDICE createvi le WSDL in una cartella sul desktop.
Creare un progetto SOAPUI con un servizio reale da testare e non da esporre su SOAPUI Nel menu selezionare "File" → "Nuovo soapUI Project". Impostare il nome del progetto, ad esempio "tripprezzo-servizio". Poiché il servizio TripPriceService è sviluppato e deployato (ad esempio su TIBCO) ed è quello a cui siamo interessati per il test, mentri gli altri servizi saranno degli STUB, qua Initial WSDL occorre fare l’invocazione: "http://localhost:8080/trip-price-0.0.1-SNAPSHOT/webservices/TripPriceService?wsdl" . Dopo fare clic su OK. Ora per i servizi che invoca TripPriceService e che dovremmo esporre come STUB su SOAPUI ipotizzeremo che siano su porta 8088 di default di SOAPUI; però conviene sempre controllare che sul sistema Windows o su Unix (dove cioè funziona SOAPUI) non fosse già in uso; ad esempio sul Windows da comandi DOS con nestat –a | find “” oppure su Unix con netstat –a | grep Ricordarsi ora che per i due servizi STUB occorre cambiare nella WSDL in fondo nel binding IP e/o porta di dove è SOAPUI e analogamente occorre configurare TripPriceService per puntare ad essi. Come risultato, si ottiene il progetto "trip -prezzo-servizio" che contiene l'interfaccia "TripPriceServiceFacadeServiceSoapBinding". Se cliccate su TripPriceServiceFacadeServiceSoapBinding e successivamente su Service Endpoints vedrete le attuali configurazioni. Cliccando su Request 1 vedrete un messaggio di prova che successivamente potrete popolare dopo. I “?” sono da valorizzare.
3
Generare una suite di test Fare clic col tasto destro destro sull'interfaccia "TripPriceServiceFacadeServiceSoapBinding" e selezionare "Generate TestSuite" e click OK. Inserire
un
nome
per
la
piattaforma
di
test,
consiglio
ad
esempio
"TripPriceServiceFacadeServiceSoapBinding TestSuite" e click OK. Come risultato si ottiene il "TripPriceServiceFacadeServiceSoapBinding TestSuite", che include il "getTripPrice TestCase" caso di test. Il caso di test contiene già generato la richiesta "getTripPrice" test.
Configurare il caso di test (Test Case) L'impostazione di un timeout per il test è una buona pratica, in modo che il test non rimanga in attesa infinita, in caso qualcosa vada storto. A sinistra col tasto destro destro del mouse sul TestCase, fare clic su Options. Impostare TestCase timeout in millisecondi, per esempio "5000". Fare clic su OK.
Configurare la richiesta di test Fare doppio clic sulla richiesta "getTripPrice" Test e modificare la Request in modo da chiedere il prezzo dei viaggi seguenti: da Berlino a Parigi, per 2 adulti, della durata di 3 giorni, una camera necessarie. Potete per prova fare “past & cut” del messaggio successivo e incollarlo al posto della Request 1: 2 3 Berlin 1 Paris
4
Fare clic su "Invia la richiesta di URL specificato endpoint" tasto (triangolo verde in alto alla finestra della Request 1). Come primo risultato, otteniamo un errore SOAP, perché i servizi esterni di cui abbiamo bisogno non sono disponibili. > soap:Server Could Could not send Message.
Mock servizi esterni - Importare il file WSDL del servizio esterno Sul nome del progetto a sinistra fare ADD WSDL e Impostare il percorso WSDL di HotelPriceService.wsdl. Fare clic su "OK". Come risultato, si aggiunge una nuova interfaccia per il progetto: "HotelPriceServiceFacadeServiceSoapBinding".
Aggiungere una risposta mock al caso di test Torniamo a getTripPrice Test Case. Tasto destro del mouse e selezionare "Aggiungi Step" -> "Mock Response". Specificare un nome per il nuovo passaggio, per esempio "getRoomPrice Mock Response" e fare clic su OK. Selezionare l'operazione "getRoomPrice" di interfaccia "HotelPriceServiceFacadeServiceSoapBinding". Impostare il percorso (prelevabile dalla WSDL): /external-services-0.0.1-SNAPSHOT/webservices/HotelPriceService Porta 8088. Fare clic su OK. Modificare la response generata, per restituire un prezzo, per esempio "55.0". Impostare la fase di avvio della risposta mock ( start step) nel "MockResponse Properties" a "getTripPrice". Così il servizio mock inizierà ad ascoltare le richieste non appena la richiesta è getTripPrice.
Aggiungere un’assert per la richiesta getRoomPrice Attivare una richiesta al servizio mock, così questo renderà più facile la configurazione dell’assertion. Fare clic su triangolo verde nell'editor caso di test. Si dovrebbe vedere una richiesta nell'editor risposta mock. Fare clic sulla scheda "Assertions". Cliccare sul pulsante "Add an Assertion for this item ". Selezionare "Schema Compliance" e fare clic su OK. Controllare l'url definizione da convalidare e fare clic su OK. L'assertion deve apparire come valido.
5
Ripetiamo con FlightPriceService FlightPriceService Fare
clic
destro
sul
project
e
selezionare
"Aggiungi
WSDL".
Impostare
il
percorso
WSDL
FlightPriceService.wsdl. Fare clic su OK. Come risultato, si aggiunge una nuova interfaccia per il progetto: "FlightPriceServiceFacadeServiceSoapBinding". Sempre sul TestCase tasto destro del mouse e selezionare "Aggiungi Step" -> "Mock Response". Specificare un nome per il nuovo passaggio, per esempio "getFlightPrice Mock Response" e fare clic su OK. Selezionare l'operazione "getFlightPrice" di interfaccia "FlightPriceServiceFacadeServiceSoapBinding". Impostare la porta, qui "8088". Impostare il percorso, qui /external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService e fare clic su OK. Modifica la risposta generata, per restituire un prezzo, ad esempio, "49,99". Impostare la fase di avvio della risposta mock nel "MockResponse Properties" a "getTripPrice". Così il servizio mock inizierà ad ascoltare le richieste non appena la richiesta inviata è getTripPrice. Avviare (triangolo verde) il Test Case. Si dovrebbe vedere una richiesta nell'editor risposta mock. envelope/"> > Berlin Paris
Fare clic sulla scheda "asserzioni". Clicca sul pulsante "Add an assertion of this item". Selezionare "Schema Compliance" e fare clic su OK. Controllare l'url di definizione da convalidare e fare clic su OK. L'affermazione dovrebbe apparire come valida. Clicca sul pulsante "Add an assertion of t his item". Selezionare "XPath Match" e fare clic su "OK", si aprirà la finestra di configurazione XPath Match. Fare clic su "dichiarare" per generare automaticamente le dichiarazioni declare namespace soap='http://schemas. soap='http://schemas.xmlsoap.org/soap/e xmlsoap.org/soap/envelope/'; nvelope/'; declare namespace ns2='http://external.service ns2='http://external.services/flight'; s/flight'; Query the "from" field //ns2:getFlightPrice/from //ns2:getFlightPrice/from
Impostare il risultato atteso, in questo esempio "Berlin". Fare clic su "Salva" L'affermazione dovrebbe apparire come valido. Fare lo stesso per il campo "A" e controllare che sia Parigi.
6
Verificare la risposta Eseguire il caso di test, ora dovrebbe avere successo. Aprire l'editor richiesta di test. Fare clic sulla scheda "asserzioni". Aggiungere un "Schema Compliance" affermazione, per verificare lo schema di risposta. Aggiungere un "Non SOAP Fault" affermazione, per verificare la risposta non è un errore SOAP. Aggiungere un "SOAP Response" affermazione, per verificare la risposta è una risposta SOAP. Aggiungere un "XPath Match" affermazione di controllare il prezzo . XPath Expression declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/'; declare namespace ns2='http://trip.price.service'; //ns2:getTripPriceResponse/return Expected Result : 264.98 (3 * 1 * 55 + 2 * 49.99) (3 * 1 * 55 + 2 * 49,99) Ora la Test Suite è pronta.
Conclusioni Un ottimo strumento. Vanno affinate solo le vostre conoscenze su esso su come usarlo. Studiare a fondo il tutorial messi a disposizione in [1][2]. [1] http://www.soapui.org
[2] http://www.soapui.org/JMS/getting-started.html
APPENDICE SCENARIO A WSDL_PrepaidMobileNumberInformationQuery_ WSDL_PrepaidMobile NumberInformationQuery_Concrete-v1.wsdl Concrete-v1.wsdl TF-8"?> "> > "> "/> sd"/> --> xsd:schema> xsd:schema> wsdl:types> wsdl:types> "> "/>
7
"/> wsdl:message> wsdl:message> "> "/> "/> wsdl:message> wsdl:message> "> "/> "/> wsdl:operation> wsdl:operation> wsdl:portType> wsdl:portType> "> "/> "> "/> > "/> "/> wsdl:input> wsdl:input> > "/> "/> wsdl:output> wsdl:output> wsdl:operation> wsdl:operation> wsdl:binding> wsdl:binding> v1"/> wsdl:port> wsdl:port> wsdl:service> wsdl:service> wsdl:definitions> wsdl:definitions>
PrepaidMobileNumberInformationQuery.xsd TF-8"?> "> "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > xs:element> xs:element> "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > xs:element> xs:element>
8
"> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> "/> xs:simpleType xs:simpleType > xs:schema> xs:schema>
PrepaidMobileNumberInformationQueryEntities.xsd TF-8"?> "> "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence>
9
xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > > > >TelephoneNumber TelephoneNumber bvi:className> bvi:className> bvi:primitiveType> bvi:primitiveType> xs:appinfo> xs:appinfo> xs:annotation> xs:annotation> "> \+?\d{1,}"/> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> > > > >Decimal Decimal bvi:className> bvi:className> bvi:primitiveType> bvi:primitiveType> xs:appinfo> xs:appinfo> xs:annotation> xs:annotation> "/> xs:simpleType xs:simpleType > "> > > > >DateTime DateTime bvi:className> bvi:className> bvi:primitiveType> bvi:primitiveType> xs:appinfo> xs:appinfo> xs:annotation> xs:annotation> "/> xs:simpleType xs:simpleType > "> "> \d{1,}"/> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "/> xs:simpleType xs:simpleType > "/> xs:simpleType xs:simpleType > "> "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > xs:schema> xs:schema>
SOAPHeader_v1.1.xsd TF-8"?>
SOAPHeader_v1.1.xsd SOAPHeader_v1 .1.xsd 1.1 XSD file
10
// Created: 05/03/2009 // Modified 16/03/2010 // Developed by: Simone Avossa --> "> "> > >Informazioni di contesto dell'invocazione dell'invocazione del servizio servizio xs:documentation> xs:documentation> xs:annotation> xs:annotation> > > >Sistema da cui proviene la richiesta richiesta xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> > >Data e Ora di invocazione del servizio xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> > >Identifica univocamente il processo di business business xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> > >Identifica il messaggio in maniera univoca univoca xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> > >Identifica la transazione per gestire i ritorni sincroni sincroni xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> "> \d{4}-\d{2}-\d{2}"/> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> "> xs:restriction> xs:restriction> xs:simpleType xs:simpleType > "> > "> >
11
>Per compatibilità con i diversi prodotti o librerie software (es. Axis2 e BW) si è scelto di utilizzare il tipo string. La restizione applicata accetta il formato: CCYY -MM-DD. Non sono presenti restrizioni sul range dei valori. valori. xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> "> > >Per compatibilità con i diversi prodotti o librerie software (es. Axis2 e BW) si è scelto di utilizzare il tipo string. La restizione applicata accetta il f ormato: hh:mm:ss.sss. Non sono presenti restrizioni sul range dei valori. Per gli ulteriori dettagli sul formato fare riferimento alla definizione di Time Data Type W3C, presente al link: http://www.w3schools.com/Schema http://www.w3schools.com/Schema/schema_dtypes_date.a /schema_dtypes_date.asp sp xs:documentation> xs:documentation> xs:annotation> xs:annotation> xs:element> xs:element> xs:sequence> xs:sequence> xs:complexType xs:complexType > "/> xs:schema> xs:schema>
SCENARIO B TripPriceService.wsdl TF-8"?> "> > "> "/> "/> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> "/> "/> "/> "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "/> "> xs:complexType xs:complexType > xs:schema> xs:schema> wsdl:types> wsdl:types> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> "> wsdl:input> wsdl:input > "> wsdl:output> wsdl:output> "> wsdl:fault> wsdl:fault>
12
wsdl:operation> wsdl:operation> wsdl:portType> wsdl:portType> "> "/> "> "/> "> "/> wsdl:input> wsdl:input> "> "/> wsdl:output> wsdl:output> "> "/> wsdl:fault> wsdl:fault> wsdl:operation> wsdl:operation> wsdl:binding> wsdl:binding> "> "> "/> wsdl:port> wsdl:port> wsdl:service> wsdl:service> wsdl:definitions> wsdl:definitions>
HotelProceService.wsdl TF-8"?> "> > "> "/> "/> "> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > xs:schema> xs:schema> wsdl:types> wsdl:types> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> "> wsdl:input> wsdl:input> "> wsdl:output> wsdl:output> wsdl:operation> wsdl:operation> wsdl:portType> wsdl:portType> "> "/> "> "/> "> "/> wsdl:input> wsdl:input> "> "/> wsdl:output> wsdl:output> wsdl:operation> wsdl:operation> wsdl:binding> wsdl:binding> ">
13
SNAPSHOT/webservices/HotelPriceService"/> wsdl:port> wsdl:port> wsdl:service> wsdl:service> wsdl:definitions> wsdl:definitions>
FlightPriceService.wsdl TF-8"?> "> > "> "/> "/> "> > "/> "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "> > "/> xs:sequence> xs:sequence> xs:complexType xs:complexType > "/> "> xs:complexType xs:complexType > xs:schema> xs:schema> wsdl:types> wsdl:types> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> wsdl:part> wsdl:part> wsdl:message> wsdl:message> "> "> "> wsdl:input> wsdl:input> "> wsdl:output> wsdl:output> "> wsdl:fault> wsdl:fault> wsdl:operation> wsdl:operation> wsdl:portType> wsdl:portType> "> "/> "> "/> "> "/> wsdl:input> wsdl:input> "> "/> wsdl:output> wsdl:output> "> "/> wsdl:fault> wsdl:fault> wsdl:operation> wsdl:operation> wsdl:binding> wsdl:binding> "> "> "/> wsdl:port> wsdl:port>
14
wsdl:service> wsdl:service> wsdl:definitions> wsdl:definitions>
15