readyapi4j

Swagger Assert4j Core

This modules provides the core Java APIs for creating and executing Test recipes which can then be executed locally or remotely as described in the Concepts document.

Fluent vs Pojo API

To simplify the creation of Test Recipes POJOs that get serialized to the corresponding JSON Recipes before execution this module provides a fluent API layer that attempts to provide a more “verbal” way of expressing Recipes through code. All the examples below will use this fluent API - but if you’re interested in the underlying POJO classes please have a look at the source or core javadocs. The java samples module contains examples using both the fluent and pojo approach.

Recipes

Assert4j expresses tests as “recipes”. A test recipe is an ordered list of steps that are executed sequentially when the test is run. The easiest way to build a recipe is to use the TestRecipeBuilder class:

TestRecipe recipe = TestRecipeBuilder.buildRecipe( ...list of TestStepBuilder objects... );

// optionally add some more teststeps
recipe.addStep( ...another TestStepBuilder object... );

// execute the test!
RecipeExecutionResult result = RecipeExecutorBuilder.buildDefault().executeRecipe( recipe );

Test Steps

Test steps represent the actual actions performed during the execution of a test, the TestSteps class provides factory methods for creating TestStepBuilders for each supported test step type, as you will see below.

REST Requests

The TestSteps class provides convenience methods for the common HTTP Verbs:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    GET( "http://petstore.swagger.io/v2/store/inventory" ),
    POST( "http://petstore.swagger.io/v2/store/order" ),
    DELETE( "http://petstore.swagger.io/v2//pet/{petId}"),
    GET( "http://petstore.swagger.io/v2/pet/findByStatus" )
);

Parameters

Both the DELETE and last GET in this example require parameter values - let’s add them:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    GET( "http://petstore.swagger.io/v2/store/inventory" ),
    POST( "http://petstore.swagger.io/v2/store/order" ),
    DELETE( "http://petstore.swagger.io/v2//pet/{petId}").
        withPathParameter( "petId", "1" )
    ,
    GET( "http://petstore.swagger.io/v2/pet/findByStatus" ).
        withQueryParameter( "status", "test")
);

If you need to add multiple parameters you can use

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
  GET( "http://petstore.swagger.io/v2/pet/findByStatus" ).
     withParameters( 
         query( "status", "test"),
         query("limit", "10")
     )
);

Content

Adding content to a POST or PUT is straight-forward:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    POST( "http://petstore.swagger.io/v2/store/order" ).
        withMediaType( "application/json").
        withRequestBody( ...some object that can be serialized to JSON... )
);

the built in serialization support json, yaml and xml media types.

Authentication

If you need to add authentication to your request, then use one of the factory methods provided by the Authentications class.

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
  GET( "http://petstore.swagger.io/v2/pet/findByStatus" ).
     withParameters( 
         query( "status", "test"),
         query("limit", "10")
     ).
     withAuthentication(
        basic( "username", "password"),
        oAuth2().
            withAccessToken( ... ) 
     )
);

Attachments

If you’d like to attach a file to the body of a request instead of providing it as content as shown above, you can use the withAttachments(...) method together with the factory methods in the Attachments class

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
  POST( "http://petstore.swagger.io/v2/store/order" ).
     withAttachments(
         file( "request.json", "application/json" )
     )
);

SOAP Requests

The built in SOAP support makes it super-easy to call SOAP Services. You will need to provide the underlying WSDL and details on which binding and operation to call. Filling out the actual message body can either be done using utility methods or manually by providing the request XML:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
   soapRequest(new URL("http://www.webservicex.com/globalweather.asmx?WSDL"))
                  .forBinding("GlobalWeatherSoap11")
                  .forOperation("GetWeather")
                  .withParameter("CountryName", "Sweden")
                  .withPathParameter("//*:CityName", "Stockholm")
);

As you can see in this example we’re making a call to the GetOperation method defined in the WSDL. The actual XML request for this operation looks as follows:

<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:web="http://www.webserviceX.NET">
    <soapenv:Header/>
    <soapenv:Body>
        <web:GetWeather>
            <web:CityName>?</web:CityName>
            <web:CountryName>?</web:CountryName>
        </web:GetWeather>
    </soapenv:Body>
</soapenv:Envelope>

