Automated Testing with Kafka and RESTful APIs

Real and Practical Projects
Aila Bogasieru

*Acknowledgements

This book would not have been possible without my family, especially my parents. Special thanks to Dumitru Bogasieru and Ioana Bogasieru.
In memory of my beloved father, Dumitru Bogasieru, I will carry you in my thoughts and heart every day and forever. I would like to thank you for all your love and affection, support, and education.

Thank you for passing on your critical and analytical thinking to me. I would like to express my deepest gratitude to my father, whose hard work, honesty, dedication, and determination always inspired me to grow and to be the person I am today, which gave me a great sense of belief in my work, being a constant and a great source of strength.

I carry on your genetic heritage, I can see myself in you through your strength, independence, straightforwardness, and decisiveness.
I am extremely grateful for all the moments we have together, and this brings me a lot of joy, but at the same time, it hurts me terribly that there will be no new ones.

I would like to express my deepest gratitude to my mother for sticking with me. I know it was not easy, I am very thankful for her advices and talks. This book would not have been possible without their unspoken guidance and inspiration.

This book would not have been possible without all my professional experiences and opportunities, projects and technologies, my co-workers, IT fellows guiding, encouraging, helping, and criticizing me along the way to be a better and improved version of myself.

I also acknowledge the help I have received from people who have read sections of the book and provided feedback.
Introduction

About the Author

With more than a decade of experience as a Software Engineer, I began my journey into test automation in 2010.

My passion for technology has taken me through diverse domains: e-Commerce, transportation, finance, and streaming platforms, where I have helped build scalable, reliable systems with a strong focus on automation and architecture.

About the Book

This book is a hands-on guide for developers and testers who want to work with RESTful APIs, Kafka, and event-driven systems, without getting lost in abstract theory.

It is filled with real-world code, architecture patterns, and automation workflows that I have used across projects aimed at helping you implement practical solutions in complex environments.

I am excited to share what I have learned and continue learning from the amazing tech community.

What You’ll Find Inside

• RESTful API Design — Learn to structure scalable, maintainable APIs with practical, real-world examples.
• Kafka Integration — Get started with real-time event streaming, message brokers, and microservices using Apache Kafka.
• Live Coding Showcases — Code from scratch, understand how it works, and grasp the ”why” behind each architectural choice.
• Production-Friendly Practices — Built-in testing, clean code, and modular design ready for real-world deployment.

Who This Book is For

Whether you are:
• A backend developer looking to level up your architecture game
• A software engineer exploring event-driven systems
• A curious coder who prefers building over theorizing...

...this book has something for you.

Why I Wrote This Book

Automating end-to-end tests is hard when your system uses Kafka, APIs, and multiple databases.

I wanted a way to:
• Trigger real events
• Validate asynchronous flows

• Make it all reproducible So I built a series of examples and wrapped them in this book. Available now on Amazon Kindle & Paperback.

Although there is an abundance of documentation and courses available on platforms such as YouTube, Coursera, Udemy, and Pluralsight, few truly demonstrate how to build a reliable and maintainable testing framework.
Even with the latest and powerful AI, capable of gathering, structuring and summarizing the data from multiple sources, also capable to learn and adapt, I still think that in order to navigate through the automation seas you need to have a string foundation and structured thinking to manage successfully any software engineering product especially for technologies such as Restful APIs and Kafka so popular now days.

Kafka is such a powerful technology that is used by large companies for real-time data streaming, event-driven architectures, and data integration.
Several tech companies that use Kafka technology should be referred to:

• LinkedIn – Originally developed by Kafka and continues to be to use it extensively.

• Netflix – uses Kafka for real-time monitoring and operational data pipelines.
• Uber - handles high-throughput messaging between microservices with Kafka.
• Airbnb – employs Kafka for logging, metrics, and analytics pipelines.

• Goldman Sachs – Builds event-driven architectures and
data pipelines using Kafka.
• JPMorgan-Chase – uses Kafka for real-time fraud detection and trade monitoring.
• PayPal – processes and analyzes financial transactions.
in real time with Kafka.

• Walmart – leverages Kafka for real-time inventory and analytics.
• Alibaba – processes billions of messages per day through Kafka.
• BMW – utilizes Kafka in their IoT platform for connected cars.
• Spotify – integrates Kafka for real-time data processing and integration.

Kafka is also widely used across industries for log aggregation, stream processing, fraud detection, and real-time analytics.

Keeping it simple is essential.
Adding unnecessary complexity can hinder the effectiveness of your automation framework.
My technology stack primarily consists of Java, Selenium (for web applications), Rest Assured (for RESTful APIs), JUnit/TestNg (as testing frameworks), Apache Maven (as building tool), and CI/CD tools like Jenkins. Over the past four years, there has been a notable shift toward Python, leading to a decline in Java’s popularity.
The pandemic further accelerated the demand for AI-driven projects, redefining automation’s practices.

Concurrently, ”low-code” platforms such as Katalon and ReadyAPI have emerged, yet they often fall short in delivering the necessary speed, robustness, and reliability. These platforms exhibit significant limitations, particularly the absence of objectoriented programming (OOP) capabilities, which are crucial for creating scalable and eﬀicient test automation solutions.
In the first years of automation testing, automation frameworks were built from scratch (Selenium was created later on ), so every click, list, button, and text field needed to be created, so more time and effort were put into the automation testing frameworks.

As technology continues to evolve, we see enhancements in performance, data handling, and user-friendly interfaces for web applications. This evolution extends beyond web applications to encompass desktop and mobile applications, as well as RESTful APIs, broadening the scope of automation from basic frameworks to more scalable and robust solutions.
In the first years of automation testing, automation frameworks were built from scratch (Selenium was created later on ), so every click, list, button, and text field needed to be created, so more time and effort were put into the automation testing frameworks.
Once Selenium gained traction and popularity because having amazing capabilities to save a lot of hard work on coding, bringing solutions to handle multi browsers, languages, among others, it was clearly a step ahead for automation testing. In my humble opinion, it is more important to focus on the logic behind writing tests than on the specific framework or programming language used, the specific framework or programming language, but on the underlying logic of writing robust tests. While the choice of technology is indeed vital, it is equally important to architect automation frameworks that are streamlined and eﬀicient, leveraging the capabilities of various libraries.

Even though we are living in a society where technology and speed are playing an important role, just a few clicks and we have our products delivered, streaming platforms available (no need to actually go to a cinema theater), basically we have applications to solve almost any requirement, we should not forget and be aware about the quality of the software products that could be significantly impacted.

Another important factor to be considered in the product’s quality might be the shift from oﬀice work to remote work and this resulted in cost-eﬀiciency alternatives by hiring people all over the world, not that skilled and if you include also the AI increasing popularity resulting in the projects’ trajectory. Part 1: Introduction to Automation Testing

• Chapter 1: Understanding the Basics of Automation Testing

• Benefits of automation testing
• Types of automation testing (unit, regression, system, end-to-end and integration)
• Challenges and misconceptions
• Factors to consider when selecting a framework

• Chapter 2: API Testing

• RESTful APIs automated tests design
• Okta Token Authentication
• Oauth 2.0 Authentication

Part 2: Practical Automation Techniques

• Chapter 3: Setting Up Your Automation Environment

• Installing necessary tools and libraries
• Configuring your testing environment

• Chapter 4: Creating Effective Test Scripts

• Writing maintainable and reusable test scripts
• Best practices for test script design

• Chapter 5: Continuous Integration and Continuous Delivery (CI/CD)

1. Integrating automation testing into CI/CD
2. Benefits of CI/CD
3. Best practices for CI/CD implementation

• Chapter 6: Low-Code Platforms in Practice

1. Overview of low-code platforms (Katalon, ReadyAPI) 2. Key limitations (e.g., lack of OOP, redundancy issues)
Part 3: Case Studies and Best Practices
• Chapter 7: Real-World Automation Success Stories
1. Case studies of successful automation implementations for web apps, RESTful APIs, Kafka streams and Databases
2. Lessons learned and best practices
Part 4: Overcoming Common Challenges

• Chapter 8: Addressing Common Issues in Automation Testing

1. Tips for troubleshooting and debugging

• Part 1: Introduction to Automation Testing

◦ Chapter 1: Understanding the Basics of Automation Testing

Benefits of Automation Testing. In today’s fast-paced technological landscape, the standards for software quality are higher than ever. With the ability to order various products at the click of a button, staying competitive is paramount. Automation testing significantly reduces the time and costs associated with manual processes. Although manual testing is essential for certain tasks, particularly those unsuitable for automation, it often proves tedious and labor-intensive. The benefits of automation testing are clear:

• 1. Higher Productivity: Automated tests can run continuously, freeing up valuable resources.

• 2. Reliability: Automated tests are less prone to human error.
• 3. Availability: Tests can be executed at any time, enhancing eﬀiciency.

• 4. Increased performance: Automated testing allows for more thorough testing in less time.
• 5. Reduced Operating Costs: Automation minimizes the resources required for repetitive testing tasks.

Types of Automation Testing:
• 1. Unit Testing
• 2. Integration Testing
• 3. System Testing
• 4. End-to-End Testing
• 5. Regression Testing

Unit testing focuses on testing individual components or functions of the code in isolation. The goal is to verify that each part of the program behaves as expected. Integration testing examines the interactions between different modules or services to ensure that they work together as intended. This can include testing APIs, databases, or any other external systems.
System testing evaluates the complete and integrated software system to ensure it meets the specified requirements.
This type of test is typically performed in an environment that simulates real-world conditions. End-to-end testing simulates real user scenarios to verify the application flow from start to finish. This type of testing checks the entire application environment, including user interfaces and databases.
Regression testing involves running previously executed tests to ensure that new code changes have not adversely affected existing functionalities. It is essential to maintain software integrity over time.
Each type of automation testing serves a unique purpose in the software development life cycle, ensuring quality, performance, and reliability. Using these various testing strategies, teams can deliver robust software solutions that meet user expectations and industry standards.
Challenges and Misconceptions.
Automation in testing is a powerful tool that can significantly improve the eﬀiciency and effectiveness of the software development process. However, misconceptions about its scope can lead to ineffective implementations and suboptimal results. Here are some common misunderstandings:

• 1. Automation can replace all testing, in reality: while automation is beneficial for repetitive and regression tests, it cannot fully replace manual testing. Exploratory testing, usability testing, and tasks that require human intuition or creativity are best performed by humans. Understanding when to automate and when to rely on manual testing is crucial.

• 2. Automation equals faster results, in reality: automation does speed up certain processes, but setting up automated tests can be time-consuming and resource-intensive. Initial investments in time and effort are necessary to create a comprehensive automated test suite. The benefits of automation often come in the long run, through continuous execution and maintenance.

• 3. All tests are good candidates for automation, in reality: not all tests should be automated. Tests that are infrequently run, or those that change often, may not be worth the investment. A good rule of thumb is to automate tests that are repetitive, high-risk, or critical to the functionality of the application.

• 4. Automation guarantees quality, in reality:
while automated tests can detect many issues, they do not guarantee software quality. Tests must be designed thoughtfully, and the quality of the tests themselves is paramount. Poorly designed tests can lead to false positives or negatives, reducing confidence in the testing process.

• 5. Tools are everything, in reality: the choice of automation tools is important, but the effectiveness of automation is more based on the testing strategy, test design, and team skills. A good automation framework, clear testing objectives, and a well-trained team can often outweigh the benefits of using high-end tools.

• 6. Once automated, tests are set and forget, in reality: automated tests require regular maintenance to remain effective. Changes in the application can render tests obsolete, and test scripts must be updated to reflect these changes. Continuous integration practices help ensure that tests are kept current and relevant.

• 7. Automation is only for large projects, in reality: while larger projects may benefit from extensive automation, smaller projects can also gain significant advantages from automating key tests. The scope of automation should be based on the specific needs of the project rather than the size.

Conclusion : Understanding the true scope of automation in testing is crucial for any software development team. By addressing these misconceptions, teams can implement automation more effectively, maximizing its benefits, while ensuring that both manual and automated testing play essential roles in delivering high-quality software.

Factors to consider when selecting a framework. Building an automation testing framework is not an easy job, even though there is a wide list of available frameworks on the market to fit best all the needs of the project. The most important key aspects that we should consider are: programming language, ecosystem, features and capability, documentation, learning curve, scalability, maintenance, and updates.

Programming language is the most important aspect of a testing framework. I used to work on projects in which the implementation programming language was different from the automation testing (Java and C). In terms of troubleshooting and CI/CD is definitely better to have the same programming language.This should not be a rule though, in the sense that the automation testing frameworks should be stand-alone and autonomously. It is important to consider a programming language for which libraries, tools, and community support are accessible to you.

Features and capabilities- each programming language has its strong capabilities and its limitations. A robust assertion library is essential for writing clear and concise automated tests. Compatibility with other frameworks is extremely important. Also, reporting is another aspect to take into account. Community and support - a large and active community provides valuable resources, support and contributions. Documentation and tutorials can help users learn and use the framework eﬀiciently. Learning curve - how easy or diﬀicult is the framework to be learned. with the amount of resources available: time and documentation. Scalability - project size and complexity can be handled by the framework, also performance of the tests needs to be considered with the increase of test coverage.

• Chapter 2: API Testing

◦ RESTful APIs automated tests design
◦ Okta Token Authentication
◦ Oauth 2.0 Authentication

REST API stands for Representational State Transfer API. It is a type of API (Application Programming Interface) that allows communication between different systems over the Internet. REST APIs work by sending requests and receiving responses, typically in JOSN format, between the client and the server.

REST APIs use HTTP methods (such as GET, POST, PUT, DELETE) to define actions that can be performed on resources. These methods align with CRUD (Create, Read, Update, Delete) operations, which are used to manipulate resources over the web.

How the data could be accessed by a call request depends on the authentication involved, could be basic using username and password, bearer token, Okta Token and Oauth 2.0

• Basic authentication To demonstrate how an automation framework can be built for RESTful APIs, Basic authentication, we could consider a real-world project:

Case 1: Identity Access Management.
Technologies stack: Java, TestNG, rest assured, Apache Maven.

Firstly, we will define the API’s payload as a HashMap containing all keyvalue pairs, where each key corresponds to a field and each value corresponds to its value in the message body.

Secondly, we will define the Rest-assured connector to create our request in the upcoming test.

RestAssuredConnector class defines the capability for the API: POST, GET, PUT, PATCH, DELETE and the properties set on the header-level method. PostDeviceCreateData class builds up the Json payload for the RESTful API Call. Data modeling for the Json payload is illustrated in the following.

1 package com.IAM.apis;
2 import com.google.gson.Gson;
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;

6

7 public class PostDeviceCreateData {
8 public static String generateCreatePostBody (
9 String name, String hostName,
10 String serialNumber,String partnerAddress,

11 String partnerContName,String partnerContEmail,
12 String partnerType,String isStrategicPartner,
13 String partnerStartDate,String partnerEndDate,
14 String iamDevContactName,String iamDevContactEmail,
15 String iamDevReqdBy,String iamDevContactTeam,
16 String partnerAddInfo)

17
18 List<HashMap<String, String>> results = new ArrayList<>();
19
20 HashMap<String, String> devices = new
→ HashMap<String,String>(15);
21

22 devices.put("name", name);
23 devices.put("hostName", hostName);
24 devices.put("serialNumber", serialNumber);
25 devices.put("partnerAddress", partnerAddress);
26 devices.put("partnerContName", partnerContName);
27 devices.put("partnerContEmail", partnerContEmail);
28 devices.put("partnerType", partnerType);
29 devices.put("isStrategicPartner", isStrategicPartner);
30 devices.put("partnerStartDate", partnerStartDate);
31 devices.put("partnerEndDate", partnerEndDate);
32 devices.put("iamDevContactName", iamDevContactName);
33 devices.put("iamDevContactEmail", iamDevContactEmail);
34 devices.put("iamDevReqBy", iamDevReqBy);
35 devices.put("iamDevContactTeam", iamDevContactTeam);
36 devices.put("partnerAddInfo",partnerAddInfo);

37

38 results.add(devices);
39 String devicePayload = new Gson().toJson(results);
40 return devicePayload;

41 }
RestAssuredConnector class defines the methods allowed for the Restful API: GET, POST, DELETE, PUT, PATCH.
As we go along with other examples we will add the needed methods, our class defined with POST method:

1 import io.restassured.builder.RequestSpecBuilder;
2 import io.restassured.http.Headers;
3 import io.restassured.response.Response;
4 import io.restassured.specification.RequestSpecification;
5 import java.util.HashMap;
6 import java.util.Map;
7 import static io.restassured.RestAssured.given;
8 public class RestAssuredConnector {
9 public RequestSpecification request = new

10 RequestSpecBuilder().build();
11 public static Response response;
12 public static void main(String[] args) {
13 RestAssuredConnector rac = new
14 RestAssuredConnector();
15 System.out.println(response.asString());

16 }

17 public Response postRequest(String uri, Map<String,
18 String> headers, String body) {
19 request = given().baseUri(uri).body(body).headers(headers);

20
21 return request.when().post();
22 }

23 public static Map<String, String> setHeaders(String
24 token) {
25 Map<String, String> headers = new HashMap<>();

26

27 headers.put("Content-Type", "application/json");
28 headers.put("Authorization", token);
29 return setHeaders;

30 }
31 }

APICreateOrderPositiveTest class will contain the necessary constants representing the stored values for the fields, setupData() method sets the url and request body for our RESTful API, all the properties are read from localconfig.properties file.

1 import io.restassured.response.Response;
2 import org.junit.Assert;
3 import org.junit.jupiter.api.Test;
4 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory;
6 import java.util.Map;
7 import static org.junit.Assert.assertEquals;
8 public class API_CreateOrderPositiveTest {

9

10 protected static final Logger logger =
11 LoggerFactory.
12 getLogger(API_CreateOrderPositiveTest.class);

13
14 public static final String ID =
→ GenerateRandomDataUtils.generateRandomNumber(1);
15
16 public static final String PET_ID =
→ GenerateRandomDataUtils.generateRandomNumber(1);
17
18 public static final String NAME = "AutoRandom"
19 +

20 GenerateRandomDataUtils.generateRandomNumber(5);
21 public static final String HOST_NAME = "AutoRandom"+
22 GenerateRandomDataUtils.generateRandomNumber(5);
23 public static final String SERIAL_No =
24 GenerateRandomDataUtils.generateRandomNumber(10)
25 public static final String PARTNER_ADDRESS =
26 GenerateRandomDataUtils.generateRandomLetters(10)

27

28 public static final String PARTNER_NAME = "AutoRandom" +
29 GenerateRandomDataUtils.generateRandomNumber(5);
30 public static final StringPARTNER_EMAIL = "autoa@gmail.com"
31 public static final PARTNER_TYPE = "type"
32 public static final IS_STRATEGIC_PARTNER = "Yes"
33 public static final PARTNER_START_DATE = "10-10-2025"
34 public static final PARTNER_END_DATE = "10-10-2026"
35 public static final CONTACT_NAME = "Auto"
36 public static final CONTACT_EMAIL= "autoa@gmail.com"
37 public static final REQUESTED_BY = "AutoTest"
38 public static final CONTACT_TEAM = "Auto team"
39 public static finalPARTNER_ADD_INFO = "Random info"
40 private RestAssuredConnector connector = new
41 RestAssuredConnector();
42 private String requestBody;
43 private String requestUri;

44

45 @BeforeTest
46 public void setupData() {
47 PostDeviceCreateData requestDevice = new → PostDeviceCreateData();

48

49 requestDevice.requestDevice(NAME, HOST_NAME,
50 SERIAL_NP, PARTNER_ADDRESS, PARTNER_NAME,
51 PARTNER_EMAIL, PARTNER_TYPE,
52 IS_STRATEGIC_PARTNER, PARTNER_START_DATE,
53 PARTNER_END_DATE, CONTACT_NAME, CONTACT_EMAIL,
54 REQUESTED_BY, CONTACT_TEAM, PARTNER_ADD_INFO)
55 requestDevice = requestMessageBody.toString();
56 requestUri =
→ ConfigUtils.getProperty(ConfigKeys.BASE_URL_DEVICE.
57 toString())+ConfigUtils.
58 getProperty(ConfigKeys.END_URL_DEVICE.toString());

59 }
60

61 @Test
62 public void createPostOrderTest() {
63 logger.info("Making a POST report Call...)
64 Map<String, String> headersDevice =

65 RestAssuredConnector.setHeaders(ConfigUtils.
66 getProperty(ConfigKeys.TOKEN.toString()));
67 Response createDeviceResponse =
68 connector.postRequest(requestUri,requestHeaders, requestBody);

69

70 logger.info("requestUri" + requestUri + "" +
71 "requestBody: " + requestBody);
72 createDeviceResponse.getBody().asString());
73 assertEquals(createDeviceResponse.getStatusCode(),
74 200);

75 }
76 }

