Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to fast generate your API Test with OpenAPI Tools and Rest-Assured

Elias Nogueira
February 04, 2023

How to fast generate your API Test with OpenAPI Tools and Rest-Assured

The creation of API Test Automation using Java and Rest-Assured might be boring because it always follows the same repeated pattern: creating the code to make the HTTP requests, the model classes used in the responses, the object mapping, and the tests.

With the usage of the OpenAPI Tools, we can quickly generate the client code and the test stubs, speeding up the development process.

You will also learn the possible problems during its adoption that can add more maintenance effort and how to solve it with abstractions.

You will learn how to use an OpenAPI specification to automatically generate the client code to use in your test, as we will call it basic usage. As the first improvement, you will learn how to use templates and automatically create custom client and test code. And as the ultimate improvement, you will learn how to decrease the maintenance and focus on a more business-driven development by creating abstractions on top of the client code

Elias Nogueira

February 04, 2023
Tweet

More Decks by Elias Nogueira

Other Decks in Technology

Transcript

  1. How to fast generate your
    API Test with OpenAPI Tools
    and Rest-Assured
    Elias Nogueira

    View full-size slide

  2. ELIAS NOGUEIRA
    Senior Principal Software Engineer
    Brazil
    Java Champion
    Oracle ACE
    Java Magazine NL Editor

    View full-size slide

  3. the backend application

    View full-size slide

  4. The backend is composed of two services:
    Simulations API
    CRUD operation to record a loan simulation
    Restrictions API
    GET endpoint to know if a consumer has a restriction, if yes, a simulation is
    not allowed.
    the backend application
    eliasnogueira.com

    View full-size slide

  5. the backend application
    eliasnogueira.com
    POST /simulations
    GET /restrictions
    has
    restriction?
    HTTP 403
    Forbidden
    Restrictions Simulations

    View full-size slide

  6. the (non-existent) frontend
    eliasnogueira.com

    View full-size slide

  7. API documentation

    View full-size slide

  8. api documentation
    eliasnogueira.com

    View full-size slide

  9. REST Assured

    View full-size slide

  10. REST Assured
    http://rest-assured.io
    Java DSL for simplifying testing of REST based services.
    It has an intuitive code syntax to create an automated test.
    Let’s express it in a natural language:
    Given I have a parameter to send
    When I send a request using an HTTP Method an URL
    Then I can validate the status code and response body
    NOTE: this is not a BDD approach and I have no intention to apply it
    eliasnogueira.com

    View full-size slide

  11. REST Assured
    http://rest-assured.io
    REST Assured uses the same keywords given, when, then to express it
    import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    void welcomeTest() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    eliasnogueira.com

    View full-size slide

  12. import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    void welcomeTest() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    REST Assured
    http://rest-assured.io
    import libraries
    REST Assured uses the same keywords given, when, then to express it
    eliasnogueira.com

    View full-size slide

  13. import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    void welcomeTest() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    REST Assured
    http://rest-assured.io
    test method and annotation from JUnit 5
    REST Assured uses the same keywords given, when, then to express it
    eliasnogueira.com

    View full-size slide

  14. import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    void welcomeTest() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    REST Assured
    http://rest-assured.io
    request pre-condition
    REST Assured uses the same keywords given, when, then to express it
    eliasnogueira.com

    View full-size slide

  15. import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    public void welcome() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    REST Assured
    http://rest-assured.io
    action (request)
    REST Assured uses the same keywords given, when, then to express it
    eliasnogueira.com

    View full-size slide

  16. import static io.restassured.RestAssured.*;
    import static org.hamcrest.Matchers.*;
    class RestAssuredExampleTest {
    @Test
    void welcomeTest() {
    given().
    param("name", "Elias").
    when().
    post("/register").
    then().
    statusCode(200).
    body("message", is("Hello Elias"));
    }
    }
    REST Assured
    http://rest-assured.io
    assert the response body
    REST Assured uses the same keywords given, when, then to express it
    eliasnogueira.com

    View full-size slide

  17. test using REST Assured

    View full-size slide

  18. robust framework

    View full-size slide

  19. robust framework
    You have noticed, so far, we are using the “raw” REST Assured usage through
    the methods given(), when(), and then().
    They are a great start, but it won’t scale as the API and test grow.
    One of the possible approaches is to create an abstraction around the main
    REST Assured methods, having minimal work using the OpenAPI spec.
    eliasnogueira.com

    View full-size slide

  20. robust framework
    We are using the Open API spec,
    but the UI version of it
    eliasnogueira.com

    View full-size slide

  21. robust framework
    Normally we can also have the file and use it to generate the basic structure
    of the request and response.
    There’re several tools, and we will use the:
    ○ wagon-maven-plugin: to download the spec
    ○ openapi-generator-maven-plugin: to generate the API client
    based on the spec
    eliasnogueira.com

    View full-size slide

  22. openapi-generator

    View full-size slide

  23. download the spec file
    The wagon-maven-plugin will help us to download a file to a specific
    directory.
    In general, we will tell the plugin to:
    ○ download a file
    ○ describe the file location
    ○ describe the destination
    eliasnogueira.com

    View full-size slide

  24. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    View full-size slide

  25. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    unique id to identify the execution
    action, in case of multiple downloads

    View full-size slide

  26. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    goal from the plugin

    View full-size slide

  27. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    Maven Build Lifecycle that
    will trigger this action

    View full-size slide

  28. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    URL to the file

    View full-size slide

  29. download the spec file
    eliasnogueira.com

    org.codehaus.mojo
    wagon-maven-plugin
    ${wagon-maven-plugin.version}


    download-credit-api-spec

    download-single

    generate-sources


    URL-TO-THE-FILE-INCLUDING-FILE-NAME-AND-EXTENSION

    ${project.basedir}/target/openapiSpecs




    file internal location

    View full-size slide

  30. generate the Client API
    The openapi-generator-maven-plugin will help us to generate the
    Client API and it models based on the Open API file specification.
    In general, we will tell the plugin to:
    ○ look at a specific folder to know the spec file
    ○ define the main, api and model packages
    ○ use REST Assured as a support library
    ○ set the serialization library
    eliasnogueira.com

    View full-size slide

  31. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    View full-size slide



  32. generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    generate the Client API
    eliasnogueira.com
    unique id in case of many generator

    View full-size slide

  33. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    goal to generate the code

    View full-size slide

  34. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    Maven Lifecycle phase that
    will trigger the execution

    View full-size slide

  35. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    Open API spec file location

    View full-size slide

  36. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    the package used for the generated
    invoker (common) objects

    View full-size slide

  37. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    the package used for the
    generated client api

    View full-size slide

  38. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    the package used for the
    generated models

    View full-size slide

  39. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    other configurations

    View full-size slide

  40. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    library used in the client api

    View full-size slide

  41. generate the Client API
    eliasnogueira.com


    generate-client-api-code

    generate

    generate-sources


    ${project.build.directory}/openapiSpecs/credit-api.yaml

    com.eliasnogueira.credit.invoker
    com.eliasnogueira.credit.api
    com.eliasnogueira.credit.model
    java
    false
    false

    rest-assured
    jackson




    serialization library

    View full-size slide

  42. architecture
    Using the raw features of REST Assured for test creation might not scale as
    we need to solve possible duplication along its creation.
    The changes in the API spec will also influence, increasing the code
    maintainability.
    Thankfully, using abstractions and design patterns we can easily solve these
    problems.
    eliasnogueira.com

    View full-size slide

  43. architecture
    Proposed architecture
    eliasnogueira.com
    *Test
    *Test
    *Data
    RestApiClientBuilder
    *ApiClient
    *ApiService
    Client API abstraction Service abstraction
    build uses use

    View full-size slide

  44. architecture
    RestApiClientBuilder
    Each Client API generated by the OpenAPI Generator will carry the full path
    to the related HTTP request.
    SimulationsApi.java
    eliasnogueira.com
    public static class DeleteUsingDELETEOper implements Oper {
    public static final Method REQ_METHOD = DELETE;
    public static final String REQ_URI = "/api/v1/simulations/{cpf}";
    // code ignored

    View full-size slide

  45. architecture
    RestApiClientBuilder
    The main necessity of this class is to add, to all requests, the baseUri, and the
    port. The basePath is not necessary as the generated client already has it.
    eliasnogueira.com
    public class RestApiClientBuilder {
    public T build(Function, T> clientCreator) {
    Supplier requestSpecBuilderSupplier = () -> new RequestSpecBuilder()
    .addRequestSpecification(
    new RequestSpecBuilder()
    .setBaseUri("http://localhost")
    .setPort(8088)
    .build());
    return clientCreator.apply(requestSpecBuilderSupplier);
    }
    }

    View full-size slide

  46. architecture

    View full-size slide

  47. architecture
    *ApiClient
    This will abstract the current Client API class generated by the OpenAPI
    Generator, based on REST Assured, and will use the RestClientApiBuilder
    to add the common request specification.
    The generated Client Api class adds an inner class per HTTP request
    matching the OpenAPI spec.
    eliasnogueira.com

    View full-size slide

  48. architecture
    eliasnogueira.com
    public class RestrictionsApi {
    public static RestrictionsApi restrictions(Supplier reqSpecSupplier) {
    return new RestrictionsApi(reqSpecSupplier);
    }
    public OneUsingGETOper oneUsingGET() {
    return new OneUsingGETOper(createReqSpec());
    }
    }

    View full-size slide

  49. architecture
    eliasnogueira.com
    public class RestrictionsApi {
    public static RestrictionsApi restrictions(Supplier reqSpecSupplier) {
    return new RestrictionsApi(reqSpecSupplier);
    }
    public OneUsingGETOper oneUsingGET() {
    return new OneUsingGETOper(createReqSpec());
    }
    }
    Will be used by the RestClientApiBuilder
    to build the Client Api

    View full-size slide

  50. architecture
    eliasnogueira.com
    public class RestrictionsApi {
    public static RestrictionsApi restrictions(Supplier reqSpecSupplier) {
    return new RestrictionsApi(reqSpecSupplier);
    }
    public OneUsingGETOper oneUsingGET() {
    return new OneUsingGETOper(createReqSpec());
    }
    }
    Inner class with the HTTP method,
    basePath and requests specifics (params)

    View full-size slide

  51. architecture
    eliasnogueira.com
    public static class OneUsingGETOper implements Oper {
    public static final Method REQ_METHOD = GET;
    public static final String REQ_URI = "/api/v1/restrictions/{cpf}";
    @Override
    public T execute(Function handler) {
    // magic
    }
    public static final String CPF_PATH = "cpf";
    public OneUsingGETOper cpfPath(Object cpf) {
    reqSpec.addPathParam(CPF_PATH, cpf);
    return this;
    }
    }

    View full-size slide

  52. architecture
    eliasnogueira.com
    public static class OneUsingGETOper implements Oper {
    public static final Method REQ_METHOD = GET;
    public static final String REQ_URI = "/api/v1/restrictions/{cpf}";
    @Override
    public T execute(Function handler) {
    // magic
    }
    public static final String CPF_PATH = "cpf";
    public OneUsingGETOper cpfPath(Object cpf) {
    reqSpec.addPathParam(CPF_PATH, cpf);
    return this;
    }
    }
    HTTP method and baseUri

    View full-size slide

  53. architecture
    eliasnogueira.com
    public static class OneUsingGETOper implements Oper {
    public static final Method REQ_METHOD = GET;
    public static final String REQ_URI = "/api/v1/restrictions/{cpf}";
    @Override
    public T execute(Function handler) {
    // magic
    }
    public static final String CPF_PATH = "cpf";
    public OneUsingGETOper cpfPath(Object cpf) {
    reqSpec.addPathParam(CPF_PATH, cpf);
    return this;
    }
    }
    adds the HTTP method and baseUri to the request

    View full-size slide

  54. architecture
    eliasnogueira.com
    public static class OneUsingGETOper implements Oper {
    public static final Method REQ_METHOD = GET;
    public static final String REQ_URI = "/api/v1/restrictions/{cpf}";
    @Override
    public T execute(Function handler) {
    // magic
    }
    public static final String CPF_PATH = "cpf";
    public OneUsingGETOper cpfPath(Object cpf) {
    reqSpec.addPathParam(CPF_PATH, cpf);
    return this;
    }
    }
    param name and method to add it into the request

    View full-size slide

  55. architecture
    *ApiClient Creation
    We need to use the RestApiClientBuilder to build the Client Api instance to
    add the URL and port.
    Then we add a method per HTTP request. This is a recommended approach
    to ease any change (even to a different library).
    eliasnogueira.com

    View full-size slide

  56. architecture
    eliasnogueira.com
    public class RestrictionsApiClient {
    private RestrictionsApi restrictionsApi =
    new RestApiClientBuilder().build(RestrictionsApi::restrictions);
    public Response queryCpf(String cpf) {
    return restrictionsApi.oneUsingGET().cpfPath(cpf).execute(Function.identity());
    }
    }
    build of the Client Api using
    the RestApiClientBuilder

    View full-size slide

  57. architecture
    eliasnogueira.com
    public class RestrictionsApiClient {
    private RestrictionsApi restrictionsApi =
    new RestApiClientBuilder().build(RestrictionsApi::restrictions);
    public Response queryCpf(String cpf) {
    return restrictionsApi.oneUsingGET().cpfPath(cpf).execute(Function.identity());
    }
    }
    abstracting the internal (ugly) Client Api usage
    - returning a generic response
    - adding a meaningful name
    - adding the path parameter as the method parameter

    View full-size slide

  58. architecture
    eliasnogueira.com
    public class RestrictionsApiClient {
    private RestrictionsApi restrictionsApi =
    new RestApiClientBuilder().build(RestrictionsApi::restrictions);
    public Response queryCpf(String cpf) {
    return restrictionsApi.oneUsingGET().cpfPath(cpf).execute(Function.identity());
    }
    }
    using the auto-generated
    internal Client Api method

    View full-size slide

  59. architecture
    eliasnogueira.com
    public class RestrictionsApiClient {
    private RestrictionsApi restrictionsApi =
    new RestApiClientBuilder().build(RestrictionsApi::restrictions);
    public Response queryCpf(String cpf) {
    return restrictionsApi.oneUsingGET().cpfPath(cpf).execute(Function.identity());
    }
    }
    using the path parameter

    View full-size slide

  60. architecture
    *ApiService
    The *ApiService abstraction will use the *ApiClient abstraction to consume
    its methods in different ways. This is the class we will use in the tests.
    The service can have one or multiple actions from the *ApiClient and it can
    be related to the Mediator design pattern, as it encapsulates how a set of
    objects (methods) interact.
    eliasnogueira.com

    View full-size slide

  61. architecture
    *ApiService - Example
    We do have two test for the Restrictions API:
    ○ Expecting a restriction
    ○ Not expecting a restriction
    We will create the request for both, returning the correct response, in the
    service abstraction.
    eliasnogueira.com

    View full-size slide

  62. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    }

    View full-size slide

  63. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    }
    instance of the abstracted Client Api

    View full-size slide

  64. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    }
    method to query the cpf expecting no restriction

    View full-size slide

  65. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    }
    returning true because the
    status code is an HTTP 404
    so, we can add an assertions
    in the test

    View full-size slide

  66. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    }
    method to query the cpf expecting a restriction

    View full-size slide

  67. architecture
    eliasnogueira.com
    public class RestrictionsApiService {
    private RestrictionsApiClient restrictionsApiClient = new RestrictionsApiClient();
    /**
    * Query CPF without a restriction
    */
    public boolean queryCpf(String cpf) {
    restrictionsApiClient.queryCpf(cpf).then().statusCode(HttpStatus.SC_NOT_FOUND);
    return true;
    }
    public MessageV1 queryCpfWithRestriction(String cpf) {
    return restrictionsApiClient.queryCpf(cpf).then().
    statusCode(HttpStatus.SC_OK).extract().as(MessageV1.class);
    }
    } it returns the expected
    response body

    View full-size slide

  68. architecture
    Test
    Now the tests will be created using only the Service class.
    The different is that we don’t need to use the raw REST Assured methods
    anymore, relying only in the Service.
    The structure of precondition, action and assert will be always present in
    the new way to create tests.
    eliasnogueira.com

    View full-size slide

  69. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    given()
    .spec(SharedRequestSpecs.cpfPathParameter("62648716050"))
    .when()
    .get("/restrictions/{cpf}")
    .then()
    .statusCode(HttpStatus.SC_OK)
    .body("message", CoreMatchers.is("CPF 62648716050 has a restriction"));
    }
    Raw REST Assured Test

    View full-size slide

  70. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    given()
    .spec(SharedRequestSpecs.cpfPathParameter("62648716050"))
    .when()
    .get("/restrictions/{cpf}")
    .then()
    .statusCode(HttpStatus.SC_OK)
    .body("message", CoreMatchers.is("CPF 62648716050 has a restriction"));
    }
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }
    Raw REST Assured Test
    REST Assured Test using Client – Service abstraction

    View full-size slide

  71. architecture
    Test
    The usage of the service abstraction add more readability and help us to
    decrease the maintainability as we will have only one place to change it
    behaviour (client or service).
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }

    View full-size slide

  72. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }

    View full-size slide

  73. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }
    instance to use the Service abstraction

    View full-size slide

  74. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }
    usage of the method in the service

    View full-size slide

  75. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    } associating the correct return (response body)

    View full-size slide

  76. architecture
    eliasnogueira.com
    @Test
    void shouldReturnRestriction() {
    RestrictionsApiService restrictionsApiService = new RestrictionsApiService();
    MessageV1 message = restrictionsApiService.queryCpfWithRestriction("60094146012");
    Assertions.assertThat(message.getMessage()).contains("60094146012");
    }
    assertion using the response body object

    View full-size slide

  77. architecture
    BaseApiConfiguration
    Within the new approach, the general configuration is now being applied
    using the RestClientApiBuilder class, as it created a common request
    specification for all the requests.
    We can either move the previous configurations from the
    BaseApiConfiguration class to the RestClientApiBuilder or continue to
    use the BaseApiConfiguration without the baseUri, basePath, and port.
    eliasnogueira.com

    View full-size slide

  78. architecture
    Recommendation
    A better approach is to use the RestClientApiBuilder to deal only with the
    global request actions and the BaseApiConfiguration with the
    configurations related to the test.
    Both classes do different things, and they must have a single responsibility.
    eliasnogueira.com

    View full-size slide

  79. THANK YOU!
    @eliasnogueira

    View full-size slide