Fortunately we never have to create any XML: we simply set the CountryName parameter using simple the corresponding element name ignoring namespaces. The CityName parameter is set using an XPath pointer, but it could equally have been done with the withParameter method.

Property Transfers

Transferring values from the response of one TestStep to the request of another is a very common task when creating multi-step tests. The Property Transfer TestStep handles this for you:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(
    GET( "http://petstore.swagger.io/v2/pet/findByStatus?status=test" ).named("getPets"),
    propertyTransfer(
        fromPreviousResponse( "$.[0].id" ).toNextRequestProperty("petId")
    ),
    GET( "http://petstore.swagger.io/v2/pet/{petId}" ).named( "getPet" ).
        withAssertions(
            statusCodes( 200 )
        )
);

The example above uses XPath to extract the id property of the first item in the response to the petId path parameter in the following request, using the fromPreviousResponse and toNextRequest convenience methods in the PropertyTransferBuilder class.

Delay

Use the delayStep to insert a delay between two TestSteps:

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    GET( "http://petstore.swagger.io/v2/pet/findByStatus?status=test" ),
    delayStep( 1000 ), // wait for one second...
    GET( "http://petstore.swagger.io/v2/pet/(id}" )
);

Script

Script TestSteps allow you to execute an arbitrary block of Groovy code as part of your test, for example