Case 2 real-world project : dxp-data: gathers data from multiple systems: mParticle, Amplitude, Analytics, Youbora, Stripe.
RESTful-APIS between Stripe-Sigma, IMSmParticle, BI data Report - CDS, basic authentication.

RESTful APIs Technology Stack: Java, Junit, rest assured.
The architecture of the project is shown below.

Figure 1: Post Report Create Model The logic implemented is the same as in the previous example, the difference is that for each RESTful API the data is modeled using Setters and Getters. Payload:

1 {
2 "date": "24-09-2025",
3 "network" :"app",
4 "type": "type",
5 "reason":"reason",
6 "event":"event",
7 "count":1
8 }

For each field from the payload we are creating a Setter and a Getter as we can see in the code below.

1 package com.dxp.data.apis;
2 public class PostReportCreateModel {
3 private String date;
4 private String network;
5 private String app;
6 private String type;
7 private String reason;
8 private String event;
9 private Integer count;
10 public void setDate(String date) { 11 this.date = date;

12 }
13 public void setNetwork(String network) { 14 this.network = network;
15 }
16 public void setApp(String app) { 17 this.app = app;
18 }
19 public void setType(String type) { 20 this.type = type;
21 }
22 public void setReason(String reason) { 23 this.reason = reason;
24 }
25 public void setEvent(String event) { 26 this.event = event;
27 }
28 public void setCount(Integer count) { 29 this.count = count;
30 }
31

32 @Override
33 public String toString() {
34 return "{\n" +
35 " \"date\": \"" + date + "\",\n" +
36 " \"network\": \"" + network + "\",\n" +
37 " \"app\": \"" + app + "\",\n" +
38 " \"type\": \"" + type + "\",\n" +
39 " \"reason\": \"" + reason + "\",\n" +
40 " \"event\": \"" + event + "\",\n" +
41 " \"count\": " + count + "\n" +
42 "}";

43 }
44 }
How the tests are designed in the project is illustrated in the following figure:
Figure 2: RESTful APIs tests

JUnit test contains values set for each field, @Before with setupData() method in which we build our RESTful API payload based on the model we’ve seen previously ,@Test - setting the properties from the Header, saving the response message and doing validation on https response code and response message; postReportHappyTest() and postReportUnHappyTest() are the two methods to capture the happy and unhappy paths to make a call to: url: ”http://qa-bi-data-reportsda.cds.dxpdata.com.domain /20221002/valid-user-attribute/api/v1/bad-data-report”

The logic for the POST Report Call is illustrated in the code below:

1 package com.dxp.data.apis.bad_data_report;
2 import com.dxp.data.apis.PostReportCreateModel;
3 import com.dxp.data.util.connector.RestAssuredConnector;
4 import com.dxp.data.util.general.ConfigUtils;
5 import io.restassured.response.Response;
6 import org.junit.Assert;
7 import org.junit.Before;
8 import org.junit.Test;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import java.util.Map;
12 import static org.junit.Assert.assertEquals; 13 public class PostBadDataReportHappyTest {
14 protected static final Logger logger =
15 LoggerFactory.getLogger(
16 POST_BadDataReportHappyTest.class);
17 public static final String DATE_NOW = "14-09-2023"; 18 public static final String NETWORK = "AMC";
19 public static final String APP = "android";
20 public static final String TYPE = "invalid event";

21

22 public static final String REASON = "unplpfxed";
23 public static final String EVENT = "xxxxxxx";
24 public static final Integer COUNT = 1500;
25 public static final String DATE = "09-09-2021";
26 public static final String NETWORKDUMMY = "DUMMY";
27 public static final String APPDUMMY = "DUMMY";
28 public static final String TYPEDUMMY = "DUMMY";
29 public static final String REASONDUMMY = "DUMMY";
30 public static final String EVENTDUMMY = "DUMMY";
31 public static final Integer COUNTDUMMY = 1;
32 public static final RestAssuredConnector connector =
33 new RestAssuredConnector();
34 private String requestBody;
35 private String requestUri;
36 private String proxy;

37

38 @Before
39 public void setupData() {
40 PostReportCreateModel requestMessageBody = new

41 PostReportCreateModel();
42 requestMessageBody.setDate(DATE_NOW);
43 requestMessageBody.setNetwork(NETWORK);
44 requestMessageBody.setApp(APP);
45 requestMessageBody.setType(TYPE);
46 requestMessageBody.setReason(REASON);
47 requestMessageBody.setEvent(EVENT);
48 requestMessageBody.setCount(COUNT);
49 requestBody = requestMessageBody.toString();
50 requestBody = requestMessageBody.toString();
51 requestUri =
52 ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
53 BASE_URL_QA.toString()) +
54 ConfigUtils.getProperty(ConfigUtils.
55 ConfigKeys.END_URL_REPORT.toString());

56

57 proxy =
58 ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
59 PROXY_QA.toString());

60 }

1 @Test
2 public void postReportHappyTest() {
3 Map<String, String> headers =
4 RestAssuredConnector.setHeaders();
5 Response reportResponse =
6 connector.postRequest(requestUri, proxy,
7 headers, requestBody);
8 logger.info("Making a POST Report Call: " + " " +
9 requestUri);
10 logger.info("requestBody: " + " " + requestBody); 11 logger.info("Response: " +
12 reportResponse.getBody().asString());
13 assertEquals(200, reportResponse.getStatusCode()); 14 Assert.assertTrue(reportResponse.getBody(). 15 asString(), true);

16 }
1

2 @Test
3 public void postReportUnHappyTest() {
4 requestMessageBody.setDate(DATE);
5 requestMessageBody.setNetwork(NETWORKDUMMY);

6 requestMessageBody.setApp(APPDUMMY);
7 requestMessageBody.setType(TYPEDUMMY);
8 requestMessageBody.setReason(REASONDUMMY);
9 requestMessageBody.setEvent(EVENTDUMMY); 10 requestMessageBody.setCount(COUNTDUMMY); 11 requestBody = requestMessageBody.toString(); 12 requestUri = ConfigUtils.getProperty(
13 ConfigUtils. ConfigKeys.BASE_URL_QA.toString())

14 +

15 ConfigUtils.getProperty(ConfigUtils.
16 ConfigKeys.END_URL_REPORT.toString());
17 proxy = ConfigUtils.getProperty(ConfigUtils.
18 ConfigKeys.PROXY_QA.toString());
19 Map<String, String> headers =
20 RestAssuredConnector.setHeaders();
21 Response reportResponse =
22 connector.postRequest(requestUri, proxy,
23 headers, requestBody);
24 logger.info("requestUri: " + " " + requestUri);
25 logger.info("requestBody: " + " " + requestBody);
26 logger.info("Response: " +
→ reportResponse.getBody().asString());

27

28 assertEquals(reportResponse.getStatusCode(), 400);
29 Assert.assertTrue(reportResponse.getBody().
30 asString(), true);

31 }
32 }
GET Call:
we need to add GET method in our Rest Assured Connector class:
1 public Response getRequest(String uri, String host, 2 Map<String, String> headers) {
3

4 request =
5 given().baseUri(uri).proxy(host).headers(headers);
6 return request.when().get();
7 }

Happy path:
GET Data Report Call:
”http://dev-bi-data-reports-da.cds.dexpdata.com.domain /20221129/20221129/20221001/20221002/valid-user-attribute”

2 public void getIncorrectDataReportHappyTest()
3 {
4 proxy=ConfigUtils.getProperty(ConfigUtils.
5 ConfigKeys.PROXY_DEV.toString());
6 requestUri=ConfigUtils.getProperty(ConfigUtils.
7 ConfigKeys.BASE_URL_DEV.toString())
8 +
9 ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 10 END_URL_REPORT.toString())

11 +
12 ConfigUtils.getProperty(ConfigUtils.
13 ConfigKeys.START_DATE_END_DATE.toString())
14 +
15 ConfigUtils.getProperty(ConfigUtils.
16 ConfigKeys.EVENT_TYPE.toString());
17

18 Map<String, String>
19 headers=RestAssuredConnector.setHeaders();
20 Response reportResponse=connector.
21 getRequest(requestUri,proxy,headers);

22
23 logger.info("Making a GET Report: "+"" +requestUri);
24

25 logger.info("requestUri: "+" "+requestUri);
26 logger.info("Response:"+reportResponse.
27 getBody().asString());

28

29 assertEquals(200,reportResponse.getStatusCode());
30 Assert.assertTrue(reportResponse.
31 getBody().asString(),true);

32 }

Unhappy paths:
GET Data Report Call:
”http://dev-bi-data-reportsda.cds.dexpdata.com.domain
/17001202/17001203/20221129/20221001/20221002
/invalid-user-attribute” and
http://dev-bi-data-reports-da.cds.dexpdata.com.domain
/20221129/20221129/20221001/20221002/dummy”
getIncorrectDataReportInvalidDateInvalidDateTypeTest () and getIncorrectDataReportInvalidEventTypeTest() methods designed below:

2 public void
3 getIncorrectDataReportInvalidDateInvalidDateTypeTest()
4 {
5 proxy = ConfigUtils.getProperty(ConfigUtils.
6 ConfigKeys.PROXY_DEV.toString());
7 requestUri = ConfigUtils.
8 getProperty(ConfigUtils.ConfigKeys.
9 BASE_URL_DEV.toString())

10 +
11 ConfigUtils.getProperty(ConfigUtils.
12 ConfigKeys.END_URL_REPORT.toString())
13 +
14 ConfigUtils.getProperty(ConfigUtils.
15 ConfigKeys.INVALID_START_DATE_END_DATE.toString())
16 +
17 ConfigUtils.getProperty(ConfigUtils.
18 ConfigKeys.INVALID_EVENT_TYPE.toString());
19

20 Map<String, String>
21 headers=RestAssuredConnector.setHeaders();
22 Response reportResponse=connector.
23 getRequest(requestUri,proxy,headers);

24
25 logger.info("requestUri: "+" "+ requestUri);
26 logger.info("Response: "+ reportResponse.getBody().asString());
27
28 assertEquals(404,reportResponse.getStatusCode());
29 }

1 @Test
2 public void getIncorrectDataReportInvalidEventTypeTest(){
3 proxy=ConfigUtils.getProperty(
4 ConfigUtils.ConfigKeys.PROXY_DEV.toString());
5 requestUri=ConfigUtils.getProperty(
6 ConfigUtils.ConfigKeys.BASE_URL_DEV.toString())
7 +
8 ConfigUtils.getProperty(ConfigUtils.
9 ConfigKeys.END_URL_REPORT.toString())

10 +
11 ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 12 START_DATE_END_DATE_VALID.toString())
13 +
14 ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
15 INVALID_EVENT_TYPE.toString());
16

17 Map<String, String>
18 headers=RestAssuredConnector.setHeaders();
19 Response reportResponse=connector.
20 getRequest(requestUri,proxy,headers);

21
22 logger.info("Making a GET Report Call: "+requestUri);
23

24 logger.info("proxy: "+" "+proxy);
25 logger.info("Response:
26 "+reportResponse.getBody().asString());
27 assertEquals(404,reportResponse.getStatusCode());

28 }
29

30 @Test
31 public void getIncorrectDataReportNonExistentDatesTest(){
32 proxy=ConfigUtils.getProperty(ConfigUtils.
33 ConfigKeys.PROXY_DEV.toString());
34 requestUri=ConfigUtils.getProperty(ConfigUtils.
35 ConfigKeys.BASE_URL_DEV.toString())

36 +
37 ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 38 END_URL_REPORT.toString())
39 +
40 ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 41 START_DATE_END_DATE_NON_EXISTENT.toString())
42 +

43 ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
44 EVENT_TYPE_VALID.toString());
45 Map<String, String>
46 headers=RestAssuredConnector.setHeaders();
47 Response reportResponse=connector.getRequest(
48 requestUri,proxy,headers);
49 logger.info("Making a GET Report Call: "+"
50 "+requestUri);
51 logger.info("Response:
52 "+reportResponse.getBody().asString());
53 assertEquals(404,reportResponse.getStatusCode());

54 }
DELETE Call: we need to add the DELETE method in our Rest-assured Connector class:

1 public Response deleteRequest(String uri, String
2 host, Map<String, String> headers) {
3 request =
4 given().baseUri(uri).proxy(host).headers(headers);

5 return request.when().delete(); 6 }

Happy path:
Delete a plan from mParticle with url:
”http://qa-mparticle-ims-da.cds.amcn.com.domain//api /v1/6420/plans/2”

Test implementation:

1 @Test
2 public void deletemParticleUnHappyTest(){
3 logger.info("DELETE mParticle API Call: ");
4 Map<String, String> headermParticle=new HashMap<>();
5 headermParticle.put("Content-Type","application/json");
6 Response mParticleResponse=connector.deleteRequest(
7 requestUrimParticle,proxy,headermParticle);
8 logger.info("requestUrimParticle: "+"
9 requestUrimParticle);
10 logger.info("Response: "
→ +mParticleResponse.getBody().asString());

11
12 assertEquals(200,mParticleResponse.getStatusCode());
13 }
PATCH Call: we need to add we need to add PATCH method in our RestAssuredConnector class:

1 host,Map<String, String> headers,String body)
2 {
3 request=given().baseUri(uri).proxy(host).
4 headers(headers).body(body);
5 return request.when().patch();
6 }

• Okta Token Authentication is more secure compared to basic authentication, since it involves the use of a unique token that is generated for each user. This token is sent with each request and is used to authenticate the user. Token authentication is also a good choice for applications that require frequent authentication, such as single-page applications or mobile applications. Since the authentication process does not require user passwords in each request, the user receives a unique encrypted valid token for a specified session time after entering their credentials. It is more eﬀicient and can handle more concurrent requests. Let us use a simple case for an API call using Okta authentication, illustrated in the next figure.

Figure 3: Okta Token Groovy structure
Having the following Groovy script:

1 import okhttp3.*
2 import org.json.JSONObject
3 import org.assertj.core.api.SoftAssertions

4

5 Random random = new Random()
6 int randomInt = random.nextInt(10000000) + 1
7 randomString = randomInt.toString()
8 String adminSys = "IDK"
9 String key = "00" + randomString + "|||" + adminSys 10 String code = "CODE"

11

12 //Define necessary variables for Okta
13 def url_okta = "https://oktatokengeneration/aus456788"
14 def usr = "username"
15 def pswd = "password"

17 RequestBody okta = RequestBody.create(null, " ")
18

19 OkHttpClient client = new OkHttpClient()
20 Request request_okta = new Request.Builder().
21 url(url_okta).header("Authorization",
22 Credentials.basic(usr, pswd)).
23 header("Content-Type", "application/json").
24 header("Content-Type",
25 "application/x-www-form-urlencoded").post(okta).build()

26

27 Response response_okta = client.newCall(request_okta).execute()
28 String responseBody_okta = response_okta.body().string()
29 int statusCode_okta = response_okta.code()

30

31 SoftAssertions softly = new SoftAssertions()
32 softly.assertThat(statusCode_okta).isEqualTo(200)
33 softly.assertAll()

34
35 log.info("Response for OktaI Call :" + responseBody_okta) 36 log.info("Status code forOkta Call:" + statusCode_okta)
37

38 //Save bearer token
39 JSONObject rspOkta = new JSONObject(responseBody_okta)
40 String access_token = rspOkta.getString("access_token")
41 String token_type = rspOkta.getString("token_type")
42 String expires_in = rspOkta.getString("expires_in")
43 String scope = rspOkta.getString("scope")

44

45 softly.assertThat(token_type).isEqualTo("Bearer")
46 softly.assertThat(expires_in).isEqualTo("3600")
47 softly.assertThat(scope).isEqualTo("tracker")
48 softly.assertAll()

We are using library okhttp3, and making a POST with null
to our Okta token request, having Content-Type:
application:x-www-form-urlencoded AND application:json Also access token value is what we are interested in for the future calls, as we will see in our next code section:

1 //API Call using Okta Token
2
3 //Define necessary variables for Fabric API POST Call 4 def url_api = "https://trcakerapi
5
6 JSONObject sys = new JSONObject() 7 sys.put("code", code)

9 JSONObject tracking = new JSONObject() 10 tracking.put("key", key)
11 tracking.put("sys", sys)

12

13 JSONObject tracker = new JSONObject()
14 tracker.put("tracking", tracking)
15 RequestBody trackingBody = RequestBody.create(MediaType.parse
16 ("application/json"),tracker.toString())

17

18 Request request_tracker = new Request.Builder().url(url_api).
19 header("Authorization", "Bearer" + access_token).
20 header("Content-Type",
→ "application/json").post(trackingBody).build()

21
22 Response response_tracker =
→ client.newCall(request_tracker).execute()
23
24 String responseBody_tracker = response_tracker.body().string() 25 int statusCode_tracker = response_tracker.code()
26
27 softly.assertThat(statusCode_tracker).isEqualTo(200) 28 softly.assertAll()
29

30 log.info("Response for POST Tracker API Call :" + → responseBody_tracker)
31 log.info("Status code for POST Tracker API Call:" + → statusCode_tracker)

1 Request request_tracker = new Request.Builder().url(url_api).
2 header("Authorization", "Bearer" + access_token).
3 header("Content-Type",
→ "application/json").post(trackingBody).build()

we are creating a call request using the api url passing on the header the authorization the access token with the payload set in the code snippet:
1 JSONObject sys = new JSONObject() 2 sys.put("code", code)
3

4 JSONObject tracking = new JSONObject()
5 tracking.put("key", key)
6 tracking.put("sys", sys)

