What You Will Learn
This course builds a complete, enterprise-grade API test automation framework in Java — from scratch. You will implement BDD feature files with Cucumber 7, write REST Assured request/response flows, manage POJO serialization/deserialization with Jackson, inject dependencies using Pico Container, and publish professional Extent Reports — all integrated with Maven for CI/CD.
- What is a test automation framework and why it matters — standardization, reusability, and team consistency
- Framework benefits: one structure everyone follows means less rework and faster onboarding of new team members
- BDD frameworks vs. traditional automation — BDD bridges the gap between developers, testers, and business stakeholders
- BDD approach overview — write tests in plain English that both humans and machines can understand
- Maven project setup —
pom.xmlstructure, project layout, and standard directory conventions - Initial dependencies — REST Assured
5.3.0, Cucumber7.11.1, and JUnit 4 for running tests - Eclipse IDE setup with the Cucumber plugin for syntax highlighting and navigation
- Framework folder hierarchy —
src/test/java,src/test/resources, and thefeatures/directory
- HTTP protocol fundamentals —
GETfetches data,POSTcreates,PUT/PATCHupdate,DELETEremoves - Request-response lifecycle — the journey from your test sending a URL+method to the server replying with data
- Status codes and what they tell you —
200OK,201Created,400Bad Request,401Unauthorized,404Not Found,500Server Error - REST Assured library basics — the fluent
given().when().then()pattern that makes API tests readable - Making your first API calls —
.get()and.post()with a base URI configured once for all tests - Request logging —
.log().all()prints everything you send, invaluable when debugging failures - Response logging —
.then().log().all()prints exactly what the server sent back - Basic assertions —
statusCode(200)andbody("field", equalTo("value"))to confirm the API behaved correctly
- Behavior-Driven Development (BDD) — a collaboration style where developers, testers, and business write test scenarios together before coding begins
- Gherkin language —
Feature,Scenario,Given,When,Then,And,Butkeywords in plain English - Feature files —
.featureextension, stored insrc/test/resources/features/, one feature per API endpoint group - Cucumber 7.11.1 setup — adding
cucumber-javaandcucumber-junittopom.xml - Eclipse Cucumber plugin — syntax highlighting, step definition navigation, and auto-complete for Gherkin steps
- Writing scenarios that test one API behavior per scenario — keeps failures easy to diagnose
- Runner class setup —
@RunWith(Cucumber.class)tells JUnit to hand control to Cucumber @CucumberOptions—featurespath,gluepackage,pluginreporters,dryRunflag, andmonochromefor cleaner console output
- Step definitions — Java methods annotated with
@Given,@When, or@Thenthat execute when Cucumber matches a scenario step - Mapping feature file steps to Java — Cucumber scans the
gluepackage for matching annotations using exact text - Regular expressions and Cucumber expressions —
{string},{int},{word}placeholders capture values from Gherkin steps - Parameter passing — values captured in the step text are passed as Java method arguments automatically
- Step definition class structure — package naming conventions and keeping related steps in the same class
- Dry-run mode —
dryRun=truevalidates every feature file step has a matching definition without actually running tests - Auto-generated step snippets — when a step has no definition, Cucumber prints the Java method signature for you to implement
- Ambiguous step resolution — what happens when two step definitions match the same Gherkin text and how to fix it
- JSON file-based payloads — storing request bodies as
.jsonfiles insrc/test/resources/payloads/keeps data separate from code - POJO (Plain Old Java Object) classes — create Java classes matching your API's data model, with fields and getters/setters
- Jackson serialization — converting a POJO object to a JSON string using
ObjectMapper.writeValueAsString() @JsonPropertyannotations — map Java field names to JSON keys when they differ (e.g.,firstNamevsfirst_name)FileInputStream— reading JSON payload files at runtime so tests use real-world data files.body(pojoObject)vs..body(jsonString)— REST Assured accepts both; POJOs are cleaner for maintenance- Setting
ContentType.JSONwith.contentType(ContentType.JSON)— tells the server your request body is JSON - Building POST and PATCH requests —
given().body(payload).when().post("/endpoint")as the standard pattern
- Inline body validation —
.body("field", equalTo("value"))uses Hamcrest matchers to assert specific JSON fields - JsonPath extraction —
.extract().path("data.id")pulls a value out of the response for use in the next test step - POJO deserialization —
.as(PojoClass.class)converts the JSON response body into a Java object automatically - Response header validation —
.header("Content-Type", "application/json")confirms the server is sending the correct format ResponseSpecBuilder— define once (status code, content type, common fields) and reuse across all tests via.spec()RequestSpecBuilder— centralises base URI, authentication headers, and content type so you don't repeat them in every test- Chaining extracted values — capture the
idreturned by a POST and use it in the subsequent GET or DELETE request - Hamcrest matchers —
equalTo(),containsString(),hasItems(),notNullValue()for expressive assertions
- Scenario Outline — write a scenario once with placeholders, then drive it with an
Examples:table for multiple data sets - Data tables in Gherkin — inline multi-row datasets directly in the feature file, passed to step definitions as a list
- Testing positive, negative, and boundary cases in a single feature file without code duplication
- Query parameters —
.queryParam("key","value")appends search/filter parameters to GET requests - Path parameters —
.pathParam("id", userId)inserts dynamic values into URL segments like/users/{id} - Combining positive and negative test cases in one Scenario Outline — one
Examplestable, multiple test behaviors Backgroundkeyword — shared setup steps (like logging in) that run automatically before every scenario in a feature- Doc Strings in Gherkin — embed multi-line JSON payloads directly in the feature file as step arguments
- Cucumber tags — label scenarios or entire features with
@smoke,@regression,@sanityto group them logically - Tag filtering with OR —
tags = "@smoke or @regression"runs scenarios matching either tag - Tag filtering with AND —
tags = "@smoke and @regression"runs only scenarios that have both tags - Negation tags —
tags = "not @wip"excludes any scenario still marked as work-in-progress - Dry-run with tags — check that step definitions exist for all tagged scenarios without executing any tests
- Feature-level tagging — tag an entire
.featurefile so all its scenarios inherit that tag automatically - Scenario-level tagging — apply different tags to individual scenarios within the same feature file
- Running tag subsets via
@CucumberOptions(tags=...)in the Runner class — no command-line changes needed
@Before— runs before each scenario; perfect for generating auth tokens, initialising connections, or resetting state@After— runs after each scenario; use for cleanup, logging results, or capturing failure screenshots@BeforeAll/@AfterAll— suite-level setup and teardown that runs once before or after the entire test run- Conditional hooks —
@Before("@smoke")applies setup logic only to scenarios tagged with that specific label - Token generation hook — automatically call the login/auth API before tests so every scenario gets a valid bearer token
- Database verification example — check database state after a scenario to confirm the API wrote the correct data
- Hook ordering — the
orderparameter controls which hook runs first when multiple@Beforehooks exist - Hooks as a separate class — keeps setup/teardown logic out of step definition files; relies on dependency injection to share state
- Why DI is needed — when step definitions live in multiple classes, they can't access each other's variables without a sharing mechanism
- Cucumber Pico Container — add the
cucumber-picocontainerdependency; Cucumber handles the rest automatically - Constructor-based injection — declare a shared object in the constructor and Cucumber injects the same instance into every class that needs it
- Base utilities pattern — create a
TestContextorRestUtilsclass that holds shared state (response, request spec, extracted IDs) - Separating concerns — base classes, step definitions, and helper utilities in distinct packages for clean architecture
- Avoiding
NullPointerException— the most common crash in multi-class step definition setups, solved by proper DI - Sharing
RequestSpecificationandResponseobjects across classes so one class can build the request and another can validate it - Thread-safe design — using instance variables (not static) sets your framework up for parallel execution when the project scales
- Properties files —
config.properties,dev.properties,qa.properties,staging.propertieshold environment-specific settings outside the code - Global environment switching — changing one property value redirects the entire test suite to a different server
- Base URI management —
RestAssured.baseURI = prop.getProperty("baseURL")sets the root URL for all requests in one place - Why externalized config matters — hard-coded URLs and passwords break the moment a server URL changes or a password rotates
ConfigReaderutility class — reads.propertiesfiles usingFileInputStreamand provides a cleangetProperty(key)method- Environment selection via Maven — pass
-Denv=qaon the command line; theConfigReaderloads the matching properties file - Credential management — usernames, passwords, and API tokens live in config files, never committed to version control
- REST Assured global settings —
RestAssured.baseURI,basePath, andportconfigured once for the entire test suite
- Extent Reports 5 —
ExtentReports,ExtentTest, andExtentSparkReporterare the three core classes for building reports - Extent Cucumber 7 adapter — plug the
ExtentCucumberAdapterinto@CucumberOptions(plugin=...)and reports generate automatically - Spark HTML report — a rich, colour-coded pass/fail dashboard with scenario details, screenshots, and timing information
- PDF report —
ExtentPDFReporterproduces an archivable PDF version ideal for sharing with stakeholders - Report folder structure — use date-time stamps in the output path to create a unique report for every test run
- Maven test execution —
mvn testcompiles and runs all tests;mvn clean testclears previous output first - Surefire plugin — configure
maven-surefire-plugininpom.xmlto control which Runner class executes and set JVM options - Parallel execution — Surefire's
dataproviderthreadcountandparallelsettings run multiple scenarios simultaneously - JDK version and Maven build path configuration — common setup issues resolved so your framework compiles correctly
- CI/CD readiness — adding
mvn clean testas a Jenkins build step or GitHub Actions workflow step automates the entire test pipeline
BDD vs. TDD vs. ATDD
BDD extends TDD by using Gherkin that every stakeholder can read. "Given-When-Then" scenarios serve as living documentation — they describe expected behavior AND verify it. ATDD focuses on acceptance criteria from the user's point of view. Cucumber bridges feature files (business-readable) with step definitions (developer-implemented).
REST Assured Syntax
Write from memory: given().spec(reqSpec).body(payload).when().post("/users").then().spec(resSpec).statusCode(201).extract().path("data.id"). Know RequestSpecBuilder for centralising base config and ResponseSpecBuilder for reusable assertions — these show framework-level thinking.
Dependency Injection
Answer confidently: "Cucumber doesn't allow sharing state through static variables — Pico Container injects shared objects via constructors. I create a TestContext class that holds the Response, extracted IDs, and request spec. Every step definition class receives the same instance through its constructor."
POJO Serialization
Explain clearly: "I create Java POJO classes with Jackson annotations. ObjectMapper.writeValueAsString(pojo) converts the object to a JSON string for the request body. On the response side, response.as(Pojo.class) maps JSON back to Java so I can access fields directly. No hand-crafted JSON strings in the code."
Tag-Based Execution
"I use @smoke, @regression, and @sanity tags in feature files. In Jenkins I pass -Dcucumber.filter.tags="@smoke" as a Maven parameter to run only the fast, critical tests on every commit. Full @regression runs overnight. not @wip keeps in-progress tests out of the pipeline entirely."
Framework Design
Walk them through your package structure: features/ holds all Gherkin scenarios, stepDefinitions/ holds the Java implementations, utilities/ has ConfigReader and RestUtils, payloads/ stores JSON request files, and pojo/ holds data model classes. This architecture is what gets you hired.