Skip to content

User journey: Create, Update and GET Employee Details

authorjapps edited this page Sep 8, 2020 · 52 revisions

Visit here for a quick introduction to What is Declarative Testing And Zerocode

Table Of Content

Quick Overview

In Declarative Testing, the framework here does the job for us behind the scene i.e.

  • Making Http calls to the target end-point, with our request payload
  • Receiving the server response into the test case
  • Doing the result comparison of the actual vs expected response for our "assertions".
    • Here we can choose to skip the fields we do not need to assert
    • Or keep some fields as not null if server response is in-deterministic
    • We keep the payload as JSON structure as it is or we have the option to use the flat key-value assertions using the JSON Path
    • We have the option of comparing the results in LINIENT or STRICT mode

It saves us from the major hassles of writing any code to do the above repetitive tasks.

Let's see how it is applied to a user journey.

USER JOURNEY - Acceptance Criteria(ACs)

  • AC1
GIVEN- The Create API POST:"/api/v1/employees"
WHEN I invoke the POST operation with a "employee" payload 
and content-type as "application/json"
THEN I will create a new employee 
AND validate the 201(created) status 
and verify the response has only the employeeId(strict matching).
  • AC2
GIVEN- The Get API GET:"/api/v1/employees/{employeeId}"
WHEN I invoke the GET operation with max 5 retries with 1sec gap
THEN I will fetch the employee details 
AND validate the status as 200(OK) along with 
AND response has the expected payload with the required fields(lenient matching).

To write a test-case for the above CRUD operation scenario is quite easy using Zerocode, just our IDE's JSON editor is easy enough to hook these rwo steps/ACs. For instance, POST and GET step would look like below(simple and clean).

Hosts and ports are externalized to a properties file for env switching

And at the same time we don't have to search for or think hard of any syntaxes to do the job. That means, we are ready with a BDD test scenario quickly with these simple JSON steps(see below).

The advantage here is the tests are instantly readable to any stakeholder

View the JSON or YAML based test-scenario below.

Using JSON

JSON (Click to expand)

{
    "scenarioName": "Validate the POST and GET employee API",
    "steps": [
        {
            "name": "create_emp",
            "url": "/api/v1/employees",
            "method": "POST",
            "request": {
                "headers": {
                    "Content-Type": "application/json"
                },
                "body": {
                    "id": "GOV-CROY-9001",
                    "name": "Oliver",
                    "postcode": "EC2 9XY"
                }
            }, 
            "retry": {
                "max": 3, //<--- configure to your "retry" requirement or skip it
                "delay": 1000
             },
            "verify": {
                "status": 201,
                "body": {
                    "id": "GOV-CROY-9001"
                }
            },
            "verifyMode": "STRICT"
        },
        {
            "name": "get_emp",
            "url": "/api/v1/employees/${$.create_emp.response.body.id}",
            "method": "GET",
            "retry": {
                "max": 5,
                "delay": 1000
            },
            "request": {
                "headers": {
                    "Content-Type": "application/json"
                }
            },
            "verify": {
                "status": 200,
                "body": {
                    "id": "${$.create_emp.response.body.id}",
                    "deptCode": "GOV.ASY"
                }
            },
            "verifyMode": "LENIENT"
        }
    ]
}

Using YAML

YAML (Click to expand)

Or you can use YAML DSL(e.g. when inside a Kubernetes/docker based infrastructure)

  • YAML DSL
---
scenarioName: Validate the POST and GET employee API
steps:
- name: create_emp
  url: "/api/v1/employees"
  method: POST
  retry:
    max: 3
    delay: 1000
  request:
    headers:
      Content-Type: application/json
    body:
      id: GOV-CROY-9001
      name: Oliver
      postcode: EC2 9XY
  verify:
    status: 201
    body:
      id: GOV-CROY-9001
  verifyMode: STRICT
- name: get_emp
  url: "/api/v1/employees/${$.create_emp.response.body.id}"
  method: GET
  retry:
    max: 5
    delay: 1000
  request:
    headers:
      Content-Type: application/json
  verify:
    status: 200
    body:
      id: "${$.create_emp.response.body.id}"
      deptCode: GOV.ASY
  verifyMode: LENIENT

That's it, done. We are ready to run the above scenario.

The hosts and ports in the url fields are externalized as usual(explained below).