7
8 JSONObject tracker = new JSONObject()
9 tracker.put("tracking", tracking

• Auth 2.0 is an open standard for authorization that provides a way for users to grant access to their data without sharing their passwords. OAuth is used to authenticate users and authorize access to a system or service.

OAuth 2.0 authentication in REST API is a great option for applications that need to access user data from other services, such as Google, Facebook, Twitter, or any other external service. It allows users to grant access to their data without having to share their username and password with the application.

In addition to Okta authentication, there is an extra level of security to have a user token generated for request access data. The next figure illustrates Groovy scripts using Auth 2.0.

Figure 4: Auth 2.0

1 import okhttp3.*
2 import org.json.JSONArray
3 import org.json.JSONObject
4 import org.assertj.core.api.SoftAssertions
5 import org.slf4j.*
6 import java.util.*

7

8 //Define necessary variables for User Token 9 def url_user = "https://usertoken
10 def username_user = "username"
11 def password_user = "password"
12 def client_id = "client_id"
13 def client_secret = "client_secret"

14
15 JSONObject bodyUsr = new JSONObject()
16

17 bodyUsr.put("client_id", client_id)
18 bodyUsr.put("client_secret", client_secret)
19 bodyUsr.put("grant_type", "")
20 bodyUsr.put("realm", "")
21 bodyUsr.put("audience", "")
22 bodyUsr.put("username", "")
23 bodyUsr.put("password", "")

24

25 RequestBody payloadUser =
→ RequestBody.create(MediaType.parse("application/json"), → bodyUser.toString())

26

27 Request request_User = new Request.Builder().url(url_user).
28 header("Authorization", Credentials.basic(username, password)).
29 header("Content-Type",
→ "application/json").post(payloadUser).build()

30

31 OkHttpClient client = new OkHttpClient()
32 Response response_user =
→ client.newCall(request_User).execute()
33 String responseBody_user = response_user.body().string()
34 int statusCode_user = response_user.code()

35
36 SoftAssertions softly = new SoftAssertions()
37
38 softly.assertThat(statusCode_user).isEqualTo(200) 39 softly.assertAll()
40
41 log.info("Response for User Call :" + responseBody_user) 42 log.info("Status code for User Call:" + statusCode_user)
43
44 //Save id_token
45 JSONObject rspUser = new JSONObject(responseBody_user) 46 String user_access_token = rspUser.getString("access_token")
Let us analyze our code. To generate a user token, we need to set as a payload:

1 bodyUsr.put("client_id", client_id)
2 bodyUsr.put("client_secret", client_secret)
3 bodyUsr.put("grant_type", "")
4 bodyUsr.put("realm", "")
5 bodyUsr.put("audience", "")
6 bodyUsr.put("username", "")
7 bodyUsr.put("password", "")

then we build our request using the url and passing on the header credentials with the payload set above:

1 Request request_User = new Request.Builder().url(url_user).
2 header("Authorization", Credentials.
3 basic(username, password)).
4 header("Content-Type", "application/json").
5 post(payloadUser).build()

Making the call will result in having obtained the user access token that we are going to use in our future Api calls using oauth 2.0 as follows:

1 import org.assertj.core.api.SoftAssertions
2 import org.slf4j.*
3 import java.util.*
4 import java.text.DateFormat
5 import java.text.SimpleDateFormat
6 import okhttp3.*
7 import org.json.JSONArray
8 import org.json.JSONObject

9

10 Date now = new Date()
11 SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd → HH:mm:ss")
12 String currentDateComplete = date.format(now)
13 String dateEDT = currentDateComplete + ".100 EDT"

14
15 //Define necessary variables for Okta 16 def url_okta = "https://oktatoke"

17 def usr = "username"
18 def pswd = "password"
19 RequestBody okta = RequestBody.create(null, " ")

20

21 OkHttpClient client = new OkHttpClient()
22 Request request_okta = new Request.Builder().
23 url(url_okta).header("Authorization",
24 Credentials.basic(usr, pswd)).
25 header("Content-Type",
→ "application/json").header("Content-Type",
26 "application/x-www-form-urlencoded").post(okta).build()

27

28 Response response_okta = client.newCall(request_okta).execute()
29 def responseBody_okta = response_okta.body().string()
30 int statusCode_okta = response_okta.code()

31
32 SoftAssertions softly = new SoftAssertions()
33
34 softly.assertThat(statusCode_okta).isEqualTo(200) 35 softly.assertAll()
36
37 log.info("Response for OktaI Call :" + responseBody_okta) 38 log.info("Status code for Okta Call:" + statusCode_okta)
39

40 //Save bearer token
41 JSONObject rspOkta = new JSONObject(responseBody_okta)
42 String access_token = rspOkta.getString("access_token")

43

44 //Define necessary variables for User Token
45 def url_user = "https://usertoken"
46 def username_user = "user"
47 def password_user = "pswd"
48 def client_id = "client_id"
49 def client_secret = "client_secret"

50
51 JSONObject bodyUser = new JSONObject()
52

53 bodyUser.put("client_id", client_id)
54 bodyUser.put("client_secret", client_secret)
55 bodyUser.put("grant_type", "")
56 bodyUser.put("realm", "")
57 bodyUser.put("audience", "")
58 bodyUser.put("username", "m")
59 bodyUser.put("password", "")
60 RequestBody payloadUser = RequestBody.create(MediaType.
61 parse("application/json"), bodyUser.toString())

62
63 Request request_User = new Request.Builder().url(url_user). 64 header("Authorization", Credentials.Credentials.

65 basic(username_user, password_user)).
66 header("Content-Type", "application/json").
67 post(bodyUser).build()

68

69 Response response_user =
→ client.newCall(request_User).execute()
70 String responseBody_user = response_user.body().string()
71 int statusCode_user = response_user.code()

72

73 //Save id_token
74 JSONObject rspUser = new JSONObject(responseBody_user)
75 String user_access_token = rspUser.getString("access_token")

76

77 //Define necessary variables for the Agreement Customer
78 String agmtKey = "1111"
79 String adminSystem = "AAAA"

80

81 def url_agr_cst_micro = "https://agrcstmicro" + "/" + agmtKey → + "|||" + adminSystem
82 def username_agr = "agrcst"
83 def password_agr = pswd2025

84

85 Request request_agrKey = new
→ Request.Builder().url(url_agr_cst_micro).
86 header("Authorization", Credentials.
87 basic(username_agr, password_agr)).
88 header("Content-Type", "application/json").build()

89

90 Response response_agrKey =
→ client.newCall(request_agrKey).execute()
91 String responseBody_agrKey = response_agrKey.body().string()
92 int statusCode_agrKey = response_agrKey.code()

93

94 log.info("Response for agreementKey:" +
→ responseBody_agrKey.toString())
95 log.info("Status code for agreementKey:" + statusCode_agrKey)

96
97 JSONObject jsonResponseAgreementCustomer = new → JSONObject(response_agrKey)
98

99 JSONArray agreements = jsonResponseAgreementCustomer. 100 getJSONArray("agreements")
101 JSONObject customer = agreements.getJSONObject(0). 102 getJSONArray("agreementCustomers").getJSONObject(1)

103

104 String roleType = customer.getString("roleType")
105 String memberGUIDAgreementCustomer =
→ customer.getString("memberGUID")

106

107 //Define necessary variables for Customer Id
108 def url_customer_id_micro = "https://cstid +
109 "/" + memberGUIDAgreementCustomer
110 def username_customerIds = "username"
111 def password_customerIds = "password"

112

113 Request request_customerId = new Request.Builder().
114 url(url_customer_id_micro).
115 header("Authorization", Credentials.
116 basic(username_customerIds, password_customerIds)).
117 header("Content-Type", "application/json").build()

118

119 Response response_customerId =
→ client.newCall(request_customerId).
120 execute()
121 String responseBody_customerId=
→ response_customerId.body().string()
122 int statusCode_customerId = response_customerId.code()

123

124 log.info("Response for CustomerId:" +
→ responseBody_customerId.toString())
125 log.info("Status code for CustomerId:" + statusCode_customerId)

126
127 JSONObject jsonResponseCustomerId = new → JSONObject(responseBody_customerId)
128

129 JSONArray customers = jsonResponseCustomerId.
130 getJSONArray("customers")
131 String memberGUIDCustomerId = customers.getJSONObject(0).
132 getString("memberGUID")

133

134 softly.assertThat(memberGUIDAgreementCustomer).
135 isEqualTo(memberGUIDCustomerId)
136 softly.assertAll()

137

138 //POST Cst
139 def url_api = "https://cstapi" + "/" +
→ memberGUIDAgreementCustomer + "/agreements/" + agmtKey + → "|||" + adminSystem

140
141 log.info("Calling Fabric API:" + url_api)
142
143 // template
144 String cts = """
145 {

146 "transaction":{
147 "code":"CODE",
148 "date":"${dateEDT}"

149 },

150 "product":{
151 "item":{
152 "plan":"PLAN",
153 "category":"Allo"

154 }
155 }
156 }
157 """
158

159 log.info(cts)
160 RequestBody cstPayload=RequestBody.create(MediaType.
161 parse("application/json"), cts)

162

163 Request request_cst = new Request.Builder().url(url_api).
164 header("Authorization", "Bearer " + access_token).
165 header("Content-Type", "application/json").header("Accept", → "*/*").
166 header("Accept-Encoding","gzip, deflate, br").
167 header("Connection","keep-alive").
168 header("userToken", user_access_token)
169 .post(cstPayload).build()

170

171 Response response_cst= client.newCall(request_cst).
172 execute()
173 String responseBody_cst = response_cst.body().string()
174 int statusCode_cst= response_cst.code()

175
176 log.info("Response for POST Cst Call :" + responseBody_cst) 177 log.info("Status code for POST Cst Call:" + statusCode_cst)
Our final call request is having as payload: 1 { 2 "transaction":{ 3 "code":"CODE", 4 "date":"${dateEDT}" 5 }, 6 "product":{ 7 "item":{ 8 "plan":"PLAN", 9 "category":"Allo"
10 }
11 } 12 }
an on the header level the 2 layers of authorization: Okta token and user token:

1 Request request_cst = new Request.Builder().url(url_api).
2 header("Authorization", "Bearer " + access_token).
3 header("Content-Type", "application/json").
4 header("Accept", "*/*").header("Accept-Encoding","gzip, → deflate, br").
5 header("Connection","keep-alive").
6 header("userToken",
→ user_access_token).post(cstPayload).build()

Part 2: Practical Automation Techniques

• Chapter 3: Setting Up Your Automation Environment
• Installing necessary tools and libraries
• Configuring your testing environment

◦ Installing necessary tools and libraries. Technologies used: Java, TestNG, Selenium, Apache Maven. Libraries used in the solution are set in the pom.xml file as dependencies. Tests defined as TestNG classes require TestNG dependency in pom.xml:

Figure 5: RESTful APIs dependencies

1 <dependency>
2 <groupId>org.testng</groupId>
3 <artifactId>testng</artifactId>
4 <version>${testng.version}</version>
5 </dependency>

6
7 v.7.8.0 is set in <properties>:
→ <testng.version>7.8.0</testng.version>
8

9 Using Selenium implies setting the dependency in pom.xml: 10 <dependency>
11 <groupId>org.seleniumhq.selenium</groupId>
12 <artifactId>selenium-java</artifactId>
13 <version>${selenium.version}</version>
14 </dependency>

where selenium.version is set in <properties>:
1 <selenium.version>3.4.0</selenium.version>
In order to do logging with information about our implementation and the progress of the test we need to have set dependency in pom.xml:

1 <dependency>
2 <groupId>org.slf4j</groupId>
3 <artifactId>jcl-over-slf4j</artifactId>
4 <version>${slf4j.version}</version>
5 </dependency>

v.1.7.25 is set in <properties>: <slf4j.version>1.7.25</slf4j.version> 2. Practical project RESTful APIs: Identity Access Management that we covered in Chapter 2 and Chapter 3: Restful APIs built on the Okta platform to grant, remove access based on role, profile level in organizations.
Technologies stack for web app: Java, TestNG, Selenium, Apache Maven. Tests defined as JUnit classes require TestNG dependency in pom.xml: v.4.13.2 is set in <properties>:
<junit.version>4.13.2</junit.version>

1 <dependency>
2 <groupId>junit</groupId>
3 <artifactId>junit</artifactId>
4 <version>${junit.version}</version>
5 <scope>test</scope>
6 </dependency>

For RESTful APIs implementation, RestAssuredConnector class uses rest assured, so dependency needs to be added in the pom.xml.

1 <dependency>
2 <groupId>io.rest-assured</groupId>
3 <artifactId>rest-assured</artifactId>
4 <version>${rest-assured.version}</version>
5 </dependency>

v.5.5.0 is set in <properties>:
<rest-assured.version>4.13.2</rest-assured.version>
Payload formats used for RESTful APIs are JSON, dependency is set in <properties>

1 <dependency>
2 <groupId>org.json</groupId>
3 <artifactId>json</artifactId>
4 <version>${json.version}</version>
5 </dependency>

v.20230618 is set in <properties>: <json.version>20230618</json.version> Assertions can be done because of dependency:

1 <dependency>
2 <groupId>org.assertj</groupId>
3 <artifactId>assertj-core</artifactId>
4 <version>${assertj-core.version}</version>
5 </dependency>

mvn clean install command downloads and sets all the jars from the dependencies set in pom.xml, can be accessible on the Maven project level, as can be seen in Figure 5.6:

Figure 6: mvn dependencies

Path: C:\Users\user\.m2\repository contains all the downloaded jars. Configuring your testing environment. In our previous real-world projects to store all the properties: urls, tokens, or any other persistent data in src/test/resource/localconfig.properties.
Tests read the properties from the config file.
We will configure three config files: dev.properties, qa.properties, prod.properties, one for each of the environment: dev, qa, prod we want to run our tests, as we can see in Figure 5.7:

Figure 7: config. files
In pom.xml we will create profiles for each of the environments.

1 <profiles>
2 <profile>
3 <id>dev</id>
4 <properties>
5 <config.file>${basedir}/src/main/resources
6 /dev.properties</config.file>
7 </properties>
8 </profile>
9 <profile>
10 <id>qa</id>
11 <properties>
12 <config.file>${basedir}/src/main/resources 13 /qa.properties</config.file>
14 </properties>
15 </profile>
16 <profile>
17 <id>prod</id>
18 <properties>
19 <config.file>${basedir}/src/main/resources 20 /prod.properties</config.file>
21 </properties>
22 </profile>

• Chapter 4: Creating Effective Test Scripts

◦ Writing maintainable and reusable test scripts
◦ Best practices for test script design
A good testing automation framework can be measured by different metrics:

test coverage, test execution time, test maintenance effort, bugs detection rate, false positive rate, return of investment, scalability, integration with CI/CD, reporting and analytics. It is really important to pay attention to the architecture of the automation solution that we want to build in scope of any types of tests we have: functional, system, integration, end-to-end and regression.

To achieve this, we need to write code that is easy to understand and maintain with as little effort as possible. We can obtain this by using the following:
Design patterns: Factory, Builder, Page Object Model, Singleton, Template, Data-Driven Testing.

Template : in Case 2: dxp-data project we built data modeling for APIs’ payloads with methods allowed and urls set in a configuration file we build the request and response message.

Figure 8: dxp-data architecture test The logic and implementation can be checked again in Chapter 3: API Testing, Case 2.
2. Structure of the tests and test suites: clean, clear names.
JUnit tests, we have runners to organize and prioritize our tests. Going back to Chapter 3 to our real-world project: dxp-data covering RESTful APIs automated tests, the test suite is defined as a runner containing the list of tests based on existing tests:

Figure 9: tests structure
Setting our tests from Figure 5.9, runner is illustrated below:
Figure 10: dxp data runners errors Errors are showing, no imports being added for all JUnit classes in use, after all the imports were added, our runner is runnable.
Figure 11: dxp data runner successful

3. Custom written tests can be specific by addressing issues and end cases in the application, reducing the frequency of failures.
By integrating different components and simulating real-world scenarios, our tests will provide a more accurate assessment of the system’s performance. Custom tests can be quickly adapted to reflect changes in our application, ensuring continuous and up-to-date coverage.

Test autonomy by generating data we are enabling more reliable validations while reducing reliance on potentially outdated, insuﬀicient, or inexistent test data.
Diving into the automated solution for RESTful APIs, we can choose test: PD 866 POST mParticle Happy Test, payload is set with dynamic values: data plan id, data plan name, description, event name, on each call is made.

1 public class PD_866_POST_mParticle_HappyTest {
2
3 protected static final Logger logger = LoggerFactory.getLogger( 4 PD_866_POST_mParticle_HappyTest.class);
5
6 private RestAssuredConnector connector = new
→ RestAssuredConnector();
7
8 public static final String DATA_PLAN_ID = "auto" + "_" 9 + "data_plan" + GenerateRandomDataUtils.generateRandomNumber(4);
10
11 public static final String DATA_PLAN_NAME = "Mobile Data Plan"
12 +
13 GenerateRandomDataUtils.generateRandomString(4);
14
15 public static final String DATA_PLAN_DESCRIPTION = "Auto"
16 +
17 GenerateRandomDataUtils.generateRandomNumber(2)
18 +
19 "This is an example data plan description.";
20
21 public static final Integer VERSION = 1;
22 public static final String ACTIVATED_ENVIRONMENT = "none";
23
24 public static final String DESCRIPTION = "Auto"
25 +
26 GenerateRandomDataUtils.generateRandomString(4) + "data point";
27
28 public static final String MATCH_TYPE = "custom_event";
29
30 public static final String EVENT_NAME = "Auto"
31 +
32 GenerateRandomDataUtils.generateRandomString(4)
33 +

34 "My Custom Event Name";
35 public static final String CUSTOM_EVENT_TYPE = "other";
36 public static final String VALIDATOR_TYPE = "json_schema";
37 public static final boolean ADDITIONAL_PROPPERTIES = true;
38 public static final String FOO_TYPE = "number";
39 private String requestBodymParticle;
40 private String proxy;
41 private String requestUrimParticle;

42
43 @Before
44 public void setupData() {
45
46 PostmParticleCreateModel requestMessageBodymParticle = new → PostmParticleCreateModel();
47

48 requestMessageBodymParticle.setData_plan_id(
49 DATA_PLAN_ID);
50 requestMessageBodymParticle.setData_plan_name(
51 DATA_PLAN_NAME);
52 requestMessageBodymParticle.
53 setData_plan_description(DATA_PLAN_DESCRIPTION);
54 requestMessageBodymParticle.setVersion(VERSION);
55 requestMessageBodymParticle.
56 setActivated_environment(ACTIVATED_ENVIRONMENT);
57 requestMessageBodymParticle.
58 setDescription(DESCRIPTION);
59 requestMessageBodymParticle.

60 setMatchType(MATCH_TYPE);
61 requestMessageBodymParticle.
62 setEvent_name(EVENT_NAME);
63 requestMessageBodymParticle.
64 setCustom_event_type(CUSTOM_EVENT_TYPE);
65 requestMessageBodymParticle.
66 setValidator_type(VALIDATOR_TYPE);
67 requestMessageBodymParticle.
68 setAdditionalProperties(ADDITIONAL_PROPPERTIES);
69 requestMessageBodymParticle.
70 setFoo_type(FOO_TYPE);

71
72 requestBodymParticle = requestMessageBodymParticle. 73 toString();
74
75 requestUrimParticle = ConfigUtils.getProperty(ConfigUtils. 76 ConfigKeys.BASE_URL_mParticle.toString())
77 +
78 ConfigUtils.getProperty(ConfigUtils.
79 ConfigKeys. END_URL_mParticle_AMCN_TEST.toString());
80
81 proxy = ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 82 PROXY_QA.toString());
83 }
84
85 @Test
86 public void postmParticleHappyTest() {
87
88 logger.info("mParticle API call create new data plan: ");
89
90 Map<String, String> headermParticle = new HashMap<>(); 91 headermParticle.put("Content-Type", "application/json");
92

93 Response mParticleResponse =
→ connector.postRequest(requestUrimParticle, proxy, → headermParticle, requestBodymParticle);

94 logger.info("requestUrimParticle: " + " " + → requestUrimParticle);
95 logger.info("requestBodymParticle: " + " " + → requestBodymParticle);

96

97 assertEquals(200, mParticleResponse.getStatusCode());
98 logger.info("Response: " +
→ mParticleResponse.getBody().asString());
99 Assert.assertTrue(mParticleResponse.getBody(). 100 asString(), true);

101 }
102 }

4. Logging information in our tests. To gain more detailed information about test progress, as well as debugging and troubleshooting scopes
By logging important key events, function calls, method progress, or test execution, you can track and monitor when and why something went wrong.

