Context. It all depends on context!
I.?????????????Intro
I have often been asking if I know REST API and if I can test API (automation/manual). Do I have enough knowledge to write automated tests?
I was stunned by such questions from the people reading my resume. At first, I tried to do something with it: to say what I could answer questions about the difference between PUT and PATCH, what status codes exist, and what category they belong to, but alas - I think I could not prove these people even the fact that I am a human being, which means I can think and learn.
It's not about the resume; the point is that the employer wants to confirm all the skills you have mentioned in your resume. Context is very important!
I decided that arguing was ineffective, and I thought, why not write a small demo showing my programming and testing skills?
GitHub project with source code: https://github.com/YuriiChukhrai/http-status-codes-demo.
What is the idea behind the demo?
Write a REST API that will only do two things:
1) display help information about the HTTP code of interest (JSON/XML);
2) simulate a response with the desired status code - for example, you sent a request about code 100 and received an HTTP response of code 100 and its description.
And we will cover all this with tests: unit and integration. TestNG will run all these tests, and the results will be presented in the generated Allure report (with attached request/response context).
It seems simple. And so we will briefly describe how all this will work for us. We will need Spring Boot + Maven + TestNG + Rest-Assured.
Pic#1. The handling of requests
Pic#2. The design of SpringBoot application for [HTTP codes demo]
Incoming requests will be processed in REST controllers (annotation @RestController):
These controllers will call the service class [HttpCodeServiceImpl] (@Service annotation), which will process them according to business logic. If the service class needs to interact with the database, it can do this through the [HttpCodeCustomRepositoryImpl] class. This is additional customization to the standard JpaRepository interface.
II.?????????????Small context
Before diving into the details, let's go over the additional tools we'll use. After creating the database, we need to fill it with some information.
2.1 Flyway - SQL migration
Flyway is an open-source tool that helps you implement automated and version-based database migrations. It allows you to define the required update operations in an SQL script or as Java code. Flyway detects the required update operations and executes them.
So, you don’t need to know which SQL update statements need to be performed to update your current database. You and your co-workers just define the update operations to migrate the database from one version to the next. And Flyway detects the current version and performs the necessary update operations to get the database to the latest version. Resource: https://thorben-janssen.com/flyway-getting-started/
Flyway integration (Maven/pom.xml)
<dependency
??? <groupId>org.flywaydb</groupId>
??? <artifactId>flyway-core</artifactId>
??? <version>${flyway.version}</version>
</dependency>>
Here is the configuration:
1)???flyway.enabled=true - we enable this support in the Spring project (after adding the dependency to pom.xml);
2)???flyway.locations=classpath:resources/db/migration - specify the location of files for migration.
?2.1.1 Flyway migration
The file name follows Flyway’s default naming convention: V<VERSION>__<DESCRIPTION>.sql. This file contains the SQL statements for database version 1, and Flyway will store it with the description “create_database” in the SCHEMA_VERSION table.
?
This is what the files for SQL migration look like:
?????./src/main/resources/db/migration/V1_1__create_http_codes_table.sql - contains instruction for creation of schemas;
?????./src/main/resources/db/migration/V2_1__insert_http_codes_table.sql - fill the data in the tables.
2.2 Database (H2)
We will use H2 like a database (https://www.h2database.com/html/quickstart.html). This DB uses the RAM for storage - this is great for our purposes. It can be easily changed to any other DB by changing the config in settings:
?????spring.jpa.database-platform
?????spring.datasource.url
?????spring.datasource.driverClassName
?????spring.datasource.username
?????spring.datasource.password
?????spring.h2.console.enabled
You can check/view the database when the project is running by following the link ( https://localhost:{port}/h2-console )
Pic#3. DB - Access to the H2 console in a browser
Pic#4. Content of the DB table [HTTP_CODE] in H2
2.3 Lombok
Project Lombok is a mature library that reduces boilerplate code with easy-to-use annotations. The library works well with popular IDEs and provides a utility to “de-lombok” your code by reverting—that is, adding back all the boilerplate that was removed when the annotations were added. It can be useful for making your code more concise, reducing the chance of bugs, and speeding up development time. The size of the Java classes reduced significantly – just add an annotation for your Getters or Setters. Or we can use the Java record introduced in the JAVA 16.
Pic#5. HttpCode entity/POJO/model representation with JPA and Lombok annotations
III.?????????????More of Context.
For convenience, all examples will be given in MediaType.APPLICATION_JSON, but the API supports XML too (don't forget to specify Accept: application/xml in HTTP Headers). Let's briefly describe each of the controllers.
3.1 InfoController
a) method: Info getGeneralInformation() – has no input parameters and returns the fields with the description of the given project (Info – response entity/POJO/model implemented by Java recod).
Request:
GET {uri}/api/v1/info
Response (200 - OK):
{
"app_name": "HTTP status codes. Demo",
? "app_version": "0.0.1",
? "http_codes_size": 66,
? "dev": "Yurii Chukhrai",
? "e_mail": "[email protected]",
? "git_hub_url": "https://github.com/YuriiChukhrai",
? "linkedin_url": "https://www.dhirubhai.net/in/yurii-c-b55aa6174",
? "swagger_url": "https://server:port/context-path/swagger-ui.html",
? "openapi_url": "https://server:port/context-path/v3/api-docs",
? "openapi_yaml_url": "https://server:port/context-path/v3/api-docs.yaml"
}
Pic#6. InfoController and Java Records for entity/POJO/model
3.2 HttpCodeControllerImpl
a) method: HttpCode getHttpCodeByCode(Integer code) - receives the HTTP code of interest as input and returns the [HttpCode] entity/POJO/model containing reference information about it. We will use the CRUD (Create Read Update Delete) principle. I placed all the codes that I could find here and here in the database (V2_1__insert_http_codes_table.sql), so let's start by looking at GET (read). For example, I want to get information about status code 101:
?
Request:
GET {url}/api/v1/http/code/info?code=101
Response (200 - OK):
{?????
"id" : 2,
???"code" : 101,
???"category" : "1** Informational",
???"reason_phrase" : "Switching Protocols",
???"definition" : "This code is sent in response to an Upgrade request header from the client and indicates the protocol the server is switching to."
}
b) method: HttpCode getHttpCodeById(Long id) - receives the ID of the [HttpCode] model in the DB as input and returns the [HttpCode] entity/POJO containing reference information about it.
Request:
GET {url}/api/v1/http/code/3
Response (200 - OK):
{
????? "id" : 3,
????? "code" : 102,
????? "category" : "1** Informational",
????? "reason_phrase" : "Processing",
????? "definition" : "This code indicates that the server has received and is processing the request, but no response is available yet."
}
c) method: List<HttpCode> getAllHttpCodes() - does not receive any input parameters - returns a collection of all available status codes.
Request:
GET {url}/api/v1/http/code/info/all
Response (200 - OK):
[
????? {
????????????????? "id" : 1,
????????????????? "code" : 100,
????????????????? "category" : "1** Informational",
????????????????? "reason_phrase" : "Continue",
????????????????? "definition":"This interim response indicates that the client should continue the request or ignore the response if the request is already finished."
????? },
????? {??????????
????????????????? "id" : 2,
????????????????? "code" : 101,
????????????????? "category" : "1** Informational",
????????????????? "reason_phrase" : "Switching Protocols",
????????????????? "definition" : "This code is sent in response to an Upgrade request header from the client and indicates the protocol the server is switching to."
????? }
????? ....
]
The remaining methods will not be difficult to understand by their names:
? HttpCode saveHttpCode(HttpCode newHttpCode) - create a new object [HttpCode] in DB
? HttpCode putHttpCode(HttpCode newHttpCode, Long id) - update a previously created object in the database.
? void deleteHttpCode(Long id) - deleting an object from their database.
3.3 ExampleResponseController
a) method: ResponseEntity<HttpCode> getResponseEntityById(Integer code) - получает статус код, ответ которого надо симулировать, тело ответа - справочная информация о статус коде.
Request:
领英推荐
GET /api/v1/http/code/example/{code}
Response (XXX - xxx: status code depends on input).
Pic#7.a (JSON) Response in Postman - ExampleResponseController#getResponseEntityById()
Pic#7.b (XML) Response of ExampleResponseController#getResponseEntityById()
3.4 SpringDoc / OpenAPI / Swagger UI
3.4.1 Spring Doc / Open API
SpringDoc — a tool that simplifies the generation and maintenance of API docs based on the OpenAPI 3 specification for Spring Boot applications. SpringDoc-OpenApi works by examining an application at runtime to infer API semantics based on Spring configurations, class structure, and various annotations. Automatically generates documentation in JSON/YAML and HTML format APIs. This documentation can be completed by comments using Swagger-API annotations.
Pic#8. Spring Boot configuration Bean for OpenAPI documentation
The ability of APIs to describe their own structure is the root of all awesomeness in OpenAPI. Once written, an OpenAPI specification and Swagger tools can drive your API development further in various ways.
The springdoc-openapi dependency already includes Swagger UI, so we're all set here.
We can simply access the API documentation at:
URL (JSON): https://localhost:7777/v3/api-docs
Pic#9. OpenAPI documentation JSON (it’s how Mozilla parses JSON by default)
Documentation can be available in YAML format as well, on the following path: /v3/api-docs.yaml
URL (YAML): https://localhost:7777/v3/api-docs.yaml
Pic#10. OpenAPI documentation YAML
3.4.2 Swagger UI
Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document, and consume REST APIs. Swagger UI – renders OpenAPI specs as interactive API documentation. Use Swagger UI to generate interactive API documentation that lets your users try out the API calls directly in the browser.
URL: https://localhost:7777/swagger-ui/index.html
Pic#11. Swagger UI. Endpoint information
Pic#12. Swagger UI. Schemas descriptions
IV. Test Coverage
4.1 Integration (E2E). Rest-Assured
The tests are based on the Rest-Assured library. These tests represent E2E and can be moved to a separate repository and be part regression suite. As you can see, we start our Spring Boot application in the method [beforeSuite()]. REST-assured was designed to simplify the testing and validation of REST APIs and is highly influenced by testing techniques used in dynamic languages such as Ruby and Groovy.
Location: core.yc.qa.test.e2e.InfoControllerTest
Pic#13. Spring Boot server + Rest-Assured
4.2 Integration (MockMVC)
These integration tests are implemented using native Spring Boot tools like: [WebApplicationContext and MockMvc] by using annotation for auto wire. The test was implemented for ExampleResponseController endpoints. It’s you use the context, including the up-and-running DB and MockMVC, to mock server requests/responses. MockMVC class is part of the Spring MVC test framework, which helps in testing the controllers explicitly starting a Servlet container. Tests with MockMvc lie somewhere between unit and integration tests.
Location: core.yc.qa.test.integration.ExampleResponseControllerTest
Pic#14. Spring Boot Test. MVC - ExampleResponseControllerTest
4.3 Integration (Mockito + MockMVC)
These integration tests are implemented using native Spring Boot tools like: [WebApplicationContext and MockMvc] by using annotation for auto wire. But we are not using real DB for that; instead of that, I will use the Mockito framework to mock all requests related to the [httpCodeRepository] (class – responsible for working with the DB) and inject that behavior into [httpCodeService] class. Mockito is a mocking framework used for effective unit testing of JAVA applications. Mockito is used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in unit testing.
Location: core.yc.qa.test.integration.mock.InfoControllerTest
Pic#15. Spring Boot Test. Mockito + MockMVC - InfoControllerTest
4.4 Integration (Mockito only)
In this suite, implemented examples of the pure Mockito tests without any of the SpringBootTest/MockMvc injections – we will use the Mockito listener [MockitoTestNGListener.class] for that. DB - will be mocked - [httpCodeRepository] (class – responsible to work with the DB) and [httpCodeService] class.
Location: core.yc.qa.test.integration.mock.HttpCodeServiceImplTest
Pic#16. Mockito. HttpCodeServiceImplTest
4.5 Integration (Rest-Assured + WireMock)
These tests are mostly like the E2E, implemented by using the Rest-Assured test framework. Instead of bringing up the real server, I decided to provide examples with WireMock library.
WireMock is an HTTP mock server. At its core, it is a web server that can be primed to serve canned responses to requests (stubbing) and that captures incoming requests so that they can be checked later (verification).
This approach will allow you to start to develop your test automation based on the specification long before the dev will complete their part – be more proactive ?.
Location:
a)???core.yc.qa.test.e2e.mock.HttpStatusCodeTest
b)???core.yc.qa.test.e2e.mock.PersonTest
Pic#17. Rest-Assured + WireMock
4.6 Allure report
I love Allure. It’s open source; it’s beautiful; It has nice features [Steps; Attachments; Links and Tms] – What also do you need for good report and triage?
Pic#18. Allure report with all test suites and attachments (including the MockMvc requests)
References
https://github.com/allure-framework
https://mermaid-js.github.io/mermaid
https://www.restapitutorial.com/httpstatuscodes.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
https://spring.io/guides/tutorials/rest
https://www.baeldung.com/spring-boot-h2-database
https://www.baeldung.com/spring-response-status
https://howtodoinjava.com/spring-boot2/h2-database-example
https://www.codegrepper.com/code-examples/whatever/responseentity+with+status+code+and+message
https://stackabuse.com/how-to-return-http-status-codes-in-a-spring-boot-application
https://www.baeldung.com/spring-boot-testing
https://www.baeldung.com/spring-test-pyramid-practical-example
https://www.baeldung.com/spring-mock-mvc-rest-assured
https://allaroundjava.com/unit-testing-spring-rest-controllers-mockmvc/
https://mkyong.com/spring-boot/spring-rest-validation-example/
https://www.freecodecamp.org/news/unit-testing-services-endpoints-and-repositories-in-spring-boot-4b7d9dc2b772/
https://howtodoinjava.com/spring-boot2/testing/spring-boot-mockito-junit-example/
https://blog.devgenius.io/spring-boot-deep-dive-on-unit-testing-92bbdf549594
https://reflectoring.io/unit-testing-spring-boot/
https://www.tutorialspoint.com/spring_boot_h2/spring_boot_h2_unit_test_service.htm
https://medium.com/backend-habit/integrate-junit-and-mockito-unit-testing-for-service-layer-a0a5a811c58a
https://projectlombok.org/
https://springdoc.org/#Introduction
https://swagger.io/docs/specification/about/