Then we stick the above json/yml file to a JUnit runner and run. We can point to any host and port in the Runner. See the sample below.

@TargetEnv("application_host.properties")
@RunWith(ZeroCodeUnitRunner.class)
public class JustHelloWorldTest {

    @Test
    @Scenario("helloworld/user_crud_journey_test.json")
    public void testGet() throws Exception {
       // No code is needed here.
    }
}

the application_host.properties looks as below:

web.application.endpoint.host=https://hbc.banking.co.local.uk
web.application.endpoint.port=443
web.application.endpoint.context=
                             Done. Happy days!

If You Have Time To Read

If you have little more time to read below, see what all hassles we escaped and how much time we saved !

  • No need to write pojos and builder for the domain objects(e.g. Employee here)
  • No need of any serialization/deserialization
  • No need of http client calls and read the response
  • No need to assertThat(expected, is(actual)) etc multiple times
  • No need of any feature files and syntax searchings
  • No need of English statements and grammars

What We Did Not Have to Do(luckily)

JOURNEY1 :

  • Step 1

Employee emp = EmployeeBuilder.aNewInstance(); .name("Larry P") .job("Full Time") .build()

Make the POST call

ObjectMapper objectMapper = ObjectMapperProvider.getInjectedObjectMapper();

HttpResponse postResponse =

aHttpClient.post("http://host:port/api/v1/persons")

.header("accept", "application/json")

.body(objectMapper.writeValueAsString(emp))

.execute();

Assert the response

assertThat(postResponse.getStatusCode(), is(201))

assertThat(postResponse.getEntity().getId(), is(1001))

  • Step 2

Create an employee with updated payload

Employee empForUpdate = EmployeeBuilder.aNewInstance() .name("Larry Page") .job("Co-Founder") .build();

Make a PUT call

HttpResponse putResponse =

aHttpClient.put("http://host:port/api/v1/persons/" + postResponse.getEntity().getId())

.header("accept", "application/json")

.body(objectMapper.writeValueAsString(empForUpdate))

.execute();

Employee empUpdated = response.getUser();

Assert the response

assertThat(putResponse.getStatusCode(), is(200))

assertThat(empUpdated.getName(), is(empForUpdate.getName()))

assertThat(empUpdated.getJob(), is(empForUpdate.getJob()))

  • Step 3

Make the GET call

HttpResponse response =

aHttpClient.get("http://host:port/api/v1/persons/" + postResponse.getEntity().getId())

.header("accept", "application/json")

.execute();

Employee empFetched = response.getEmployee();

Assert the response

assertThat(response.getStatusCode(), is(200))

assertThat(empFetched.getName(), is(empForUpdate.getName()))

assertThat(empFetched.getJob(), is(empForUpdate.getJob()))



Also, we escaped the hard way of doing things with special attention to English statements and grammars. See below:


This approach might take different shapes and forms for developers/test-engineers who need to spend lot of time agreeing on the semantics rather than spending time in creating actual executable test-scenarios.

e.g.
GIVEN- the REST api POST end point,
WHEN- I invoke the API with a payload,
THEN- I will receive 201(Created) status with a newly created ID and assert the response

or

GIVEN- the REST url and the method POST,
WHEN- I invoke the API with a body,
THEN- I will receive 201(Created) status with newly created ID
AND assert the response

or

~~GIVEN- the REST url /api/v1/persons/ ~~
~~AND the http method POST ~~
WHEN- I invoke the API using a HTTP client and send the body,
THEN- I will receive 200(OK) status with body
AND assert the response

and so on...

Note- Too much is going on the above around an user journey, in terms of writing correct sentences or nearly correct sentences/grammars, too many assertThats to come up with a test scenario.

And imagine the maintenance overhead and difficulty in tracing the steps, when we have more number of steps in an user journey !


:::Note:::

It might(arguably) make sense for the BAs(Business Analysts) or non-technology folks while creating the stories and defining the entry and exit criteria of the tickets for a business scenario or User-Journey. But the techincal folks simply picking these statements and trying hard syntactically to fit these into the executable scenarios seems like bit too much of a wastage of time and maintenance overhead.

But we should choose the tools, technologies and solutions which best fits to our project and situation and really helps us solving the testing challenges.

See this in action(HelloWorld):

The simplified HelloWorld projects are in GitHub repo to clone and run locally

Blogs

Clone this wiki locally