Logs can capture relevant information that helps recreate issues, making debugging more eﬀicient. They also provide insights into system behavior, offering valuable information for better decisions about design, features, and enhancements.

Popular logging frameworks Log4j, Logback and SLF4J. Log meaningful information: threads, timestamps, exceptions. Logging is essential, excessive logging can impact performance.
In all code snippets we have seen examples of logging, it requires sl4j maven dependency, set in pom.xml file:

1 <dependency>
2 <groupId>org.slf4j</groupId>
3 <artifactId>jcl-over-slf4j</artifactId>
4 <version>${slf4j.version}</version>
5 </dependency>

necessarily imports in our tests:
1 import org.slf4j.Logger;
2 import org.slf4j.LoggerFactory;
3
4 protected static final Logger logger = LoggerFactory.getLogger( 5 POST_SigmaPastDateReport_UnHappyTest.class);
6

7 //test logic in here
8 logger.info("requestUrimParticle: " + " " + requestUrimParticle);
9 logger.info("requestBodymParticle: " + " " + requestBodymParticle); 10 logger.info("Response: " + mParticleResponse.getBody().asString()); 11 logger.info("response code: " + reportResponse.getStatusCode());

code snippet logs information about: url, payload, response message and response code for the API in use.

• Chapter 6: Low-Code Platforms in Practice

1. Overview of low-code platforms (Katalon, ReadyAPI)
2. Key limitations (e.g., lack of OOP, redundancy issues)
Overview of low-code platforms (Katalon, ReadyAPI, etc.)

1. Katalon Tool. In the last couple of years many companies made a shift from building testing automation solutions using popular programming languages: Java, C and Python to ”low-code” platforms, Katalon being one of them.

Its popularity might be related to the fact that it is addressed for people not that skilled in coding and is built to support web, API, mobile and desktop applications. Sounds like a beautiful dream, to have the amount of work almost done, but we should know better that if we have this type of solution there is always a catch or multiple.

Even though supports testing for all types of applications and has integration with CI/CD like:
Jenkins, GitLab, Azure DevOps and comes with a variety of features like: Katalon Studio, Katalon TestOps, Katalon Recorder, Katalon Runtime Engine and Katalon TestCloud, Katalon comes with a price: is a licensed software and has a lot of limitations.

In just half a year of using it I did notice a lot of disadvantages. Katalon’s interface seems user friendly at first, to me was not that intuitive with hidden fields and functionalities.

In terms of documentation and community support, even though it is a growing community it is not that easy to find information when you’re running into problems.
Built-in solutions definitely are more diﬀicult to manage, understand, scale, debug and meet different scope for automated tests. Katalon is a commercial product, so a license needs to be purchased to use it, for companies with limited budgets this can become a great inconvenience.
There are some performance issues, glitches, crashes, non responsive, increased execution time for large-scale test automation projects or complex test scenarios. All of these make us dependent on the tool itself.

2. ReadyAPI Tool.
Aside from Katalon, ReadyAPI is another ”low-code” platform emerging as an alternative for no or less coding. ReadyAPI is limited only to RESTful APIs and Kafka streams capabilities. Supports functional testing, performance testing, security testing, mock services, API virtualization, integration with CI/CD through TestEngine.

In one year of using ReadyAPI, I did notice a lot of disadvantages. The same as Katalon to me is not that user friendly, hidden fields and functionalities could require a pretty decent amount of time spent to learn how tests can be set and configured.

The same as Katalon, ReadyAPI is a commercial product and requires purchasing a license to use it.

While writing Groovy test scripts I experienced many crashes, performance issues, increased time execution and almost impossible to do a proper debug. All of these make us dependent on the tool itself.

Other issues and limitations noticed:
• redundant, duplicate code;
• bulk code;
• doesn’t support rest assured library;
• no possibility to have classes/methods or OOP;
• no possibility to order tests;
• no possibility to extend to multiple environments;
• no possibility to write multiple tests;

• no possibility to create easily integration tests to validate, track, process and generate data in different systems;

• no stack trace for failed tests;
• diﬀicult git workflow (code review especially);
• not able to solve conflicts easily (really diﬀicult to go through xml files.

Figure 12: Ready API Groovy Scripts

Kafka Consumer Groovy test script:
1 package kafka.consumers
2
3 import org.apache.kafka.clients.consumer.*
4
5 import java.text.SimpleDateFormat
6
7 String pattern = "mm-dd-yyyy"
8 SimpleDateFormat dateFormat = new SimpleDateFormat(pattern)
9
10 String currentDate = dateFormat.format(new Date())
12 String uniqueGroupID = "automation_doc_events_consumer" + → currentDate
13
14 String bootstrapServers = ""
15
16 String topicName = ""
17
18 String schemaRegistryUrl = "" 19 String trustStoreLocation = ""
20
21 log.info ("Adding properties to Kafka Consumer")
22

23 Properties props = new Properties()
24 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
→ bootstrapServers);
25 props.put(ConsumerConfig.GROUP_ID_CONFIG, uniqueGroupID)
26 props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "");
27 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "");
28 props.put("security.protocol", "")
29 props.put("sasl.mechanism", "")
30 props.put("basic.auth.credentials.source", "")
31 props.put("basic.auth.user.info", "")
32 props.put("sasl.jaas.config", "")
33 props.put("ssl.truststore.location", trustStoreLocation)
34 props.put("ssl.truststore.password", "changeit")
35 props.put("schema.registry.url", schemaRegistryUrl)
36 props.put("schema.registry.ssl.truststore.location", → trustStoreLocation)
37 props.put("schema.registry.ssl.truststore.password", "changeit")
38 props.put("auto.offset.reset", "latest")

39
40 log.info("Initializing and subscribe to the topic")
41

42 KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)
43 consumer.subscribe(Arrays.asList(topicName))
44 consumer.close()

45
46 log.info("Kafka consumer SetUp... with success")
RESTful API Groovy test script example:
1 package restful_apis.post_get
2

3 import okhttp3.*
4 import okhttp3.MediaType
5 import org.json.JSONArray

6 import org.json.JSONObject
7 import java.util.Random
8 import java.text.DateFormat
9 import java.text.SimpleDateFormat 10 import org.assertj.core.api.SoftAssertions

11

12 String base_receive = ""
13 String middle_receive = ""
14 String end_url_receive = ""

15

16 Random random = new Random()
17 int randomInt = random.nextInt(10000000)
18 String key = randomInt.toString()

19

20 Date now = new Date()
21 SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
22 String currentDateComplete = date.format(now)

23
24 String url_receive = base_url_receive + middle_url_receive 25 + end_url_receive
26
27 String username = "" 28 String password = ""
29
30 log.info("Creating POST Receive Call ..." + url_receive)
31

32 JSONObject requestBodyJson = new JSONObject()
33 requestBodyJson.put("key", key)
34 requestBodyJson.put("id", "")
35 requestBodyJson.put("amount", 500000)
36 requestBodyJson.put("quoteDate", currentDateComplete)

37

38 RequestBody receiveBody =
→ RequestBody.create(MediaType.parse("application/json"), → requestBodyJson.toString())

39

40 OkHttpClient client = new OkHttpClient()
41 Request request_receive = new
→ Request.Builder().url(url_receive.header("Authorization", → Credentials.basic(username, password)).header("Content-Type", → "application/json").post(receiveBody).build()
42 log.info("Receive Call:" + request_receive)

43

44 Response response_receive =
→ client.newCall(request_receive).execute()
45 String responseBody_receive= response_receive.body().string()

46
47 log.info("response:" + responseBody_receive)
48 int statusCode_receive = response_receive.code()
50 SoftAssertions softly = new SoftAssertions()
51
52 softly.assertThat(statusCode_receive).isEqualTo(200) 53 softly.assertAll()
54
55 log.info("Response for Receive Call :" + responseBody_receive) 56 log.info("Status code for Receive Call:" + statusCode_receive)
57

58 //Save key
59 JSONObject jsonObject = new JSONObject(responseBody_receive)
60 JSONArray msgArray = jsonObject.getJSONArray("messages")
61 JSONObject msgObject = msgArray.getJSONObject(0)
62 String msgDesc = msgObject.getString("msgDesc")
63 String storedKeyString = msgDesc.substring(msgDesc.lastIndexOf(" ") → + 1)
64 int storedKey = Integer.parseInt(storedKeyString.trim())

65

66 //GET Receive
67 String url_receive_get = url_receive + key
68 log.info("Creating GET Receive with ..." + url_receive_get)

69

70 Request request_receive_get = new Request.Builder().
71 url(url_receive_get).header("Authorization", Credentials.
72 basic(username, password)).
73 header("Content-Type", "application/json").build()

74
75 log.info("Calling the GET Receive ...")
76

77 Response response_receive_get =
→ client.newCall(request_receive_get).execute()
78 String responseBody_receives_get=
→ response_receive_get.body().string()
79 int statusCode_receive_get = response_receives.code()

80
81 softly.assertThat(statusCode_receive).isEqualTo(200) 82 softly.assertAll()
83
84 log.info("Response for GET Receive :" + responseBody_receive_get) 85 log.info("Status code for GET Receive :" + statusCode_receive_get)
86

87 //DELETE Receive
88 String url_receive_delete = url_receive + storedKey
89 log.info("Creating POST Receive Call ..." + url_receive)

90

91 Request request_receive_delete = new
→ Request.Builder().url(url_receives_delete).
92 header("Authorization",Credentials.basic(username, password))
93 .header("Content-Type", "application/json").delete().build()

94 log.info("Receive Call:" + request_receive_delete)
95

96 Response responsey_receive_delete =
→ client.newCall(request_receive_delete).execute()
97 String responseBody_receive_delete=
→ response_receive_delete.body().string()
98 int statusCode_receive_delete = response_receive_delete.code()

99
100 softly.assertThat(statusCode_receive).isEqualTo(200) 101 softly.assertAll()
102

103 log.info("Response for DELETE Receive Call :" + → responseBody_receive)
104 log.info("Status code for DELETE Receive Call:" + → statusCode_receive)

Let’s analyze what is wrong with the code:
• a lot of duplicate, redundant code due to limitation of methods definition, encapsulation and inheritance;

• diﬀicult to read, understand and follow the steps and the data flow in the test;
• extensive logging (for a better code reading);

• adding more complexity of the test implies an extra level of confusion and diﬀiculty in understanding the test (let’s think about the following use case: publish a message produced by Kafka Producer, consume the message with Kafka Consumer, make a RESTful call and make DB data validations);

• old library is used: okhttp3.* (doesn’t support rest assured library);
• every time we need to make request calls for the RESTful APIS, the same code is rewritten;
• JSON Objects for payload and response messages need to be rewritten every time it is used without the data modeling for the payload.

Let’s see how it looks the test implementation for the steps described:
1. Publish message on a topic produced with Kafka Producer;
2. Consume the message with Kafka Consumer;

3. Make necessarily RESTful APIs call and response message data validations and store data;
4. Make data validation in DB.
1 package kafka.producer_micros_consumer_db
2
3 import java.text.SimpleDateFormat
4

5 import org.apache.kafka.clients.producer.KafkaProducer
6 import org.apache.kafka.clients.producer.ProducerConfig
7 import org.apache.kafka.clients.producer.ProducerRecord

8

9 import org.apache.avro.Schema
10 import org.apache.avro.generic.GenericData
11 import org.apache.avro.generic.GenericRecord 12 import org.apache.kafka.clients.producer.RecordMetadata

13

14 import org.apache.kafka.clients.consumer.*
15 import org.apache.kafka.clients.consumer.KafkaConsumer
16 import org.apache.kafka.clients.consumer.ConsumerRecords
17 import org.json.JSONArray
18 import org.json.JSONObject

19

20 import java.sql.Connection
21 import java.sql.PreparedStatement
22 import java.sql.DriverManager
23 import java.sql.ResultSet
24 import org.assertj.core.api.SoftAssertions

25

26 //Producer variables
27 String bootstrapServers = ""
28 String topicProducer = ""
29 String trustStoreLocation = ""
30 String schemaRegistryUrl = ""

31

32 Date now = new Date()
33 SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
34 String currentDateComplete = date.format(now)

35
36 UUID generateUUID = UUID.randomUUID()
37

38 SimpleDateFormat inputFormat = new SimpleDateFormat
39 ("E MMM dd HH:mm:ss z yyyy")
40 inputFormat.setTimeZone(TimeZone.getTimeZone("EDT"))
41 String dateCreated = inputFormat.format(now)

42

43 String eventType= ""
44 String eventSubtype = ""
45 String eventId = generateUUID.toString()
46 String evntSrcDesc = "Dummy desc"
47 //define all necessary variables

49 String GUID = '{' + generateUUID.toString() + '}'
50

51 //Kafka Consumer variables to setup the props: groupId, brokers, → topic, schema
52 String uniqueGroupID = "automation_events_consumer" + → currentDateComplete
53 String topicConsumer = ""

54

55 //RESTful APIs calls
56 //Define necessary variables for RESTful ASPIs in use
57 String base_url_agr_c = ""
58 String middle_url_agr_c = ""
59 String end_url_agr_c_agrKey = ""
60 String url_agrKey = base_url_agr_c + middle_url_agr_c + → end_url_agr_c_agrKey
61 String base_url_c = ""
62 String middle_url_c = ""
63 String expectedRole = ""
64 String username_agr = ""
65 String password_agr = ""
66 String username_c = ""
67 String password_c = ""

68

69 //MySQL variables
70 String connectionUrl = ""
71 String username = ""
72 String password = ""
73 String queryMessage = ''
74 String appId = ""
75 String msgType = ""

76
77 log.info("Adding properties to Kafka Producer")
78
79 Properties propsProducer = new Properties()
80

81 propsProducer.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, → bootstrapServers);
82 propsProducer.put(ProducerConfig
83 .KEY_SERIALIZER_CLASS_CONFIG,
84 org.apache.kafka.common.serialization.
85 StringSerializer.class);
86 propsProducer.put(ProducerConfig.
87 VALUE_SERIALIZER_CLASS_CONFIG,
88 io.confluent.kafka.serializers.KafkaAvroSerializer.class);
89 propsProducer.put("security.protocol", "")
90 propsProducer.put("sasl.mechanism", "")
91 propsProducer.put("basic.auth.credentials.source", "")
92 propsProducer.put("basic.auth.user.info", "")
93 //add Kafka Producer props

95 log.info("Producing valid System message on topic: " + → topicProducer)
96

97 String avroSchema = '{}' //add Avro schema to validate against
98 Schema.Parser parser = new Schema.Parser()
99 Schema avroSchemaSystem = parser.parse(avroSchema)

100
101 GenericRecord avroRecord = new GenericData.Record(avroSchemaSystem) 102 // log.info(avroRecord.toString())
103
104 GenericRecord eventHeader = new GenericData.Record(avroSchemaSystem. 105 getField("eventheader").schema())
106

107 eventHeader.put("eventType", eventType)
108 eventHeader.put("eventSubtype", eventSubtype)
109 eventHeader.put("eventDateTime", currentDateComplete)
110 eventHeader.put("eventGeneratedDateTime", currentDateComplete)
111 //set all eventHeader fields and values

112
113 GenericRecord eventBody = new GenericData.Record(avroSchemaSystem. 114 getField("eventBody").schema())
115

116 eventBody.put("pfx", pfx)
117 eventBody.put("objS", objS)
118 eventBody.put("MimeType", mimeType)
119 eventBody.put("DateCreated", dateCreated)
120 //add eventBody values

121
122 //propertiesList implementation
123 List<GenericRecord> propertiesList = new ArrayList<>()
124

125 Schema propertiesListSchema =
→ avroSchemaSystem.getField("eventBody").schema().
126 getField("propertiesList").schema().getElementType()

127
128 GenericRecord propertyRecord = new
→ GenericData.Record(propertiesListSchema)
129

130 propertyRecord.put("name", "PropertyName")
131 propertyRecord.put("value", "PropertyValue")
132 propertyRecord.put("type", "PropertyType")
133 //add propertyRecord values
134 propertiesList.add(propertyRecord)
135 eventBody.put("propertiesList", propertiesList)

136
137 avroRecord.put("eventheader", eventHeader) 138 avroRecord.put("eventBody", eventBody)
140 log.info("System message produced by Kafka Producer: " + → eventHeader.toString() + eventBody.toString())
141
142 ProducerRecord<Object, Object> recordProducer = new → ProducerRecord<>(topicProducer, null, avroRecord)
143
144 KafkaProducer producer = new KafkaProducer(propsProducer)
145
146 RecordMetadata metadata = producer.send(recordProducer).get()
147
148 log.info("Producer record with eventId: " + eventId.toString())
149
150 producer.flush() 151 producer.close()
152

153 log.info("Message sent successfully to topic" + metadata.topic() + " → " +
154 "partition: " + metadata.partition() + " "+ "offset: " + → metadata.offset())

155

156 log.info("Kafka Producer Prim Id is:" + eventBody.get("prim_id"))
157 log.info("Sys is:" + eventBody.get("sys"))
158 String agrKeyProducer = (eventBody.get("prim_id") + → eventBody.get("sys")).toString()

159
160 // Kafka Consumer Setup
161 log.info ("Adding properties to Kafka Consumer")
162

163 Properties propsConsumer = new Properties()
164 propsConsumer.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, → bootstrapServers);
165 propsConsumer.put(ConsumerConfig.GROUP_ID_CONFIG, uniqueGroupID)
166 propsConsumer.put(ConsumerConfig.
167 KEY_DESERIALIZER_CLASS_CONFIG, "");
168 propsConsumer.put(ConsumerConfig.
169 VALUE_DESERIALIZER_CLASS_CONFIG, "");
170 propsConsumer.put("security.protocol", "")
171 propsConsumer.put("sasl.mechanism", "")
172 propsConsumer.put("basic.auth.credentials.source", "")
173 propsConsumer.put("basic.auth.user.info", "")
174 //set all the props on Kafka Consumer

175
176 log.info("Initializing and subscribing to the topic")
177
178 SoftAssertions softly = new SoftAssertions()
179

180 KafkaConsumer<String, GenericRecord> consumer = new → KafkaConsumer<>(propsConsumer)
181 consumer.subscribe(Arrays.asList(topicConsumer))

183 ConsumerRecords<String, GenericRecord> records = consumer.poll(1000)
184

