Zerocode automated testing framework for a REST API project concept is getting seen via this tutorial by taking a sample spring boot maven project. Let us see the dependencies for Zerocode :
<dependency> <groupId>org.jsmart</groupId> <artifactId>zerocode-tdd</artifactId> <version>1.3.27</version> <scope>test</scope> </dependency>
Zerocode framework supports the following
- REST
- SOAP
- Security
- Load/Stress
- Database
- Apache Kafka
- GraphQL
In this tutorial let us see REST API testing.
Example Project
Project Structure:
Â
pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> Â Â Â Â xsi:schemaLocation="http://maven.apache.org/POM/4.0.0Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â http://maven.apache.org/xsd/maven-4.0.0.xsd"> Â Â Â Â Â Â Â Â < modelVersion >4.0.0</ modelVersion > Â Â Â Â < groupId >com.gfg.zerocode</ groupId > Â Â Â Â < artifactId >zerocode</ artifactId > Â Â Â Â < packaging >jar</ packaging > Â Â Â Â < version >1.0-SNAPSHOT</ version > Â Â Â Â Â Â Â Â < dependencies > Â Â Â Â Â Â Â Â < dependency > Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.springframework.boot</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >spring-boot-starter-web</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â < version >${spring.boot.version}</ version > Â Â Â Â Â Â Â Â </ dependency > Â Â Â Â Â Â Â Â < dependency > Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.springframework.boot</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >spring-boot-starter-test</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â < version >${spring.boot.version}</ version > Â Â Â Â Â Â Â Â Â Â Â Â < scope >test</ scope > Â Â Â Â Â Â Â Â </ dependency > Â Â Â Â Â Â Â Â < dependency > Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.jsmart</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >zerocode-tdd</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â < version >${zerocode-tdd.version}</ version > Â Â Â Â Â Â Â Â Â Â Â Â < scope >test</ scope > Â Â Â Â Â Â Â Â </ dependency > Â Â Â Â </ dependencies > Â Â Â Â Â Â < build > Â Â Â Â Â Â Â Â < plugins > Â Â Â Â Â Â Â Â Â Â Â Â < plugin > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.springframework.boot</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >spring-boot-maven-plugin</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < profiles > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < profile >it</ profile > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ profiles > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < executions > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < id >pre-integration-test</ id > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goal >start</ goal > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < skip >${skip.it}</ skip > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < id >post-integration-test</ id > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goal >stop</ goal > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < skip >${skip.it}</ skip > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ executions > Â Â Â Â Â Â Â Â Â Â Â Â </ plugin > Â Â Â Â Â Â Â Â Â Â Â Â < plugin > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.apache.maven.plugins</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >maven-failsafe-plugin</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < version >${maven-failsafe-plugin.version}</ version > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < skip >${skip.it}</ skip > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ configuration > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < dependencies > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < dependency > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < groupId >org.apache.maven.surefire</ groupId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < artifactId >surefire-junit47</ artifactId > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < version >${surefire-junit47.version}</ version > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ dependency > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ dependencies > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < executions > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goal >integration-test</ goal > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â < goal >verify</ goal > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ goals > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ execution > Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â </ executions > Â Â Â Â Â Â Â Â Â Â Â Â </ plugin > Â Â Â Â Â Â Â Â </ plugins > Â Â Â Â </ build > Â Â Â Â Â Â < properties > Â Â Â Â Â Â Â Â < maven-failsafe-plugin.version >3.0.0-M5</ maven-failsafe-plugin.version > Â Â Â Â Â Â Â Â < surefire-junit47.version >3.0.0-M5</ surefire-junit47.version > Â Â Â Â Â Â Â Â < maven.compiler.source >8</ maven.compiler.source > Â Â Â Â Â Â Â Â < maven.compiler.target >8</ maven.compiler.target > Â Â Â Â Â Â Â Â < spring.boot.version >2.4.2</ spring.boot.version > Â Â Â Â Â Â Â Â < skip.it >true</ skip.it > Â Â Â Â Â Â Â Â < zerocode-tdd.version >1.3.27</ zerocode-tdd.version > Â Â Â Â </ properties > Â Â </ project > |
To test REST API, let us create a sample spring boot application. Let us create the model class first
GeekUser.java
Java
public class GeekUser { Â Â Â Â Â Â Â Â private String id; Â Â Â Â private String firstName; Â Â Â Â private String lastName; Â Â Â Â private String departmentName; Â Â Â Â Â Â Â Â public String getDepartmentName() { Â Â Â Â Â Â Â Â return departmentName; Â Â Â Â } Â Â Â Â Â Â public void setDepartmentName(String departmentName) { Â Â Â Â Â Â Â Â this .departmentName = departmentName; Â Â Â Â } Â Â Â Â Â Â public float getSalary() { Â Â Â Â Â Â Â Â return salary; Â Â Â Â } Â Â Â Â Â Â public void setSalary( float salary) { Â Â Â Â Â Â Â Â this .salary = salary; Â Â Â Â } Â Â Â Â Â Â private float salary; Â Â Â Â Â Â public String getId() { Â Â Â Â Â Â Â Â return id; Â Â Â Â } Â Â Â Â Â Â public void setId(String id) { Â Â Â Â Â Â Â Â this .id = id; Â Â Â Â } Â Â Â Â Â Â public String getFirstName() { Â Â Â Â Â Â Â Â return firstName; Â Â Â Â } Â Â Â Â Â Â public void setFirstName(String firstName) { Â Â Â Â Â Â Â Â this .firstName = firstName; Â Â Â Â } Â Â Â Â Â Â public String getLastName() { Â Â Â Â Â Â Â Â return lastName; Â Â Â Â } Â Â Â Â Â Â public void setLastName(String lastName) { Â Â Â Â Â Â Â Â this .lastName = lastName; Â Â Â Â } } |
GeekUserZerocodeApplication.java
Java
import java.util.ArrayList; import java.util.List; import java.util.UUID;   import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*;   @SpringBootApplication @RestController @RequestMapping ( "/api/users" ) public class GeekUserZerocodeApplication {     private List<GeekUser> users = new ArrayList<>();       public static void main(String[] args) {         SpringApplication.run(GeekUserZerocodeApplication. class , args);     }       @PostMapping     public ResponseEntity create( @RequestBody GeekUser user) {         if (!StringUtils.hasText(user.getFirstName())) {             return new ResponseEntity( "firstName can't be empty!" , HttpStatus.BAD_REQUEST);         }         if (!StringUtils.hasText(user.getLastName())) {             return new ResponseEntity( "lastName can't be empty!" , HttpStatus.BAD_REQUEST);         }         if (!StringUtils.hasText(user.getDepartmentName())) {             return new ResponseEntity( "DeparmentName can't be empty!" , HttpStatus.BAD_REQUEST);         }         if (user.getSalary() < 0 ) {             return new ResponseEntity( "Salary is not valid!" , HttpStatus.BAD_REQUEST);         }         user.setId(UUID.randomUUID()             .toString());         users.add(user);         return new ResponseEntity(user, HttpStatus.CREATED);     }   } |
We have to write a scenario to test the same
{ "scenarioName": "geek test user creation endpoint", "steps": [ // Array of JSON objects, as much we want we can store { "name": "geek_test_successful_creation", "url": "/api/users", // Relative URL "method": "POST", "request": { "body": { // We have to specify whole bean class parameter and its values "firstName": "Rachel", "lastName": "Green", "departmentName":"IT", "salary":100000.0 } }, "verify": { // expected part containing status and body "status": 201, // status code for a given HTTP call "body": { // Resultant body from the call "id": "$NOT.NULL", "firstName": "Rachel", "lastName": "Green", "departmentName":"IT", "salary":100000.0 } } } }
The above one is a success call that creates a user. Similarly, we can do for validation part as well
{ "name": "test_firstname_validation", "url": "/api/users", "method": "POST", "request": { "body": { "firstName": "", "lastName": "Bing", "departmentName":"IT", "salary":100000.0 } }, "assertions": { "status": 400, "rawBody": "firstName can't be empty!" } }, { "name": "test_lastname_validation", "url": "/api/users", "method": "POST", "request": { "body": { "firstName": "Monica", "lastName": "", "departmentName":"Chef", "salary":100000.0 } }, "assertions": { "status": 400, "rawBody": "lastName can't be empty!" } }, { "name": "test_departmentname_validation", "url": "/api/users", "method": "POST", "request": { "body": { "firstName": "Phoebe", "lastName": "Buffe", "departmentName":"", "salary":50000.0 } }, "assertions": { "status": 400, "rawBody": "DeparmentName can't be empty!" } }
We can combine everything together and keep it under the test/resources folder. Basically, it is a JSON file and combines all the entries that are getting tested. Now coming to the test file part
- @RunWith(ZeroCodeUnitRunner.class) – ZeroCodeUnitRunner, as it is responsible for it
- @TargetEnv – Provide the property file that is required for running the scenario
GeekUserEndpointIT.java
Java
import org.jsmart.zerocode.core.domain.Scenario; import org.jsmart.zerocode.core.domain.TargetEnv; import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner; import org.junit.Test; import org.junit.runner.RunWith;   // Here in RunWith it has been specified // as ZeroCodeUnitRunner, as it is responsible @RunWith (ZeroCodeUnitRunner. class ) // @TargetEnv – this points to the property file // that will be used when our scenario runs @TargetEnv ( "rest_api.properties" ) public class GeekUserEndpointIT {       @Test     @Scenario ( "rest/geek_user_create_test.json" )     public void test_geekuser_creation_endpoint() {     }   } |
rest_api.properties
web.application.endpoint.host=http://localhost # In case of changes they need to be modified appropriately web.application.endpoint.port=8080 web.application.endpoint.context=
For the execution of tests, in pom.xml necessary dependencies are added
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.0.0-M5</version> <dependencies> <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit47</artifactId> <version>3.0.0-M5</version> </dependency> </dependencies> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin>
We can run the project in the command line by using
mvn verify -Dskip.it=false
Under the target folder of the directory, we can see multiple files that help to understand different layers of testing
Â
Similarly whatever testing scenarios given are validated against Zerocode
Â