```java
TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    GET( "http://petstore.swagger.io/v2/pet/findByStatus?status=test" ),
    delayStep( 1000 ), // wait for one second...
    groovyScriptStep( "System.out.println(\"Hello world!\")" ),
    GET( "http://petstore.swagger.io/v2/pet/(id}" )
);

The specified code has access to the complete underlying object-model of the executing test, see https://www.soapui.org/scripting—properties/the-soapui-object-model.html and https://www.soapui.org/functional-testing/working-with-scripts.html for more info.

JDBC Request

Test recipes can contain JDBC requests to interact with relational databases as part of a test. This can be valuable for either initializing data needed for a test, or for validating data that previous TestSteps are meant to create/modify. To use the JDBC TestStep make sure you have the corresponding JDBC driver in your classpath, then create a JdbcConnection object to the database which can be used to build the actual TestSteps:

JdbcConnection connection = jdbcConnection("org.mysql.Driver", "jdbc:mysql://localhost/mydb" );

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
   connection.jdbcRequest("select * from some_table")
);

Internally the result returned from the query is converted to XML format, which you could for example use in combination with a property transfer:

JdbcConnection connection = jdbcConnection("org.mysql.Driver", "jdbc:mysql://localhost/mydb" );

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    connection.jdbcRequest("select id from pets").named( "SelectPetIds"),
    propertyTransfer(
           // use xpath to select first returned id
           fromPreviousResponse( "//id[0]" ).toNextRequestProperty("petId")
       ),
       GET( "http://petstore.swagger.io/v2/pet/{petId}" ).named( "getPet" ).
           withAssertions(
               statusCodes( 200 )
           )
);

Assertions

Assertions can be added to REST, SOAP and JDBC request teststeps via the withAssertions(...) method to assert the content of their respective responses. The Assertions method contains factory methods that allow you to easily create any of the supported assertions, for example;

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
   soapRequest(new URL("http://www.webservicex.com/globalweather.asmx?WSDL"))
                  .forBinding("GlobalWeatherSoap11")
                  .forOperation("GetWeather")
                  .withParameter("CountryName", "Sweden")
                  .withPathParameter("//*:CityName", "Stockholm")
      .withAssertions(
          validStatusCodes( 200 ),
          notSoapFault(),
          xPathContent( "//*:CityName", "Stockholm")
      )
);

HTTP Assertions

The following HTTP-specific assertions are available:

Content Assertions

These assertions are for validating the body content of a response:

Internally the execution engine uses:

SOAP Assertions

The following SOAP-specific assertions are available:

JDBC Assertions

The following assertions are available for JDBC TestSteps:

Miscellaneous Assertions

Properties and Property Expansion

Properties are a common concept in the SoapUI execution engine; all TestSteps expose properties and there is even a dedicated Properties TestStep for defining properties for a Test:

TestRecipe testRecipe = newTestRecipe(
            properties(
                property( "username", "..."),
                property( "password", "...")
            )
            .named("Properties"),
            ...more TestSteps...
        ).buildTestRecipe();

Using defined properties is done via Property Expansion, for example, the above defined properties could be user for authentication:

TestRecipe testRecipe = newTestRecipe(
           properties(
               property( "username", "..."),
               property( "password", "...")
           )
           .named("Properties"),
           GET("..some endpoint..")
               .withAuthentication(basic("${Properties#username}", "${Properties#password}")
               )
           )
        ).buildTestRecipe();

Of course you could simply define username/password as java variables when building the recipe; the advantage of using properties is related to the augmentation and reuse of recipes as described under recipe filters below.

Extractors

Extractors allow you to easily extract values from a response message using XPath or JSONPath. For example you might want to use those values as input to other tests that are executed as part of an orchestrated end-to-end test suite:

String conversionRate;

TestRecipe recipe = TestRecipeBuilder.buildRecipe(  
    soapRequest(new URL("http://www.webservicex.com/CurrencyConvertor.asmx?wsdl"))
        .forBinding("CurrencyConvertorSoap")
        .forOperation("ConversionRate")
        .named( "GetConversionRate")
        .withParameter("FromCurrency", "USD")
        .withParameter("ToCurrency", "SEK")
        .withAssertions(
            schemaCompliance(),
            notSoapFault()
        )
        .withExtractors(
            fromResponse( "//*:ConversionRateResult/text()", v -> {
                 conversionRate = v;
            })
        )
        
// execute recipe and use extracted conversionRate for something else
RecipeExecutionFacade.executeRecipe( recipe );    

Executing Recipes

The Concepts document shows how to execute recipes using both the local and remote execution engines via the RecipeExecutionFacade class. Using the underlying RecipeExecutorBuilder makes it possible to augment execution using execution listeners and recipe filters as described below.

The actual Execution object available for the execution of a recipe is useful for querying asynchronous test execution status:

// use submitRecipe for async execution
Execution execution = RecipeExecutionFacade.submitRecipe( ... );

while( execution.getCurrentStatus() == RUNNING ){
    // do something useful
}

System.out.println( "Test finished with status: " + execution.getCurrentStatus() );

Execution Listeners

ExecutionListeners get notified of specific events related to recipe execution - for example the provided ExecutionLogger writes all execution transaction logs as HAR files to a specified folder:

// build a local executor that logs executions to a logs folder
RecipeExecutor executor = new RecipeExecutorBuilder()
    .withExecutionListener(new ExecutionLogger("logs"))
    .buildLocal();

executor.executeRecipe( ... )

Since the ExecutionLogger is commonly used the RecipeExecutorBuilder actually has a dedicated withExecutionLog( String logFolder) method:

// build a local executor that logs executions to a logs folder
RecipeExecutor executor = new RecipeExecutorBuilder()
    .withExecutionLog("logs")
    .buildLocal();

executor.executeRecipe( ... )

Recipe Filters

RecipeFilters can be used to augment the underlying TestRecipe for a test before it is executed; for example it could add common authentication settings to all REST Requests, or common property values to all TestSteps of a certain type.

The included RecipeLogger filter logs all generated recipes to a specified folder - which can be useful for debugging/logging purposes or if you want to repurpose these recipes as LoadTests in LoadUI or API monitors using AlertSite

// build a local executor that logs json recipes to a logs folder
RecipeExecutor executor = new RecipeExecutorBuilder()
    .withRecipeFilter(new RecipeLogger("logs"))
    .buildLocal();

executor.executeRecipe( ... )

Similar to the ExecutionLogger above, the RecipeExecutorBuilder has a dedicated withRecipeLog( String logFolder) method:

// build a local executor that logs executions to a logs folder
RecipeExecutor executor = new RecipeExecutorBuilder()
    .withRecipeLog("logs")
    .buildLocal();

executor.executeRecipe( ... )

Execution Results

When an execution finishes the Execution object exposes a RecipeExecutionResult that gives access to a TestStepResult for each executed TestStep. For example if you want to log the response content for all executed TestSteps you can do:

RecipeExecutionResult result = executor.executeRecipe(...);

for( TestStepResult testStepResult : result.getTestStepResults()){
    System.out.println( "TestStep [" + testStepResult.getTestStepName() + "] returned [" + testStepResult.getResponseContent() + "]");
}