185 for(ConsumerRecords<String, GenericRecord> record : records) {
186 GenericRecord avroRecordConsum = record.value()
187 log.info("Current record value: " + record.value().toString())
188 String eventIdConsumer =
→ avroRecordConsum.get("eventHeader.eventId").toString()

189

190 if(eventId.equals(eventIdConsumer)) {
191 softly.assertThat(avroRecordConsum.
192 get("eventHeader.eventType").toString()).
193 IsEqualTo(eventType)
194 softly.assertThat(avroRecordConsum.
195 get("eventHeader.eventSubtype").

196

197 softly.assertThat(avroRecordConsum.
198 get("eventHeader.eventDateTime").
199 toString()).isEqualTo(currentDateComplete)
200 softly.assertThat(avroRecordConsum.
201 get("eventHeader.
202 eventGeneratedDateTime").toString()).
203 isEqualTo(currentDateComplete)

204

205 softly.assertThat(avroRecordConsum.
206 get("eventHeader.eventId").
207 toString()).isEqualTo(eventId)

208

209 softly.assertThat(avroRecordConsum.
210 get("eventHeader.eventRequestId").
211 toString()).isEqualTo(eventId)

212

213 softly.assertThat(avroRecordConsum.
214 get("eventHeader.evntSrcDesc").
215 toString()).isEqualTo(evntSrcDesc)

216
217 //add assertions for all fields
218 }
219 long offset = record.offset()
220 log.info("Consumed record at offset: " + offset)
221 }
222 softly.assertAll()
223 consumer.close()
224
225 log.info("Validating the Consumer fields against the fields produced → with success, messages is sent on Consumer side")
226
227 log.info("Creating Agr Cst with ..." + end_url_agr_c_agrKey)

229 OkHttpClient client = new OkHttpClient()
230 Request request_agrKey = new Request.Builder().
231 url(url_agrKey).
232 header("Authorization", Credentials.
233 basic(username_agr, password_agr)).
234 header("Content-Type", "application/json").build()

235
236 log.info("Agr c MS:" + request_agrKey)
237

238 log.info("Calling the Agr Cst...")
239 Response response_agrKey = client.newCall(request_agrKey).execute()
240 String responseBody_agrKey = response_agrKey.body().string()
241 int statusCode_agrKey = response_agrKey.code()

242
243 log.info("Response for agrKey:" + responseBody_agrKey.toString()) 244 log.info("Status code for agrKey:" + statusCode_agrKey)
245
246 JSONObject jsonRspAgrC = new JSONObject(responseBody_agrKey)
247

248 JSONArray agr = jsonRspAgrC.getJSONArray("agr")
249 JSONObject c = agr.getJSONObject(0).getJSONArray("agrC").
250 getJSONObject(0)

251
252 JSONArray keyArray = jsonRspAgrC.getJSONArray("agr") 253 String keyMS = keyArray.getJSONObject(0).getString("agrKey")
254

255 String type = c.getString("type")
256 String mbrGUID_A_C = c.getString("mbrGUID")
257 log.info("Type role from Agr Cst is:" + type)

258
259 //Save mbrGUID
260 log.info("Saving mbrGUID..")
261
262 String url_c = base_url_c + middle_url_c + mbrGUID_A_C 263 log.info("Creating MS with ..." + url_c)
264

265 Request request_cId = new
→ Request.Builder().url(url_c).header("Authorization", → Credentials.basic(username_c, password_c)).

266 header("Content-Type", "application/json").build() 267 log.info(" Cst Id:" + request_cId)
268

269 log.info("Calling the Cst Id...")
270 Response response_cId = client.newCall(request_cId).
271 execute()
272 String responseBody_customeId = response_cId.body().
273 string()
274 int statusCode_cId = response_cId.code()

276 JSONObject jsonResponsecId = new JSONObject(responseBody_customeId)
277

278 JSONArray cs = jsonResponsecId.getJSONArray("cs")
279 String mbrGUIDId = cs.getJSONObject(0).
280 getString("mbrGUID")

281

282 if(roleType.equals(expectedRole) &&
→ (mbrGUID_A_C).equals(mbrGUIDId)) {
283 log.info ("key from producer is: " + agrKeyProducer)
284 log.info("key from Agr Cst is: " + keyMS)

285 }
286

287 //DB validations
288 Connection conn = DriverManager.
289 getConnection(connectionUrl, username, password)
290 PreparedStatement statement = conn.prepareStatement(queryMessage)
291 statement.setString(1, eventId)

292

293 ResultSet rs = statement.executeQuery()
294 if(rs.next()){
295 String payload = rs.getString("payload")
296 String source = rs.getString("source")
297 String type = rs.getString("type")

298

299 JSONObject jsonPayload = new JSONObject(payload)
300 JSONObject data = jsonPayload.
301 getJSONObject("content").getJSONObject("data")

302

303 String origEvntId = data.getString("origEvntId")
304 String origEvntSrcDesc = data.getString("origEvntSrcDesc")
305 String origAppId = data.getString("origAppId")
306 String number = data.getString("number")
307 String origEvntId = data.getString("origEvntId")
308 String origEvntDateTime = data.getString("origEvntDateTime")
309 JSONArray onsuccess = jsonPayload.getJSONObject("triggers").
310 getJSONArray("onsuccess")
311 String DBmsgType = jsonPayload.getString("msgType")

312

313 if (origEvntId.equals(eventId)) {
314 softly.assertThat(origAppId).isEqualTo(appId)
315 softly.assertThat(type).isEqualTo("auto")
316 //softly.assertThat(origEvntSrcDesc).
317 isEqualTo(evntSrcDesc)

318

319 softly.assertThat(number).isEqualTo(prim_id)
320 log.info("DB policy No:" + number)
321 softly.assertThat(origEvntId).isEqualTo(eventId)
322 softly.assertThat(origEvntDateTime).
323 isEqualTo(currentDateComplete)

324 softly.assertThat(DBmsgType).isEqualTo(msgType) 325 softly.assertAll()
326 }
327 }
328 conn.close()
329
330 log.info("Validating the db fields against the fields produced with → success")
Ready API test results:
Figure 13: Ready API Test Results

If the tests are all passed then you don’t have to worry, but if there are failures in the tests’ execution is very diﬀicult to know exactly what is the cause, without a proper logging or debugging.
In case of multiple failures test by test needs to be ran and check the logs when the implementation is done with Groovy, with pre built in functionalities provided by Ready API is impossible to check where the failure occurs.
A lot of logging is required in order to have code more readable, important fact is that as we add more complexity to our tests and add more steps the bulk code become harder and harder to be followed and this results in diﬀiculties in debugging too. Based on all the aspects and test scripts mentioned above, writing Ready API test scripts becomes extremely diﬀicult and challenging and impossible for good coding standards and practices to be followed. Part 3: Case Studies and Best Practices

• Chapter 7: Real-World Automation Success Stories
1. Case studies of successful automation implementations for web apps, RESTful APIs, Kafka streams and Databases
2. Lessons learned and best practices
◦ Case studies of successful automation implementation for web apps, RESTful APIs, Kafka streams and Databases.

1. Web apps cases : in Chapter 2 we have covered step by step guideline on how to create
testing automation solutions using Java and Selenium following Page Object Model design pattern in scope of functional, integration, end-to-end, and regression testing.

2. Backend systems: RESTful APIs, Kafka streams and DBs.

In our practical projects from previous chapters we covered automating testing solutions for RESTFul APIs in Chapter 3 in scope of functional and regression testing. Also in Chapter 7 we covered integration and end-to-end testing with ReadyAPI test scripts for RESTful APIs, Kafka and Database.

Next we are going through the implementation for a testing automation solution covering Kafka
Producer/Consumer, RESTful APIs and Databases projects implemented in the same solution.

Case 1: Kafka Producer/Consumer, RESTFul APIs and MySQL database systems.

Data flows between Kafka Producer, Kafka Consumer, RESTful APIs and Database systems.
1. Kafka Producer/Consumer logic implementation is covered using:

• properties set on Producer/Consumer side;
• define Avro schema on Producer so the message is published on a topic;
• subscribe to a Consumer topic;
• consume and validate the message against Avro schema from step 1;

2. RESTful API logic implementation is covered using: 1. data modeling by creating payloads for POST methods;

2. creating connector setting POST, PATCH, PUT, GET, DELETE methods and the headers setup;
3. response message parsing for validations and data stored for next processing steps.

3. Database logic implementation is covered using: 1. define connection to the database; 2. define necessarily queries;

3. interrogate the data and make validations required to follow the same data from the message producer.
Structure of the project is illustrated bellow:
Figure 14: Kafka-RESTful APIs-DB project architecture main package contains necessarily java utilities, setup, data modeling used in the tests’ logic.

Figure 15: Kafka-RESTful APIs-DB main architecture test package contains a collection of JUnit tests grouped by project packages to cover
functional, integration, end-to-end and regression testing.

Figure 16: Kafka-RESTful APIs-DB test architecture
1.a) Kafka Consumer properties set up:
1 package financial.kafka;
2
3 import org.apache.kafka.clients.consumer.*;
4 import org.apache.kafka.common.serialization.StringDeserializer;
5
6 import java.util.Properties;
7
8 public class KafkaConsumerSetup {
9 public Properties setConsumerProps(String bootstrapServers, 10 String uniqueGroupID, String authInfo, String jaasConfig, 11 String trustStoreLocation, String schemaRegistryUrl) {
12

13 Properties props = new Properties();
14 props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, → bootstrapServers);
15 props.put(ConsumerConfig.GROUP_ID_CONFIG, uniqueGroupID);
16 props.put(ConsumerConfig.
17 KEY_DESERIALIZER_CLASS_CONFIG,
→ StringDeserializer.class.getName());
18 props.put(ConsumerConfig.
19 VALUE_DESERIALIZER_CLASS_CONFIG,
→ StringDeserializer.class.getName());

20

21 props.put("security.protocol", "SASL_SSL");
22 props.put("sasl.mechanism", "PLAIN");
23 props.put("basic.auth.credentials.source", "USER_INFO");
24 props.put("basic.auth.user.info", authInfo);
25 props.put("sasl.jaas.config", jaasConfig);
26 props.put("ssl.truststore.location", trustStoreLocation);
27 props.put("ssl.truststore.password", "changeit");
28 props.put("schema.registry.url", schemaRegistryUrl);
29 props.put("schema.registry.ssl.truststore.location", → trustStoreLocation);
30 props.put("schema.registry.ssl.truststore.password", → "changeit");
31 props.put("auto.offset.reset", "latest");
32 return props;

33 }
34 }

JUnit Test for the Kafka Consumer
subscribes successfully to a Consumer topic defined in local-config.properties file steps:

• Set properties for Kafka Consumer;
• Subscribe to Consumer topic.
1 package financial.KafkaProducerConsumerRestfulApisDB.consumer;
2

3 import financial.kafka.KafkaConsumerSetup;
4 import financial.utils.general.ConfigUtils;
5 import org.apache.kafka.clients.consumer.KafkaConsumer;
6 import org.junit.Test;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;

9 import java.text.SimpleDateFormat; 10 import java.util.Arrays;
11 import java.util.Date;
12 import java.util.Properties;

13

14 public class ConsumerTest {
15 protected static final Logger logger =
→ LoggerFactory.getLogger(ConsumerTest.class);
16 String pattern = "mm-dd-yyyy";
17 SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
18 String currentDate = dateFormat.format(new Date());
19 String UNIQUE_GROUP_ID = "automation_events_consumer" + → currentDate;

20

21 @Test
22 public void subscribeToConsumerTopicHappyTest() {
23 KafkaConsumerSetup kafkaConsumerSetup = new → KafkaConsumerSetup();
24 Properties props =
→ kafkaConsumerSetup.setConsumerProps(ConfigUtils.
25 getProperty(String.valueOf(ConfigUtils.
26 ConfigKeys.BOOTSTREP_SERVER_QA)), UNIQUE_GROUP_ID,
27 ConfigUtils.getProperty(String.valueOf(
28 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
29 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
30 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)), → ConfigUtils.getProperty(String.valueOf(
31 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));
32 logger.info("Adding properties to Kafka Consumer");
33 KafkaConsumer<String, String> consumer = new → KafkaConsumer<>(props);
34 consumer.subscribe(Arrays.asList(
35 ConfigUtils.getProperty(
36 String.valueOf(ConfigUtils.ConfigKeys.
37 TOPIC_FINANCE_CONSUMER))));
38 consumer.close();

39 }
40 }
Kafka Consumer logging:

1 [main] INFO financial.KafkaProducerConsumerRestfulApisDB.consumer.
2 ConsumerTest Adding properties to Kafka Consumer...
3 [main] INFO org.apache.kafka.clients.consumer.ConsumerConfig → ConsumerConfig values:

4 ^^Iallow.auto.create.topics = true
5 ^^Iauto.commit.interval.ms = 5000
6 ^^Iauto.include.jmx.reporter = true
7 ^^Iauto.offset.reset = latest
8 ^^Iclient.id = consumer-automation_doc_evnt_consumer32-01-2024-1

9
10 [main] INFO org.apache.kafka.common.telemetry.internals. 11 KafkaMetricsCollector initializing Kafka metrics collector
12
13 [main] INFO org.apache.kafka.common.security.authenticator. 14 AbstractLogin Successfully logged in.
15
16 [main] INFO org.apache.kafka.common.utils.AppInfoParser Kafka → version: 3.7.0
17
18 [main] INFO org.apache.kafka.common.utils.AppInfoParser Kafka → commitId: 2ae524ed625438c5
19
20 [main] INFO org.apache.kafka.common.utils.AppInfoParser Kafka → startTimeMs: 1727800337389
21

22 [main] INFO org.apache.kafka.clients.consumer.internals.
23 LegacyKafkaConsumer - [Consumer clientId=
24 consumer-automation_events_consumer32-01-2024-1,
→ groupId=automation_events_consumer32-01-2024] Subscribed to → topic(s):
25 topic_finance

26

27 [main] INFO org.apache.kafka.clients.consumer.internals.
28 ConsumerCoordinator - [Consumer
→ clientId=consumer-automation_events_consumer32-01-2024-1, → groupId=automation_events_consumer32-01-2024] Resetting → generation and member id due to: consumer pro-actively leaving → the group

29

30 [main] INFO org.apache.kafka.clients.consumer.internals.
31 ConsumerCoordinator - [Consumer
→ clientId=consumer-automation_events_consumer32-01-2024-1, → groupId=automation_events_consumer32-01-2024] Request joining → group due to: consumer pro-actively
32 leaving the group

33

34 [main] INFO org.apache.kafka.common.metrics.Metrics Metrics → scheduler closed
35 [main] INFO org.apache.kafka.common.metrics.Metrics Closing → reporter org.apache.kafka.common.metrics.JmxReporter
36 [main] INFO org.apache.kafka.common.metrics.Metrics Closing → reporter org.apache.kafka.common.telemetry.internals.
37 ClientTelemetryReporter

38 [main] INFO org.apache.kafka.common.metrics.Metrics Metrics → reporters closed
39 [main] INFO org.apache.kafka.common.utils.AppInfoParser App info → kafka.consumer for
→ consumer-automation_events_consumer32-01-2024-1 unregistered

40
41 Process finished with exit code 0
42 }
Consumer Test Overview: logs test result with success:
Figure 17: Consumer Test Overview
1.b) Kafka Producer properties set up:
1 package financial.kafka;
2

3 import org.apache.avro.Schema;
4 import org.apache.avro.generic.GenericData;
5 import org.apache.avro.generic.GenericRecord;
6 import org.apache.kafka.clients.producer.ProducerConfig;
7 import java.util.*;

