Wiki

Clone wiki

servicedomain-test-framework / Utveckling av testsviter och mockar

För att hålla testsviterna på en rimlig storlek är rekommendationen att göra ett SoapUI-projekt per tjänstekontrakt. Detta gör det möjligt för tjänsteleverantören att verifiera själva meddelandet i kontraktet. Om domänen innehåller flera tjänstekontrakt som samverkar i en process, är ansatsen att göra ett separat SoapUI-projekt som verifiera flödet i processen genom flera kontrakt.

För varje tjänstekontrakt måste man skapa två SoapUI-projekt, där det första är själva testsviten och det andra innehåller en mock-tjänst som används vid utveckling och underhåll av sviten.

För att på enklast sätt få mock-tjänsten att returnera rätt svar på en Request används en HttpHeader (“x-mock-response”) som innehåller namnet på det svar som ska användas, varje Request i testsviten måste därför innehålla denna HttpHeader.

Stödbibiliotek

Som stöd för testsvitsutvecklingen finns ett stödbibliotek (soapui-support). Källkoden finns i servicedomain-test-framework och byggartefakter finns här. Jar-filen soapui-support-nnn.jar checkas in i varje domän som använder den. Filen måste läggas in i SoapUI:s bin/ext-katalog för att kunna användas. Se Utveckling av soapui-support för info om utvecklingsmiljö för stödbiblioteket specifikt.

Referensimplementation

För en inblick i hur en testsvit kan se ut är det enklast att titta på en av de befintliga testsviterna i, som du hittar under test-suite i katalogen för respektive kontrakt.

Skapa testprojekt

  1. Skapa ett SoapUI-projekt för tjänstekontraktet, detta ska namnges med samma namn som kontraktet.
  2. Importera WSDL-filen för kontraktet.
  3. Lägg till en TestSuite i projektet.

Skapa mockprojekt

  1. Skapa ytterligare ett SoapUI-projekt, som ska innehålla mock-tjänsterna. Detta ska namnges med samma namn som kontraktet med tillägget "Mock" sist.
  2. Importera WSDL-filen för kontraktet och välj "Create MockService" i guiden.
  3. Öppna "Mock Operation"-dialogen och välj "Script" som Dispatch. För att välja vilket svar mock-tjänsten ska returnera baserat på ovan nämnda HttpHeader, klistra in följande rader sist i script-editorn:

def responseName = mockRequest.requestHeaders['x-mock-response'][0]
log.debug("responseName: ${responseName}")

return responseName
Scriptet kan även innehålla annan logik för att hämta värden från Request'en, men detta bör vara den minsta mängden kod som används för att välja rätt Response för givet Request.

Skapa testdatafil

En fil med namnet data.xml skapas med format enligt nedan. Filen innehåller dels global data och dessutom data som är specifik för varje testfall (i exemplet med namnet "Date_Boundaries"). Om samma fält finns i både det globala och testfallsspecifika datat, kommer det testfallsspecifika fältet att skriva över det globala värdet.

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="./../TK-doc.xsl"?>
<testsuite>
    <id>GetVaccinationHistory</id>
    <description>Beskrivning av testsuiten.</description>
    <globaldata>
        <webServiceUrl>http://localhost:8088/services</webServiceUrl>
    </globaldata>
    <testcase id="Date_Boundaries">
        <description>Beskrivning av testfallet</description>
        <data>
            <httpHeaderHsaId>112233</httpHeaderHsaId>
            <logicalAddress>112233</logicalAddress>
        </data>
    </testcase>
    <testcase id="CareUnitHsaId_Filter">
        <description>Beskrivning av testfallet</description>
        <data>
            <httpHeaderHsaId>112234</httpHeaderHsaId>
            <logicalAddress>112235</logicalAddress>
        </data>
    </testcase>
De element som anges inom elementen globaldata eller data kan sedan refereras antingen genom att ange t ex ${httpHeaderHsaId} i ett webservice-request, eller context.httpHeaderHsaId i ett script-block. Syftet är att separera testdatat från testlogiken så att en testare endast ska behöva editera testdatafilen, inte SoapUI-projektet.

Skapa testfall

  1. Lägg till ett TestCase under TestSuite'n.
  2. Lägg till ett "Script step" som namnges “Read data” under det nya TestCase't, som används för att läsa in testdata. Se exempel på script nedan.
  3. Lägg till ett "Test Request" efter Script-steget.
  4. Lägg till en HttpHeader “x-mock-response” för att låta mock-tjänsten kunna välja lämpligt svar på Request'en.
  5. Lägg till en HttpHeader “x-rivta-original-serviceconsumer-hsaid” med värdet “${httpHeaderHsaId}”. Värdet på variabel läses in av script-steget.
  6. Anpassa innehållet i Request'en så att det återspeglar det som ska verifieras av ditt testfall. Variabler refereras med ${careUnitHsaId}.
  7. Lägg till lämpliga assertions för verifieringar av svaret, se Assertions

Dokumentation

Dokumentation över testfallen genereras som en html-fil utifrån testdatafilen (data.xml) med hjälp av en mall (TK-doc.xsl). Mallen kan kopieras från annan testsvit. Genereringen sker med en xslt-processor, som xsltproc i nedanstående exempel: xsltproc test-suite/GetCareContacts/data.xml -o test-suite/GetCareContacts/GetCareContacts-doc.html