8
9 public class KafkaProducerSetup {
10
11
12

13 public Properties setProducerProps(String bootstrapServers, → String authInfo, String jaasConfig, String → trustStoreLocation, String schemaRegistryUrl)

14 {

15 Properties props = new Properties();
16 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, → bootstrapServers);
17 props.put(ProducerConfig.
18 KEY_SERIALIZER_CLASS_CONFIG,
19 org.apache.kafka.common.serialization.
20 StringSerializer.class);

21

22 props.put(ProducerConfig.
23 VALUE_SERIALIZER_CLASS_CONFIG,
24 io.confluent.kafka.serializers.KafkaAvroSerializer.
25 class);
26 props.put("security.protocol", "SASL_SSL");
27 props.put("sasl.mechanism", "PLAIN");
28 props.put("basic.auth.credentials.source", "USER_INFO");
29 props.put("basic.auth.user.info", authInfo);
30 props.put("sasl.jaas.config", jaasConfig);
31 props.put("ssl.truststore.location", trustStoreLocation);
32 props.put("ssl.truststore.password", "changeit");
33 props.put("schema.registry.url", schemaRegistryUrl);
34 props.put("schema.registry.ssl.truststore.
35 location", trustStoreLocation);
36 props.put("schema.registry.ssl.truststore.password", → "changeit");
37 props.put("auto.register.schemas", false);
38 props.put("specific.avro.reader", true);
39 return props;

40 }
41

42 public static GenericRecord produceMessage(String eventType, → String eventSubtype, String currentDateComplete, String → eventId, String evnt_desc, String evntS, String d_type, → String b_area, String pfx,

43 String obj_s, String mimeType, String sys_id, String doc, → String GUID) {
44

45 String avroSchema = "{}"
46 Schema.Parser parser = new Schema.Parser();
47 Schema avroSchemaSystem = parser.parse(avroSchema);

48
49 GenericRecord avroRecord = new
→ GenericData.Record(avroSchemaSystem);
50
51 GenericRecord eventHeader = new → GenericData.Record(avroSchemaSystem.

52 getField("eventheader").
53 schema());
54 eventHeader.put("eventType", eventType);
55 eventHeader.put("eventSubtype", eventSubtype);
56 eventHeader.put("eventDateTime", currentDateComplete);
57 eventHeader.put("eventGeneratedDateTime", currentDateComplete);
58 eventHeader.put("eventId", eventId);
59 eventHeader.put("evnt_req_id", eventId);
60 eventHeader.put("evnt_desc", evnt_desc);
61 eventHeader.put("evntS", evntS);
62 eventHeader.put("metadata", new HashMap<>());

63

64 GenericRecord eventBody = new
→ GenericData.Record(avroSchemaSystem.
65 getField("eventBody").schema());
66 eventBody.put("d_type", d_type);
67 eventBody.put("b_area", b_area);
68 eventBody.put("pfx", pfx);
69 eventBody.put("obj_s", obj_s);
70 eventBody.put("MimeType", mimeType);

71

72 eventBody.put("sys_id", sys_id);
73 eventBody.put("tran_id", new
→ GenericData.Array<>(avroSchemaSystem.
74 getField("eventBody").schema().
75 getField("tran_id").schema(),
76 Arrays.asList("1111")));

77

78 eventBody.put("DateCreated", currentDateComplete);
79 eventBody.put("doc", doc);
80 eventBody.put("GUID", GUID);
81 eventBody.put("MajorVersion", "1");
82 eventBody.put("MinorVersion", "0");

83

84 //propertiesList implementation
85 List<GenericRecord> propertiesList =
86 new ArrayList<>();

87

88 Schema propertiesListSchema =
→ avroSchemaSystem.getField("eventBody").schema().
89 getField("propertiesList").schema().
90 getElementType();
91 GenericRecord propertyRecord = new
→ GenericData.Record(propertiesListSchema);

92

93 propertyRecord.put("name", "PropertyName");
94 propertyRecord.put("value", "PropertyValue");
95 propertyRecord.put("type", "PropertyType");
96 propertyRecord.put("multiValue", "PropertyMultiValue");

97 propertyRecord.put("multiList", new
→ GenericData.Array<>(propertiesListSchema.
98 getField("multiList").schema(),
99 Arrays.asList("Value1", "Value2")));
100 propertiesList.add(propertyRecord);
101 eventBody.put("propertiesList", propertiesList);

102
103 avroRecord.put("eventheader", eventHeader); 104 avroRecord.put("eventBody", eventBody);
105

106 System.out.println("System message produced: " + eventHeader + → eventBody);
107 return avroRecord;

108
109 }

Kafka Producer Test implementation steps :
• Set properties for Kafka Producer;
• Generate a valid Avro schema message;
• Message is successfully published on the partition and offset topic Producer.

1 package financial.KafkaProducerConsumerRESTfulAPIsDB.producer;
2

3 import financial.kafka.KafkaProducerSetup;
4 import financial.utils.general.ConfigUtils;
5 import org.apache.avro.generic.GenericRecord;
6 import org.junit.Test;
7 import org.slf4j.*;
8 import java.text.SimpleDateFormat;
9 import java.util.*;
10 import java.util.concurrent.ExecutionException; 11 import org.apache.kafka.clients.producer.KafkaProducer; 12 import org.apache.kafka.clients.producer.ProducerRecord; 13 import org.apache.kafka.clients.producer.RecordMetadata;

14

15 public class ProducerAvroSchemaHappyTest {
16 protected static final Logger logger =
→ LoggerFactory.getLogger(ProducerAvroSchemaHappyTest.
17 class);

18

19 Date now = new Date();
20 SimpleDateFormat date = new SimpleDateFormat(
21 "yyyy-MM-dd HH:mm:ss");
22 String currentDateComplete = date.format(now);
23 UUID generateUUID = UUID.randomUUID();

24 String eventType = "eventType";
25 String eventSubtype = "eventSubtype";
26 String evntId = generateUUID.toString();
27 String evntDesc = "Dummy Automation Test";
28 String evntS = "evntS";
29 String d_type = "type";
30 String b_area = "area";
31 String pfx = "pfx";
32 String obj_s = "s";
33 String mimeType = "auto";
34 String sys_id = "Auto test";
35 String doc = "auto";
36 String GUID = '{' + generateUUID.toString() + '}';

37

38 @Test
39 public void producerMessageSimpleAgrtKeyHappyTest() throws → ExecutionException, InterruptedException {
40 KafkaProducerSetup kafkaProducerSetup = new → KafkaProducerSetup();

41
42 logger.info("Adding properties to Kafka Producer...");
43

44 Properties props = kafkaProducerSetup.setProducerProps(
45 ConfigUtils.getProperty(String.valueOf(
46 ConfigUtils.ConfigKeys.BOOTSTREP_SERVER_QA)), → ConfigUtils.getProperty(String.valueOf(
47 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
48 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
49 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)), → ConfigUtils.getProperty(String.valueOf(
50 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));

51

52 GenericRecord message =
→ KafkaProducerSetup.produceMessage(eventType, → eventSubtype, currentDateComplete, evntId, evntDesc, → evntS, d_type, b_area, pfx, obj_s,

53 mimeType, sys_id, doc, GUID);
54
55 logger.info("Preparing/Sending the object to Kafka → Producer");
56

57 ProducerRecord<Object, Object> record =
58 new ProducerRecord<>(ConfigUtils.
59 getProperty(String. valueOf(ConfigUtils.ConfigKeys.
60 TOPIC_NAME_PRODUCER)), null, message);

61
62 63
64 KafkaProducer<Object, Object> producer = new → KafkaProducer<>(props);
65

66 RecordMetadata metadata = producer.send(record).get();
67 producer.flush();
68 producer.close();

69

70 logger.info("Message sent successfully to topic" +
71 metadata.topic() + " " + "partition: " + → metadata.partition()
72 + " " + "offset: " + metadata.offset());

73 }
74 }
Test Results Kafka Producer Overview:
Figure 18: Kafka Producer Overview

With Kafka Producer and Kafka Consumer functional tests we can implement end-to-end test
between Kafka Producer and Kafka Consumer by generating message against Avro schema and validate it on the Kafka Consumer side.
1.c) Kafka Producer - Kafka Consumer integration test steps:

• Set properties for Kafka Producer;
• Generate a valid Avro schema message;
• Message is successfully published on topic Producer: partition and offset;
• Set properties for Kafka Consumer;
• Subscribe to the Consumer topic;
• Consume and validate published message fields by evntId field.

1 package financial.KafkaProducerConsumerRESTfulAPIsDB. 2 producer_consumer;
3

4 import financial.kafka.KafkaConsumerSetup;
5 import financial.kafka.KafkaProducerSetup;
6 import fiancial.utils.general.ConfigUtils;
7 import org.apache.avro.generic.GenericRecord;
8 import org.apache.kafka.clients.consumer.ConsumerRecord;
9 import org.apache.kafka.clients.consumer.ConsumerRecords; 10 import org.apache.kafka.clients.consumer.KafkaConsumer; 11 import org.apache.kafka.clients.producer.KafkaProducer; 12 import org.apache.kafka.clients.producer.ProducerRecord; 13 import org.apache.kafka.clients.producer.RecordMetadata; 14 import org.junit.Test;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import java.text.SimpleDateFormat;
18 import java.util.Arrays;
19 import java.util.Date;
20 import java.util.Properties;
21 import java.util.UUID;
22 import java.util.concurrent.ExecutionException;

23
24 import static org.assertj.core.api.Assertions.assertThat;
25
26 public class ProducerAvroSchemaConsumerValidationsHappyTest
27 {

28 protected static final Logger logger = LoggerFactory.getLogger(
29 ProducerAvroSchemaConsumerValidationsHappyTest.class);
30 Date now = new Date();
31 SimpleDateFormat date = new SimpleDateFormat(
32 "yyyy-MM-dd HH:mm:ss");
33 String currentDateComplete = date.format(now);
34 UUID generateUUID = UUID.randomUUID();
35 String eventType = "ev";
36 String eventSubtype = "stype";
37 String evntId = generateUUID.toString();
38 String evntDesc = "Auto";

39 String evnt_s = "evnt";
40 String d_type = "type";
41 String b_area = "area";
42 String pfx = "pfx";
43 String obj_s = "autoS";
44 String m_type = "auto";
45 String sys_id = "auto";
46 String tran_id = "auto";
47 String prim_id = "prim";
48 String adm_sys = "sys";
49 String doc = "aut";
50 String GUID = '{' + generateUUID.toString() + '}';
51 String pattern = "mm-dd-yyyy";
52 SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
53 String currentDate = dateFormat.format(new Date());
54 String UNIQUE_GROUP_ID = "automation_events_consumer" +
55 currentDate;

56

57 @Test
58 public void producerMessageConsumerValidationsHappyTest() → throws ExecutionException, InterruptedException {
59 KafkaProducerSetup kafkaProducerSetup = new → KafkaProducerSetup();

60
61 logger.info("Adding properties to Kafka Producer...");
62

63 Properties propsProducer =
→ kafkaProducerSetup.setProducerProps(
64 ConfigUtils.getProperty(String.valueOf(
65 ConfigUtils.ConfigKeys.BOOTSTREP_SERVER_QA)), → ConfigUtils.getProperty(String.valueOf(
66 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
67 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
68 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)), → ConfigUtils.getProperty(String.valueOf(
69 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));
70 GenericRecord message =
→ KafkaProducerSetup.produceMessage(eventType,
71 eventSubtype, currentDateComplete, evntId,
72 evntDesc, evnt_s, d_type, b_area, pfx, obj_s, m_type, → sys_id, doc, GUID);

73
74 logger.info("Preparing/Sending the object to Kafka → Producer");
75
76 ProducerRecord<Object, Object> recordProducer = 77 new ProducerRecord<>(ConfigUtils.getProperty(
78 String.valueOf(ConfigUtils.ConfigKeys. 79 TOPIC_NAME_PRODUCER)), null, message);
80

81 KafkaProducer<Object, Object> producer = new → KafkaProducer<>(propsProducer);
82 RecordMetadata metadata =
→ producer.send(recordProducer).get();
83 producer.flush();
84 producer.close();

85

86 logger.info("Message sent successfully to topic" + → metadata.topic() + " " + "partition: " + → metadata.partition() + " " + "offset: " + → metadata.offset());

87
88 KafkaConsumerSetup kafkaConsumerSetup = new → KafkaConsumerSetup();
89

90 logger.info("Adding properties to Kafka Consumer...");
91 Properties propsConsumer =
→ kafkaConsumerSetup.setConsumerProps(ConfigUtils.
92 getProperty(String.valueOf(
93 ConfigUtils.ConfigKeys.BOOTSTREP_SERVER_QA)),
94 UNIQUE_GROUP_ID, ConfigUtils.getProperty(String.valueOf(
95 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)),
→ ConfigUtils.getProperty(String.valueOf(
96 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)), → ConfigUtils.getProperty(String.valueOf(
97 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)), → ConfigUtils.getProperty(String.valueOf(
98 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));

99

100 logger.info("Initializing and subscribing to the topic");
101 KafkaConsumer<String, GenericRecord> consumer =
102 new KafkaConsumer<>(propsConsumer);
103 consumer.subscribe(Arrays.asList(ConfigUtils.
104 getProperty(String.valueOf(ConfigUtils.
105 ConfigKeys.TOPIC_SYSTEM_CONSUMER))));

106
107 ConsumerRecords<String, GenericRecord> records = → consumer.poll(1000);
108

109 for (ConsumerRecord<String, GenericRecord> record : → records) {
110 GenericRecord avroRecordConsum = record.value();

111

112 logger.info("Current record value: " + → record.value().toString());
113 String evntIdConsumer = avroRecordConsum.

114 get("eventHeader.evntId").toString();
115

116 if(evntId.equals(evntIdConsumer)) {
117 assertThat(avroRecordConsum.
118 .toString()).
119 isEqualTo(eventType);

120

121 assertThat(avroRecordConsum.
122 get("eventHeader.eventSubtype").
123 toString()).isEqualTo(eventSubtype);

124

125 assertThat(avroRecordConsum.
126 get("eventHeader.eventDateTime").
127 toString()).isEqualTo(currentDateComplete);
128 assertThat(avroRecordConsum.
129 get("eventHeader.eventGeneratedDateTime")
130 .toString()).isEqualTo(currentDateComplete);

131

132 assertThat(avroRecordConsum.
133 get("eventHeader.evntId").
134 toString()).isEqualTo(evntId);

135

136 assertThat(avroRecordConsum.
137 get("eventHeader.eventRequestId") → .toString()).isEqualTo(evntId);

138

139 assertThat(avroRecordConsum.
140 get("eventHeader.evntDesc")
141 .toString()).isEqualTo(evntDesc);

142

143 assertThat(avroRecordConsum.
144 get("eventBody.b_area")
145 .toString()). isEqualTo(b_area);

146

147 assertThat(avroRecordConsum.
148 get("eventBody.obj_s").
149 toString()).isEqualTo(obj_s);

150

151 assertThat(avroRecordConsum.
152 get("eventBody.m_type").
153 toString()).isEqualTo(m_type);

154

155 assertThat(avroRecordConsum.
156 get("eventBody.sys_id").toString()).
157 isEqualTo(sys_id);

158
159 //remaining assertions
160 }
161 long offset = record.offset();
162 logger.info("Consumed record at offset: " + offset);
163 }
164 consumer.close();
165 }
166 }
Kafka Producer - Consumer test results:
Figure 19: Kafka Producer - Kafka Consumer test results

2.a) GET Agr Cst test RESTful API steps:
• Make a call to Agr Cst RESTful API;
• Parse the response message;
• Validate and save roleType and mbrGUIDAgrCst values fields.

1 package financial.KafkaProducerConsumerRESTfulAPIsDB.micros;
2

3 import io.restassured.response.Response;
4 import financial.utils.connector.RestAssuredConnector;
5 import financial.utils.general.ConfigUtils;
6 import org.json.JSONArray;
7 import org.json.JSONObject;
8 import org.junit.Test;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import java.util.Map;
12 import static org.junit.Assert.assertEquals;

13

14 public class GETAgrCstHappyTest {
15 protected static final Logger logger =
→ LoggerFactory.getLogger(GETAgrCstHappyTest.class);
16 private String requestUri;
17 private String END_URL_AGR_QA="99999";

18

19 @Test
20 public void GETAgrCstHappyTest() {
21 requestUri = ConfigUtils.getProperty(ConfigUtils.
22 ConfigKeys.BASE_URL_QA.toString())

23 +

24 ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
25 MIDDLE_URL_AGR_QA.toString()) + END_URL_AGR_QA;
26 logger.info("requestUri:" + " " + requestUri);

27

28 RestAssuredConnector connector = new RestAssuredConnector();
29 Map<String, String> headers =
→ RestAssuredConnector.setHeaders(
30 ConfigUtils.getProperty(
31 ConfigUtils.ConfigKeys.TOKEN_AG_CSTR.toString()));
32 Response agrRsp = connector.getRequest(requestUri, headers);

33
34 String rsp_agrKey = agrRsp.getBody().asString(); 35 logger.info("Response:" + " " + rsp_agrKey);
36
37 assertEquals(200, agrRsp.getStatusCode()); 38 JSONObject jsonResponse = new JSONObject(rsp_agrKey);
39

40 JSONArray agrts = jsonResponse.getJSONArray("agrts");
41 JSONObject cst =
→ agrts.getJSONObject(0).getJSONArray("agrCst").
42 getJSONObject(0);

43

44 JSONArray agrtsArray = jsonResponse.getJSONArray("agrts");
45 String agrtKeyMS =
→ agrtsArray.getJSONObject(0).getString("agrtKey");

46
47 String roleType = cst.getString("roleType"); 48 String mbrGUIDAgrCst = cst.getString("mbrGUID");
49

50 logger.info("Fields extracted from the ms:" + "agrkey: " + → agrtKeyMS + " " + "roleType: " + roleType + " " + "mbr → GUID:" + mbrGUIDAgrCst);

51 }
52 }
2.b) Cst Id test RESTful API steps:
• Make call to Cst Id RESTful API;
• Parse response message;
• Validate and save mbrGUID value field.
1 package financial.KafkaProducerConsumerRESTfulAPIsDB.micros;
2

3 import io.restassured.response.Response;
4 import massmutual.utils.connector.RestAssuredConnector;
5 import massmutual.utils.general.ConfigUtils;
6 import org.json.JSONArray;
7 import org.json.JSONObject;
8 import org.junit.Test;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import java.util.Map;
12 import static org.junit.Assert.assertEquals;

13

14 public class GETCstIdHappyTest {
15 protected static final Logger logger =
→ LoggerFactory.getLogger(GETCstIdHappyTest.class);
16 private String requestUri;
17 private String CST_ID = "DEMO";

18

19 @Test
20 public void getCustomerIdHappyTest() {
21 requestUri = ConfigUtils.getProperty(ConfigUtils.
22 ConfigKeys.BASE_URL_QA.toString())

23 +
24 ConfigUtils.getProperty(ConfigUtils.ConfigKeys. 25 MIDDLE_URL_CST_QA.toString()) + CUSTOMER_ID;
26
27 RestAssuredConnector connector = new RestAssuredConnector();
28

29 Map<String, String> headers =
→ RestAssuredConnector.setHeaders(
30 ConfigUtils.getProperty(
31 ConfigUtils.ConfigKeys.TOKEN_CST_ID.toString()));

32

33 Response cstrIdResponse = connector.getRequest(requestUri, → headers);
34 logger.info("Cst Url:" + " " + requestUri);

35

36 String responseBody_cstId =
→ cstIdResponse.getBody().asString();
37 logger.info("Response:" + " " + responseBody_cstrId);

38
39 assertEquals(200, cstIdResponse.getStatusCode());
40 logger.info("Accessing mbrGUID..");
41
42 JSONObject jsonResponse = new → JSONObject(responseBody_cstId);
43
44 JSONArray cst = jsonResponse.getJSONArray("cst"); 45 String mbrGUID = cst.getJSONObject(0).getString("mbrGUID");
46
47 logger.info("mbrGUID: " + mbrGUID);
48 }
49 }
50

3. Kafka Producer - DB: integration test to cover unhappy path: no record found in the database having an invalid type.
Steps:

• Set properties on Kafka Producer;
• Generate message having invalid dType value field;
• Connect to the database;
• No record is found for our type: ”auto” for the message produced.

1
2 package financial.KafkaProducerConsumerRESTfulAPIsDB. 3 producer_db;
4

5 import financial.db.DBConnections;
6 import financial.kafka.KafkaProducerSetup;
7 import financial.utils.general.ConfigUtils;
8 import org.apache.avro.generic.GenericRecord;
9 import org.apache.kafka.clients.producer.KafkaProducer; 10 import org.apache.kafka.clients.producer.ProducerRecord; 11 import org.apache.kafka.clients.producer.RecordMetadata; 12 import org.json.JSONObject;
13 import org.junit.Test;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;

16

17 import java.sql.ResultSet;
18 import java.sql.SQLException;
19 import java.text.SimpleDateFormat;
20 import java.util.Date;
21 import java.util.Properties;
22 import java.util.UUID;

23 import java.util.concurrent.ExecutionException;
24

25 public class ProducerAvroSchemaDBValidationsType
26 AutoAndroidUnhappyTest {
27 protected static final Logger logger = LoggerFactory.getLogger( → ProducerAvroSchemaDBValidationsTypeAutoAndroid.class);

28

29 Date now = new Date();
30 SimpleDateFormat date = new SimpleDateFormat(
31 "yyyy-MM-dd HH:mm:ss");
32 String currentDateComplete = date.format(now);
33 UUID generateUUID = UUID.randomUUID();
34 String eventType = "enterpriseservices";
35 String eventSubtype = "documentaddupdate";
36 String evntId = generateUUID.toString();
37 String evntDesc = "Auto";
38 String evntS = "Auto";
39 String dType = "DUMMY";
40 String bArea = "Auto";
41 String pfx = "pfx";
42 String objS = "AUTO";
43 String mimeType = "app";
44 String sysId = "Auto";
45 String primId = "999";
46 String doc = "pfx";
47 String GUID = '{' + generateUUID.toString() + '}';
48 String appIdExp = "AUTO";
49 String msgType = "auto";
50 DBConnections dbConn;
51 String QUERY = "SELECT ident, type, format, source, → received_date, status, payload FROM message WHERE type
52 = \"auto\" AND source = \"AUTO\" AND JSON_EXTRACT(payload, → \"$.content.data.appIdType\")=\"androidAppId\" AND → JSON_EXTRACT(payload, \"$.content.data.origEvId\")=?";

53

54 @Test
55 public void produceMessageDBValidationsTypeAutoAndroid
56 UnHappyTest() throws ExecutionException, InterruptedException, → SQLException {
57 KafkaProducerSetup kafkaProducerSetup = new
→ KafkaProducerSetup();

58
59 logger.info("Adding properties to Kafka Producer...");
60

61 Properties props =
→ kafkaProducerSetup.setProducerProps(ConfigUtils.
62 getProperty(String.valueOf(
63 ConfigUtils.ConfigKeys.BOOTSTREP_SERVER_QA)), → ConfigUtils.getProperty(String.valueOf(

64 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)),
65 ConfigUtils.getProperty(String.valueOf(
66 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)),
67 ConfigUtils.getProperty(String.valueOf(
68 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)),
69 ConfigUtils.getProperty(String.valueOf(
70 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));

71

72 GenericRecord message = KafkaProducerSetup.
73 produceMessageComposeAgreementKey(eventType, eventSubtype, → currentDateComplete, evntId, evntDesc, evntS, dType, → bArea, pfx, objS, mimeType, sysId, doc, GUID);

74
75 logger.info("Preparing/Sending the object to Kafka → Producer");
76

77 ProducerRecord<Object, Object> record = new → ProducerRecord<> (ConfigUtils.getProperty( → String.valueOf(ConfigUtils.ConfigKeys.

78 TOPIC_NAME_PRODUCER)), null, message);
79

80 KafkaProducer<Object, Object> producer = new → KafkaProducer<>(props);
81 RecordMetadata metadata = producer.send(record).get();
82 producer.flush();
83 producer.close();

84

85 logger.info("Message sent successfully to topic" + → metadata.topic() + " " + "partition: " + → metadata.partition() + " " + "offset: " +

86 metadata.offset());
87

88 logger.info("Validating the DB payload:");
89 dbConn = new DBConnections();
90 ResultSet rs = dbConn.dbConn(ConfigUtils.
91 getProperty(String.valueOf(
92 ConfigUtils.ConfigKeys.DB_URL_QA)),
→ ConfigUtils.getProperty(String.valueOf(
93 ConfigUtils.ConfigKeys.DB_USERNAME_QA)),
→ ConfigUtils.getProperty(String.valueOf(
94 ConfigUtils.ConfigKeys.DB_PASSWORD_QA)),
95 QUERY, evntId);

96

97 if (!rs.next()) {
98 logger.info("There is no message in DB");
99 } else {
100 logger.info("There is something wrong and there is a → message in DB");

101 }
102 dbConn.closeConnection(rs.getStatement(). 103 getConnection());
104 }
105 }

4. Kafka Producer - RESTful APIs - DB integration test steps:
• Set properties for Kafka Producer;
• Generate a valid Avro schema message;
• Message is successfully published on the partition and offset topic Producer;
• Make a call to Agr Cst RESTful API;
• Parse the response message;
• Validate and save roleType and mbrGUIDAgrCst values fields;
• Make call to Cst Id RESTful API;
• Parse response message;
• Validate and save mbrGUID value field;
• Connect to the database;
• Database result with type: ”auto” for the message produced is found;
• Validations for the DB columns against message produced fields.

1 package financial.KafkaProducerConsumerRESTfulAPIsDB.producer_ 2 micros_db;
3

4 import io.restassured.response.Response;
5 import financial.db.DBConnections;
6 import financial.kafka.KafkaProducerSetup;
7 import financial.utils.connector.RestAssuredConnector;
8 import financial.utils.general.ConfigUtils;
9 import org.apache.avro.generic.GenericRecord; 10 import org.apache.kafka.clients.producer.KafkaProducer; 11 import org.apache.kafka.clients.producer.ProducerRecord; 12 import org.apache.kafka.clients.producer.RecordMetadata; 13 import org.json.JSONArray;
14 import org.json.JSONObject;
15 import org.junit.Before;
16 import org.junit.Test;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19 import java.sql.ResultSet;

20 import java.sql.SQLException;
21 import java.text.SimpleDateFormat;
22 import java.util.Date;
23 import java.util.Map;
24 import java.util.Properties;
25 import java.util.UUID;
26 import java.util.concurrent.ExecutionException;
27 import static org.junit.Assert.assertEquals;

28

29 public class ProducerAvroSchemaMsDBValidationsTypeAutoHappyTest {
30 protected static final Logger logger = LoggerFactory.getLogger( → ProducerAvroSchemaMsDBValidationsTypeAutoHappyTest.
31 class);

32

33 Date now = new Date();
34 SimpleDateFormat date = new SimpleDateFormat(
35 "yyyy-MM-dd HH:mm:ss");
36 String currentDateComplete = date.format(now);
37 UUID generateUUID = UUID.randomUUID();
38 String eventType = "auto";
39 String eventSubtype = "doc";
40 String evntId = generateUUID.toString();
41 String evntDsc = "Auto";
42 String evnt_s = "auto";
43 String doc_type = "auto";
44 String b_area = "auto";
45 String pfx = "pfx";
46 String obj_s = "auto";
47 String m_type = "app";
48 String sys_is = "auto";
49 String doc = "auto";
50 String GUID = '{' + generateUUID.toString() + '}';
51 String appIdExp = "auto";
52 String msg_t = "auto";
53 DBConnections dbConn;

54

55 private String requestUriAgrCstr;
56 private String requestUriCstrId;
57 String exptRole = "auto";
58 public RestAssuredConnector connector;
59 public Response agrRsp;
60 public Response cstrIdResponse;
61 public String mbrGUIDcstrId;
62 public String QUERY = "SELECT * FROM message WHERE type = → \"auto\" AND source = \"auto\" AND JSON_EXTRACT(payload, → \"$.triggers.onsuccess\") = \"auto\" AND
63 JSON_EXTRACT(payload, \"$.content.data.origEvntId\")= ?";

64
65 private final String agr_key="999";
66 private final String agr_key_end="auto"; 67 private String CST_ID = "auto";
68

69 @Before
70 public void setupData() {
71 dbConn = new DBConnections();
72 requestUriCstrId =
→ ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
73 BASE_URL_QA.toString())

74 +
75 ConfigUtils.getProperty(ConfigUtils.
76 ConfigKeys.MIDDLE_URL_CSTRID_QA.toString()) + CST_ID;
77

78 requestUriAgrCstr =
→ ConfigUtils.getProperty(ConfigUtils.ConfigKeys.
79 BASE_URL_KONG_QA.toString()) +
80 ConfigUtils.getProperty(ConfigUtils.
81 ConfigKeys.MIDDLE_URL_AGR_QA.toString()) + agr_key
82 + "1111" + agr_key_end;

83
84 connector = new RestAssuredConnector();
85 }

86 @Test
87 public void sendKafkaMessageDbValidationsHappyTest() throws → ExecutionException, InterruptedException, SQLException {
88 KafkaProducerSetup kafkaProducerSetup = new
→ KafkaProducerSetup();

89
90 logger.info("Adding properties to Kafka Producer...");
91

92 Properties props =
→ kafkaProducerSetup.setProducerProps(ConfigUtils.
93 getProperty(String.valueOf(ConfigUtils.
94 ConfigKeys.BOOTSTREP_SERVER_QA)),
95 ConfigUtils.getProperty(String.valueOf(
96 ConfigUtils.ConfigKeys.AUTH_INFO_SYSTEM)),
97 ConfigUtils.getProperty(String.valueOf(
98 ConfigUtils.ConfigKeys.SASL_CONFIG_SYSTEM)),
99 ConfigUtils.getProperty(String.valueOf(
100 ConfigUtils.ConfigKeys.TRUST_STORE_LOCATION)), 101 ConfigUtils.getProperty(String.valueOf(
102 ConfigUtils.ConfigKeys.SCHEMA_REGISTRY)));

103

104 GenericRecord message =
→ KafkaProducerSetup.produceMessage(eventType,
105 eventSubtype, currentDateComplete, evntId,
106 evntDsc, evnt_s, doc_type, b_area, pfx, obj_s, m_type, sys_is, → doc, GUID);

108 logger.info("Preparing/Sending the object to Kafka Producer");
109 ProducerRecord<Object, Object> record = new
→ ProducerRecord<>(ConfigUtils.
110 getProperty(String.valueOf(ConfigUtils.ConfigKeys
111 TOPIC_NAME_PRODUCER)), null, message);

112

113 KafkaProducer<Object, Object> producer = new → KafkaProducer<>(props);
114 RecordMetadata metadata = producer.send(record).get();
115 producer.flush();
116 producer.close();

117

118 logger.info("Message sent successfully to topic" + → metadata.topic() + " " +
119 "partition: " + metadata.partition() + " " + "offset: " + → metadata.offset());

120

121 logger.info("Agr cstr call:");
122 Map<String, String> headers =
→ RestAssuredConnector.setHeaders(ConfigUtils.
123 getProperty(ConfigUtils.ConfigKeys.
124 TOKEN_AGR_CSTR.toString()));

125
126 Response agrRsp = connector.getRequest(requestUriAgrCstr, → headers);
127

128 logger.info("Extracting cstr, agr key, role type
129 and mbr GUID");
130 String rspBody_agrKey = agrRsp.getBody().asString();

131
132 JSONObject jsonResponse = new JSONObject( 133 rspBody_agrKey);
134

135 JSONArray agrts = jsonResponse.
136 getJSONArray("agrts");
137 JSONObject cstr =
→ agrts.getJSONObject(0).getJSONArray("agrCstr").
138 getJSONObject(0);

139

140 JSONArray agrtsArray = jsonResponse.getJSONArray("agrts");
141 String agrKeyMS =
→ agrtsArray.getJSONObject(0).getString("agrKey");

142
143 String roleType = cstr.getString("roleType");
144

145 Map<String, String> headerscstr =
→ RestAssuredConnector.setHeaders(ConfigUtils.
146 getProperty(ConfigUtils.ConfigKeys.

148 Response cstrIdResponse = connector. 149 getRequest(requestUriCstrId, headerscstr);
150

151 String responseBody_cstrId =
→ cstrIdResponse.getBody().asString();
152 JSONObject cstrResponse = new JSONObject(responseBody_cstrId);

153
154 JSONArray cstrs = cstrResponse.getJSONArray("cstrs");
155
156 String mbrGUIDcstrId =
→ cstrs.getJSONObject(0).getString("mbrGUID");
157
158 assertThat(roleType).isEqualTo(exptRole);
159 assertThat(agrKeyMS).isEqualTo(agr_key + agr_key_end);
160
161 logger.info("Validating the DB payload:");
162

163 ResultSet rs = dbConn.dbConn(ConfigUtils.
164 getProperty(
165 ConfigUtils.ConfigKeys.DB_URL_QA.name()),
166 ConfigUtils.getProperty(
167 String.valueOf(ConfigUtils.ConfigKeys
168 .DB_USERNAME_QA)),
169 ConfigUtils.getProperty(String.valueOf(
170 ConfigUtils.ConfigKeys.DB_PASSWORD_QA)), QUERY, evntId);

171

172 if (rs.next()) {
173 String type = rs.getString("type");
174 String source = rs.getString("source");
175 String payload = rs.getString("payload");
176 String received_date = rs.getString("received_date");

177
178 logger.info("Validating the DB payload:");
179

180 JSONObject jsonPayload = new JSONObject(payload);
181 JSONObject data = jsonPayload.getJSONObject("content").
182 getJSONObject("data");
183 String origEvntId = data.getString("origVntId");
184 String appId = jsonPayload.getString("appId");
185 String overwritemsg_t = data.getString("overwritemsg_t");
186 //extract all the fields

187

188 JSONArray onsuccess = jsonPayload.getJSONObject("triggers").
189 getJSONArray("onsuccess");
190 String Dbmsg_t = jsonPayload.getString("msg_t");

191
192 if (origEvntId.equals(evntId)) {
193 assertThat(origAppId).isEqualTo(appIdExp);
195 assertThat(overwritemsg_t).isEqualTo(msg_t); 196 assertThat(origEvntInitDesc).isEqualTo(evntDsc);
197
198 assertThat(originalevntDsc).isEqualTo(evntDsc);
199

200 assertThat(Dbmsg_t).isEqualTo(msg_t);
201 } else {
202 logger.info("Something must be really wrong with this → data");

203 }
204 }
205 dbConn.closeConnection(rs.getStatement().
206 getConnection());
207 }
208 }

5. Producer - Consumer - RESTful APIs - DB end-to-end test steps:
• Set properties for Kafka Producer;
• Generate a valid Avro schema message;
• Message is successfully published on the partition and offset topic Producer;
• Set properties for Kafka Consumer;
• Subscribe to the Consumer topic;
• Consume and validate published message fields by evntId field;
• Make a call to Agr Cst RESTful API;
• Parse the response message;
• Validate and save roleType and mbrGUIDAgrCst values fields;
• Make call to Cst Id RESTful API;
• Parse response message;
• Validate and save mbrGUID value field;
• Connect to the database;
• Database result with type: ”auto” for the message produced is found;
• Validations for the DB columns against message produced fields.

1 package financial.KafkaProducerConsumerRESTfulAPIsDB.producer_ 2 consumer_micros_db;
3

4 import io.restassured.response.Response;
5 import financial.db.DBConnections;
6 import financial.kafka.KafkaConsumerSetup;
7 import financial.kafka.KafkaProducerSetup;
8 import financial.utils.connector.RestAssuredConnector;
9 import financial.utils.general.ConfigUtils;
10 import org.apache.avro.generic.GenericRecord; 11 import org.apache.kafka.clients.consumer.ConsumerRecord; 12 import org.apache.kafka.clients.consumer.ConsumerRecords; 13 import org.apache.kafka.clients.consumer.KafkaConsumer; 14 import org.apache.kafka.clients.producer.KafkaProducer; 15 import org.apache.kafka.clients.producer.ProducerRecord; 16 import org.apache.kafka.clients.producer.RecordMetadata; 17 import org.json.JSONArray;
18 import org.json.JSONObject;
19 import org.junit.Before;
20 import org.junit.Test;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.text.SimpleDateFormat;
26 import java.util.*;
27 import java.util.concurrent.ExecutionException;

28
29 import static org.assertj.core.api.Assertions.assertThat;
30
31 public class ProducerConsumerAgrMsDBValidationsHappyTest {
32
33 protected static final Logger logger = LoggerFactory.getLogger( 34 ProducerConsumerAgrMsDBValidationsHappyTest.class);
35

36 Date now = new Date();
37 SimpleDateFormat date = new SimpleDateFormat(
38 "yyyy-MM-dd HH:mm:ss");
39 String currentDateComplete = date.format(now);
40 UUID generateUUID = UUID.randomUUID();
41 String eventType = "e";
42 String eventSubtype = "ev";
43 String evId = generateUUID.toString();
44 String ev_desc = "Auto";
45 String evntS = "auto";
46 String d_type = "auto";
47 String b_area = "auto";
48 String pfx = "pfx";

49 String o_st = "auto";
50 String m_type = "app";
51 String sys_id = "auto";
52 String tran_id = "auto";
53 String prim_id = "auto";
54 String adm_sys = "auto";
55 String doc = "auto";
56 String GUID = '{' + generateUUID.toString() + '}';
57 String pattern = "mm-dd-yyyy";
58 SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
59 String currentDate = dateFormat.format(new Date());
60 String UNIQUE_GROUP_ID = "automation_events_consumer" + → currentDate;
61 public RestAssuredConnector connector;
62 DBConnections dbConn;
63 private String requestUriAgrCst;
64 private final String agr_key = "99999";
65 private final String agr_key_end = "AUTO";

66

67 public String QUERY = "SELECT * FROM message WHERE type = → \"auto\" AND source = \"source\" AND JSON_EXTRACT(payload, → \"$.triggers.onsucces\") = \"auto\" AND s

68 JSON_EXTRACT(payload, \"$.content.data.origId\")= ?";
69

70 @Before
71 public void setup() {
72 requestUriAgrCst = ConfigUtils.getProperty(
73 ConfigUtils.ConfigKeys.BASE_URL_QA. toString())

74 +
75 ConfigUtils.getProperty(ConfigUtils.
76 ConfigKeys.MIDDLE_URL_AGR_QA.toString())
77 +

78 agr_key + "-" + agr_key_end;
79 connector = new RestAssuredConnector();
80 dbConn = new DBConnections();

81 }
82

83 @Test
84 public void produceMessageConsumerAgrMsDBValidationsTest()
85 throws ExecutionException, InterruptedException, SQLException {

86
87 KafkaProducerSetup kafkaProducerSetup = new → KafkaProducerSetup();
88
89 logger.info("Adding properties to Kafka Producer...");
90

91 Properties propsProducer =
→ kafkaProducerSetup.setProducerProps(ConfigUtils.
92 getProperty(String.valueOf(ConfigUtils.

93 ConfigKeys.BOOTSTREP_SERVER_QA)),
94 ConfigUtils.getProperty(String.valueOf(ConfigUtils.
95 ConfigUtils.getProperty(String.valueOf(ConfigUtils.
96 ConfigKeys.SASL_CONFIG_SYSTEM)),
→ ConfigUtils.getProperty(String.valueOf(ConfigUtils.
97 ConfigKeys.TRUST_STORE_LOCATION)),
→ ConfigUtils.getProperty(String.valueOf(ConfigUtils.
98 ConfigKeys.SCHEMA_REGISTRY)));

99

100 GenericRecord message = KafkaProducerSetup.
101 produceMessage(eventType, eventSubtype, currentDateComplete,
102 evId, ev_desc, evntS, d_type, b_area, pfx, o_st, m_type, → sys_id, doc, GUID);

103
104 logger.info("Preparing/Sending the object to Kafka Producer");
105

106 ProducerRecord<Object, Object> recordProducer =
107 new ProducerRecord<>(ConfigUtils.getProperty(
108 String.valueOf(ConfigUtils.
109 ConfigKeys.TOPIC_NAME_PRODUCER)), null, message);

110
111 KafkaProducer<Object, Object> producer = new → KafkaProducer<>(propsProducer);
112

113 RecordMetadata metadata = producer.send(recordProducer).get();
114 producer.flush();
115 producer.close();

116

117 logger.info("Message sent successfully to topic" + → metadata.topic() +
118 " " + "partition: " + metadata.partition() + " " + "offset: " +
119 metadata.offset());

120
121 KafkaConsumerSetup kafkaConsumerSetup = new → KafkaConsumerSetup();
122

123 logger.info("Adding properties to Kafka Consumer...");
124 Properties propsConsumer =
→ kafkaConsumerSetup.setConsumerProps(ConfigUtils.
125 getProperty(String.valueOf(ConfigUtils.ConfigKeys.
126 BOOTSTREP_SERVER_QA)), UNIQUE_GROUP_ID,
→ ConfigUtils.getProperty(String.valueOf(
127 ConfigUtils.
128 ConfigKeys.AUTH_INFO_SYSTEM)), ConfigUtils.getProperty(String.
129 valueOf(ConfigUtils.ConfigKeys
130 .SASL_CONFIG_SYSTEM)),
131 ConfigUtils.getProperty(String.valueOf(ConfigUtils.
132 ConfigKeys.TRUST_STORE_LOCATION)),
→ ConfigUtils.getProperty(String.valueOf(ConfigUtils.

133 ConfigKeys.SCHEMA_REGISTRY)));
134
135 logger.info("Initializing and subscribing to the topic");
136
137 KafkaConsumer<String, GenericRecord> consumer = 138 new KafkaConsumer<>(propsConsumer);
139

140 consumer.subscribe(Arrays.asList(ConfigUtils.
141 getProperty(String.valueOf(ConfigUtils.ConfigKeys. → TOPIC_SYSTEM_CONSUMER))));

142

143 ConsumerRecords<String, GenericRecord> records =
→ consumer.poll(1000);
144 for (ConsumerRecord<String, GenericRecord> record : records) {
145 GenericRecord avroRecordConsum = record.value();

146
147 logger.info("Current record value: " +
→ record.value().toString());
148
149 String evIdConsumer = avroRecordConsum.get("eventHeader.evId"). 150 toString();
151

152 if (evId.equals(evIdConsumer)) {
153 assertThat(avroRecordConsum.
154 get("eventHeader.eventType").
155 toString()).isEqualTo(eventType);
156 assertThat(avroRecordConsum.get("eventHeader.
157 eventSubtype").toString()).isEqualTo(eventSubtype);
158 assertThat(avroRecordConsum.get("eventHeader.
159 eventDateTime").toString()).
160 assertThat(avroRecordConsum.get("eventHeader.
161 eventGeneratedDateTime").toString()).
162 assertThat(avroRecordConsum.get(
163 "eventHeader.evId").toString()).isEqualTo(evId);
164 assertThat(avroRecordConsum.get(
165 "eventHeader.ev_desc").
166 toString()).isEqualTo(ev_desc);
167 assertThat(avroRecordConsum.get("eventBody.b_area"). → toString()).isEqualTo(b_area);
168 assertThat(avroRecordConsum.get("eventBody.o_st"). → toString()).isEqualTo(o_st);
169 assertThat(avroRecordConsum.get("eventBody.m_type"). → toString()).isEqualTo(m_type);
170 assertThat(avroRecordConsum.get("eventBody.sys_id"). → toString()).isEqualTo(sys_id);
171 assertThat(avroRecordConsum.get("eventBody.adm_sys") → .toString()).isEqualTo(adm_sys);

172 }
173 long offset = record.offset();
174 logger.info("Consumed record at offset: " + offset);
175 }
176 consumer.close();
177

178 logger.info("Validating the Consumer fields against the fields → produced... with success, messages is sent on Consumer → side");

179
180 logger.info("Agr cstr call:");
181

182 Map<String, String> headers =
→ RestAssuredConnector.setHeaders(ConfigUtils.
183 getProperty(ConfigUtils.ConfigKeys.
184 TOKEN_AGR_CSTR.toString()));

185
186 Response agrResponse = connector.getRequest(requestUriAgrCst, → headers);
187
188 logger.info("Extracting cstr, agr key, role type 189 and mbr GUID");
190
191 String responseBody_agrtKey = agrResponse.getBody().asString(); 192 JSONObject jsonResponse = new JSONObject(responseBody_agrKey);
193

194 JSONArray agr = jsonResponse.getJSONArray("agr");
195 JSONObject cstr = agr.getJSONObject(0).getJSONArray("agrcstrs").
196 getJSONObject(0);

197
198 JSONArray agrArray = jsonResponse.getJSONArray("agr"); 199 String agrKeyMS = agrArray.getJSONObject(0).getString("agrKey");
200

201 String roleType = cstr.getString("roleType");
202 String mbrGUIDAgrCstr = cstr.getString("mbrGUID");
203 logger.info(mbrGUIDAgrCstr);

204

205 assertThat(roleType).isEqualTo("OWNR");
206 assertThat(agrKeyMS). isEqualTo(agr_key + agr_key_end);
207 ResultSet rs = dbConn.dbConn(ConfigUtils.getProperty(String.
208 valueOf(ConfigUtils.ConfigKeys.DB_URL_QA)),
→ ConfigUtils.getProperty(
209 String.valueOf(ConfigUtils.
210 ConfigKeys.DB_USERNAME_QA)),ConfigUtils.getProperty(

211
212 String.valueOf(ConfigUtils.
213 ConfigKeys.DB_PASSWORD_QA)), QUERY, evId);
214

215 if (rs.next()) {
216 String type = rs.getString("type");
217 String source = rs.getString("source");

218 String payload = rs.getString("payload"); 219 String received_date = rs.getString("received_date");
220
221 logger.info("Validating the DB payload:");
222

223 JSONObject jsonPayload = new JSONObject(payload);
224 JSONObject data = jsonPayload.getJSONObject("content").
225 getJSONObject("data");

226
227 String origvId = data.getString("origId"); 228 String appId = jsonPayload.getString("appId");
229

230 JSONArray onsuccess = jsonPayload.getJSONObject("triggers").
231 getJSONArray("onsuccess");
232 String DbmsgType = jsonPayload.getString("msgType");

233
234 if (origId.equals(evId)) {
235

236 assertThat(origAppId).isEqualTo("AUTO");
237 assertThat(msgType).isEqualTo("auto");
238 assertThat(origEventInitDesc).
239 isEqualTo(ev_desc);

240
241 assertThat(originalev_desc). 242 isEqualTo(ev_desc);
243
244 assertThat(origEventSrc).isEqualTo("AUTO NOW");
245
246 assertThat(originalEventDateTime). 247 isEqualTo(currentDateComplete);
248

249 assertThat(originalEventInitiator).
250 isEqualTo(applicationId);
251 assertThat(DbmsgType).isEqualTo("auto");
252 } else {
253 logger.info("Something must be really wrong with → this data");

254 }
255 }
256 dbConn.closeConnection(rs.getStatement().
257 getConnection());
258 }
259 }
Case 2: RESTful APIs and PostgreSQL database systems.

1. Data modeling for RESTful API, as we are already familiars with this type of approach we are creating a JSON Object from payload mapping each field with its value to make the a post request.

1 package financial.restfulapis;
2

3 import io.restassured.response.Response;
4 import org.json.JSONArray;
5 import org.json.JSONObject;

6

7 public class RESTfulAPIDataModel {
8 public String createModel(String email, String type, String → guid, int no, long amount, int r_no, String rec_type, → String tr_type, String key, String method, String name){

9
39 } 40 }

10 JSONObject restfulPayload = new JSONObject();
11 restfulPayload.put("transGuid", guid);
12 restfulPayload.put("code", 1);
13 restfulPayload.put("email", email);
14 restfulPayload.put("type", type);
15 restfulPayload.put("cId", guid);
16 restfulPayload.put("no", no);
17 restfulPayload.put("amount", amount);
18 restfulPayload.put("rNo", r_no);
19 restfulPayload.put("trId", guid);
20 restfulPayload.put("recType", rec_type);
21 restfulPayload.put("type", type);
22 restfulPayload.put("key", key);
23 restfulPayload.put("method", method);
24 restfulPayload.put("auth_type", "auto");
25 restfulPayload.put("sys", "auto");
26 restfulPayload.put("date", "2024-02-13 01:06:84532 PM");
27 restfulPayload.put("id", "auto");
28 restfulPayload.put("name", name);
29 return restfulPayload.toString();

30 }

31 public int saveKey(Response responseBody_restful){
32 JSONObject payloadObject = new
→ JSONObject(responseBody_restful.asString());
33 JSONArray msgArray = jsonObject.getJSONArray("messages");
34 JSONObject msgObject = msgArray.getJSONObject(0);
35 String msgDesc = msgObject.getString("msgDesc");
36 String keyString = msgDesc.substring(msgDesc.lastIndexOf(" → " ) + 1);
37 int keyInt = Integer.parseInt(keyString.trim());
38 return keyString;

1. RESTful API test steps:
• Make call to RESTful API having the payload generated in data modeling from RESTfulAPIDataModel class;
• Parse and validate response message.
RESTful API test implementation:
1
2 package financial.RESTfulAPIsDB.micros;
3

4 import io.restassured.response.Response;
5 import financial.db.DBConnections;
6 import financial.restfulapis.RESTfulAPIDataModel;
7 import financial.utils.connector.RestAssuredConnector;
8 import financial.utils.general.ConfigUtils;
9 import org.junit.Before;
10 import org.junit.Test;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;

13

14 import java.util.Map;
15 import java.util.Random;
16 import java.util.UUID;

17
18 import static org.junit.Assert.assertEquals;
19

20 public class POSTRESTfulHappyTest {
21 static Random random = new Random();
22 int randomInt = random.nextInt(1000000) + 1;
23 String KEY = randomInt + "-" + "AUTO";
24 protected static final Logger logger =
→ LoggerFactory.getLogger(POSTRESTfulHappyTest.class);
25 public String EMAIL = "auto@gmail.com";
26 public String TYPE = "AUTO";
27 public String GUID = String.valueOf(UUID.randomUUID());
28 public int NO = random.nextInt(100000) + 1;
29 public long AMOUNT = 888;
30 public int R_NO = random.nextInt(10000) + 1;
31 public String REC_TYPE = "AUTO";
32 public String TR_TYPE = "AUTO";
33 public String METHOD = "AUTO";
34 public String NAME = "Auto";
35 public String requestUri;
36 public String requestBody;

37 RESTfulAPIModel responseMessage;
38 RestAssuredConnector connector;
39 DBConnections dbConn;
40 public String QUERY = "SELECT * FROM table.column
41 where key = ?";

42

43 @Before
44 public void setupData() {
45 RESTfulAPIModel requestMessageBody = new RESTfulAPIModel();
46 dbConn = new DBConnections();
47 requestUri = ConfigUtils.getProperty(ConfigUtils.
48 ConfigKeys.BASE_URL_QA.toString())

49 +
50 ConfigUtils.getProperty(ConfigUtils.
51 ConfigKeys.MIDDLE_URL_QA.toString())
52 +

53 ConfigUtils.getProperty(ConfigUtils.
54 ConfigKeys.END_URL_QA.toString());
55 requestBody = requestMessageBody.
56 createModel(EMAIL, TYPE, GUID, NO, AMOUNT, R_NO, REC_TYPE, → TR_TYPE, KEY, METHOD, NAME);
57 responseMessage = new RESTfulAPIsModel();
58 connector = new RestAssuredConnector();

59 }
60 }
2. RESTful API - DB integration test steps:

• Make call to RESTful API having the payload generated in data modeling from
RESTfulAPIDataModel class;

• Parse and validate response message and store the key;
• Connect to MySQL database;
• Query the records and filter by key stored;
• Validate the fields.

RESTful API - DB integration test implementation:
1 package financial.RESTfulAPIsDB.micro_db;
2

3 import io.restassured.response.Response;
4 import financial.db.DBConnections;
5 import financial.restfulapis.RESTfulAPIsDBPaymentModel;
6 import financial.utils.general.ConfigUtils;

7 import financial.utils.connector.RestAssuredConnector;
8 import org.junit.Before;
9 import org.junit.Test;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import java.sql.ResultSet;
13 import java.sql.SQLException;
14 import java.util.Map;
15 import java.util.Random;
16 import java.util.UUID;

17
18 import static org.junit.Assert.assertEquals;
19
20 public class RESTfulAPIsDBValidationsHappyTest {
21

22 protected static final Logger logger =
→ LoggerFactory.getLogger(RESTfulAPIsDBValidationsHappyTest.
23 class);
24 static Random random = new Random();
25 int randomInt = random.nextInt(1000000) + 1;
26 String KEY = randomInt + "-" + "AUTO";
27 public String EMAIL = "auto@gmail.com";
28 public String TYPE = "AUTO";
29 public String GUID = String.valueOf(UUID.randomUUID());
30 public int NO = random.nextInt(100000) + 1;
31 public long AMOUNT = 888;
32 public int R_NO = random.nextInt(10000) + 1;
33 public String REC_TYPE = "AUTO";
34 public String TR_TYPE = "AUTO";
35 public String METHOD = "AUTO";
36 public String NAME = "Auto";
37 public String requestUri;
38 public String requestBody;
39 RESTfulAPIsDBPaymentModel responseMessage;
40 RestAssuredConnector connector;
41 DBConnections dbConn;
42 public String QUERY = "SELECT * FROM table where key = ?";

43

44 @Before
45 public void setupData() {
46 RESTfulAPIDataModel requestMessageBody = new → RESTfulAPIDataModel();
47 dbConn = new DBConnections();
48 requestUri = ConfigUtils.getProperty(ConfigUtils.
49 ConfigKeys.BASE_URL_QA.toString())

50 +
51 ConfigUtils.getProperty(ConfigUtils.
52 ConfigKeys.MIDDLE_URL_QA.toString())
53 +

54 ConfigUtils.getProperty(ConfigUtils.
55 ConfigKeys.END_URL_QA.toString());
56 requestBody = requestMessageBody.
57 createModel(EMAIL, TYPE, GUID, NO, AMOUNT, R_NO, REC_TYPE, → TR_TYPE, KEY, METHOD, NAME);
58 responseMessage = new RESTfulAPIDataModel();
59 connector = new RestAssuredConnector();

60 }
61

62 @Test
63 public void postRESTfulAPIDBvalidationsHappyTest() throws → SQLException {
64 RestAssuredConnector connector = new RestAssuredConnector();
65 Map<String, String> headers =
→ RestAssuredConnector.setHeaders(ConfigUtils.
66 getProperty(ConfigUtils.ConfigKeys.
67 TOKEN_RESTfulAPIsDB.
68 toString()));
69 Response RESTfulAPIsDBResponse =
→ connector.postRequest(requestUri, headers, requestBody);

70

71 logger.info("RESTful API URL:" + " " + requestUri);
72 logger.info("requestBody:" + " " + requestBody);
73 logger.info("Response:" + " " +
→ RESTfulAPIsDBResponse.getBody().asString());

74

75 assertEquals(RESTfulAPIsDBResponse.getStatusCode(), 200);
76 int key = responseMessage.savePaymentKey(
77 RESTfulAPIsDBResponse);
78 ResultSet rs = dbConn.dbConn(ConfigUtils.
79 getProperty(ConfigUtils.
80 ConfigKeys.RESTfulAPIsDB_DB_URL_QA.name()),
→ ConfigUtils.getProperty(
81 String.valueOf(ConfigUtils.
82 ConfigKeys.RESTfulAPIsDB_DB_USERNAME_QA)),
→ ConfigUtils.getProperty(
83 String.valueOf(ConfigUtils.
84 ConfigKeys.DB_PASSWORD_QA)), QUERY, key);

85

86 if (rs.next()) {
87 int keyDB = rs.getInt("key");
88 String noDB = rs.getString("no");
89 String rNDBo = rs.getString("r_no");
90 long amountDB = rs.getLong("amount");
91 String emailDB = rs.getString("email");
92 String cIdDB = rs.getString("c_id");
93 int confDB = rs.getInt("conf");
94 String guidDB = rs.getString("guid");
95 String dateDB = rs.getString("date");

96 String nameDB = rs.getString("name");
97 if (keyDB == (key)) {
98 assertEquals(amountDB, amount);
99 logger.info("DB Validations for key: " + keyDB + " → " + "amount: " + amountDB + "email: " + emailDB → + "name: " + namme + "GUID: " + guidDB + "passed → successfully");
100 } else {
101 logger.info("No key was found into the db");

102 }
103 dbConn.closeConnection(rs.getStatement().
104 getConnection());
105 }
106 }
107 }

What we did not cover until now is pom file. As we are already familiarized with the scope of this file, classes and resources from pre-built libraries imports into the project for: Kafka Producer, Kafka Consumer, Database, RESTful APIs (we covered it in Chapter 4: ”Installing necessary tools and libraries”) are recognized and can be properly used.

Kafka maven dependencies: Kafka Client, Kafka Serializer Avro Schema, Kafka Confluent Cloud required in our project to run successfully:

1 <!- All necessary Kafka dependencies -->
2 <dependency>
3 <groupId>org.apache.kafka</groupId>
4 <artifactId>kafka-clients</artifactId>
5 <version>${kafka.clients.version}</version>
6 </dependency>

7

8 <!- https://mvnrepository.com/artifact/org.apache. 9 kafka/kafka-server-common -->
10 <dependency>
11 <groupId>org.apache.kafka</groupId>
12 <artifactId>kafka-server-common</artifactId> 13 <version>${kafka-server-common.version} 14 </version>
15 <scope>runtime</scope>
16 </dependency>

17

18 <dependency>
19 <groupId>org.apache.avro</groupId>
20 <artifactId>avro</artifactId>
21 <version>${avro.version}</version>

22 </dependency>
23

24 <!- https://mvnrepository.com/artifact/io.confluent
25 /kafka-avro-serializer -->
26 <dependency>
27 <groupId>io.confluent</groupId>
28 <artifactId>kafka-avro-serializer
29 /artifactId>
30 <version>${kafka-avro-serializer.version}</version>
31 </dependency>

32
33

34 <dependency>
35 <groupId>io.confluent</groupId>
36 <artifactId>kafka-schema-registry-client</artifactId>
37 <version>${kafka-schema-registry-client.version}
38 </version>
39 </dependency>

40

41 <!- https://mvnrepository.com/artifact/io.confluent
42 /kafka-schema-serializer -->
43 <dependency>
44 <groupId>io.confluent</groupId>
45 <artifactId>kafka-schema-serializer</artifactId>
46 <version>${kafka-schema-serializer.version}
47 </version>
48 </dependency>

49

50 <!- https://mvnrepository.com/artifact/org.apache.
51 kafka/kafka-server-common -->
52 <dependency>
53 <groupId>org.apache.kafka</groupId>
54 <artifactId>kafka-server-common</artifactId>
55 <version>${kafka-server-common.version}
56 </version>
57 <scope>runtime</scope>
58 </dependency>

59

60 <!- https://mvnrepository.com/artifact/org.apache.
61 kafka/kafka-clients -->
62 <dependency>
63 <groupId>org.apache.kafka</groupId>
64 <artifactId>kafka-clients</artifactId>
65 <version>${kafka-clients.version}</version>
66 </dependency>

67

68 <!- https://mvnrepository.com/artifact/io.confluent
69 /kafka-json-serializer -->
70 <dependency>

71 <groupId>io.confluent</groupId>
72 <artifactId>kafka-json-serializer</artifactId>
73 <version>${kafka-json-serializer.version}
74 </version>
75 </dependency>

Database Maven dependencies: MySQL, PostgreSQL required in our project to run successfully:

1 <!- https://mvnrepository.com/artifact/com.mysql
2 /mysql-connector-j -->
3 <dependency>
4 <groupId>com.mysql</groupId>
5 <artifactId>mysql-connector-j</artifactId>
6 <version>${mysql-connector-j.version}</version>
7 </dependency>

8

9 <!- https://mvnrepository.com/artifact/org.postgresql/ 10 postgresql -->
11 <dependency>
12 <groupId>org.postgresql</groupId>

→ <artifactId>postgresql</artifactId>
13 <version>${postgresql.version}</version>
14 </dependency>

runners execute tests defined with JUnit test classes, by iterating through all tests defined within a test class, executing them sequentially. JUnit tests are grouped by test suites and projects for a better organization and execution.

Figure 20: runners
runners test results:
Figure 21: RESTful APIs Kafka DB Runner Test Results
compile phase for the project reflecting that the build was successful.
Figure 22: maven compile phase BUILD SUCCESS
test phase running all the JUnit tests:
Figure 23: maven test phase
BUILD FAILURE in test phase having test failures:
Figure 24: BUILD FAILURE TEST
verify phase:
Figure 25: maven verify phase
maven surefire report test results txt files with logs for each test are stored and tests can be investigated when they are resulting in failures.
Figure 26: maven surefire test results report
◦ Lessons learned and best practices

It is very diﬀicult to establish exactly what are the best practices, due to many factors: team capacity and skill, project technologies, project complexity, deadlines, project capabilities, budgets.

After all my work experience and the projects I have worked on, I could set some important key aspects to have a successful automation testing solution in place:
• simplicity of the framework to meet the project’s requirements;
• design tests in the scope of functional, integration, end-to-end and regression testing;
• design patterns: Factory, Builder, Page Object Model, Singleton, Template, Data-Driven-Test, Page Object Model (POM);
• testing frameworks: JUnit or TestNG;
• organized tests using xml file test suites for TestNG and runners for JUnit tests;
• autonomously implementation tests by creating new test data and not rely on existent one;
• use assertions to validate data (actual data = expected data);
• focus on test coverage in implementing the important use cases and the edge case;
• run project phases to make sure that builds are successful;
• logging information in our tests for more detailed information about the tests’ progress, debugging and troubleshooting purposes;
• git flow branches practices;
• naming conventions: classes, methods relevant name, test name should match branch name and JIRA id;

• config. files to store urls, test data, configurations;
• development, staging and production environments set up;
• build control tool such as Apache Maven;
• integrate automation testing into CI/CD;

• configure jobs for smoke, end-to-end and regression testing to run periodically, ideally smoke after each development build, end-to-end daily. Part 4: Overcoming Common Challenges

• Chapter 7: Addressing Common Issues in Automation Testing

1. ◦ Tips for troubleshooting and debugging.
Automation testing solutions can sometimes be challenging. Some challenges encountered along the way:
• product software must be stable and functional in order to have automation implemented;

• complexity and technologies could create some challenges in automating it;
• high test failures percentage due to timing issue, unstable environments;
• development frequent changes in the code that impact the testing solution;

• test coverage low percentage in automation to identify important use cases not being covered could result in framework ineﬀiciency;
• false positive and negative test results;

Solutions :
• stable environments set up;
• implement clear, autonomous, reliable tests;
• update and maintain tests;
• increase test coverage percentage;
• implement tests to cover happy and unhappy paths;

• implement tests in scope of functional, integration, end-to-end and regression testing by generating new test data.
Tips for debugging and troubleshooting automated tests.

Is a challenging task, depending on many factors from complexity to system configuration. It might be a complex custom situation and might depend on a specific context, and many things could go wrong: incorrect configurations, services down, database, and network latency are just a few issues that we cannot control in our tests and are diﬀicult to debug. It is better to have a good understanding of the system architecture, setup, and configurations to identify the root cause easier.

• run a test locally to identify the possible root cause;
• logs and errors could be very useful to understand the progress of test and the point didn’t work;

• data used is also very important if is the correct one;
• debug code by setting breakpoints and inspecting the variables;

• use assertions to do validations and check the actual data meet the expected data;
• check reports with test results;
• check documentation, dedicated communities for automation testing.

Projects could be found in a more detailed version on github here:
• navigate-automation-seas
• singularity
• sustainability

Trying to expand your technical skills and see how the most used technologies work together ? Perhaps I have the right book for you: ”Python in Action: Building Automation Projects with Kafka Messaging and REST APIs” just launched on Amazon.
Available now on Amazon Kindle.