Releasehantering

De genererade artefakterna soapui-support-nnn.jar och NamnPåTjänstekontrakt-doc.html skall checkas in, så att en release av tjänstekontraktet kan skapas utan att något bygg-steg utförs.

Script

Här visas exempel på script som kan användas för att skapa en testsvit.

Läsa testdata

import se.skl.rivta.testsupport.soapui.datasource.XmlDataReader
import com.eviware.soapui.support.GroovyUtils

def utils = new GroovyUtils(context)
def dataFile = new File(utils.projectPath + "/data.xml")
def source = new XmlDataReader(context, dataFile)

source.load(testRunner.testCase.getName())

Mock-script

Validering av inkommande Request

Vid utveckling av testsviterna är det smidigt att få hjälp med validering av de requests man skapar, använd då nedanstående script. Detta kräver stödbiblioteket soapui-support (se ovan).

Den validering som sker är att requesten uppfyller schema och inte innehåller tomma element (t.ex. vid felaktig variabelanvändning). Vid valideringsfel kommer scriptet att försöka returnera ett svar som heter SoapFault, med valideringsfelen i variabeln validationMessages.

def mh = new se.skl.rivta.tksupport.soapui.MockHelper(mockRequest, mockOperation)
context.validationMessages = mh.getSchemaErrorsInRequest() ?: mh.getEmptyElementsInRequest() ?: null
if (context.validationMessages) {
    return "SoapFault"
}

Logging till fil

Ibland vill man logga resultat av mock-valideringar och annat till en speciell fil, för att kunna kontrollera i efterhand. Detta kan göras genom att man utökar den ordinarie loggningen med en extra fil (GetCareContactsMockLog.txt) i exemplet nedan. Observera att removeCustomLogging() anropas i ett finally-block för att undvika följdproblem om något går fel i mock-koden.

def mh = new se.skl.rivta.tksupport.soapui.MockHelper(mockRequest, mockOperation, log)
try {
    mh.addCustomLogging("GetCareContactsMockLog.txt")
    context.validationMessages = mh.getSchemaErrorsInRequest() ?: mh.getEmptyElementsInRequest() ?: null
    if (context.validationMessages) {
        log.info context.validationMessages
        return "SoapFault"
    }
    log.info "Validation OK"
    return "OK"
} finally {
    mh.removeCustomLogging()
}

Hämta värden från request

Detta script är ett exempel för hur mock-tjänsten hämtar värden från Request'en, som sedan blir tillgängliga att använda i svaret som returneras.

import com.eviware.soapui.support.XmlHolder

def holder = new XmlHolder( mockRequest.requestContent )
holder.namespaces['soapenv'] = 'http://schemas.xmlsoap.org/soap/envelope/'
holder.namespaces['urn'] = 'urn:riv:itintegration:registry:1'
holder.namespaces['urn1'] = 'urn:riv:clinicalprocess:logistics:logistics:GetCareContactsResponder:2'
holder.namespaces['urn2'] = 'urn:riv:clinicalprocess:logistics:logistics:2'

def careUnitHSAid = holder.getNodeValue('/soapenv:Envelope/soapenv:Body/urn1:GetCareContacts/urn1:careUnitHSAid')
def patientId = holder.getNodeValue('/soapenv:Envelope/soapenv:Body/urn1:GetCareContacts/urn1:patientId/urn2:id')
def patientIdType = holder.getNodeValue('/soapenv:Envelope/soapenv:Body/urn1:GetCareContacts/urn1:patientId/urn2:type')

context.patientId = patientId
context.patientIdType = patientIdType
context.careUnitHSAid = careUnitHSAid

def responseName = mockRequest.requestHeaders['x-mock-response'][0]
log.debug("responseName: ${responseName}")

return responseName

Mockdatafil

I mockar som är till för att verifiera riktiga tjänstekonsumenter kan man vilja lägga in mer dokumentation och parametrar för styrning. En data.xml-fil kan då användas på motsvarande sätt som för testsviter. Exempel:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="./../.xsl/Mock-doc.xsl"?>
<mockService>
    <id>ListCertificatesForCare 3.0</id>
    <contractName>ListCertificatesForCare</contractName>
    <description>När ett anrop når mocktjänsten sker följande:
        ...
    </description>
    <globaldata>
        <!-- Loggning av anrop/valideringar (gemensam loggfil, sökväg relativt mock-projektet) -->
        <mockLogFile>ListCertificatesForCareLog.txt</mockLogFile>
        <!-- Loggning av request/response (en fil per anrop) -->
        <logTestData>false</logTestData>
        <logTestDataPath>C:/temp/SOAP-UI/</logTestDataPath>
        <logTestDataFilesAllowed>500</logTestDataFilesAllowed>
    </globaldata>
    <testInstructions>
        Testning av tjänstekonsumenten sker med en kombination av automatiska och manuella kontroller. 
        Varje testfall genomförs i huvudsak enligt följande steg.
        ...
    </testInstructions>
    <testcase id="1.1">
        <description>Beskrivning av manuellt testfall</description>
    </testcase> 
</mockService>

För att läsa in parametrarna i globaldata till mockens context behövs följande läggas till i mock-scriptet:

new se.skl.rivta.tksupport.soapui.datasource.XmlDataReaderMock(context).load("data.xml")

Updated