This guide helps you identify and resolve common issues when using the API Automation Framework.
Error Message:
PropertyNotFoundException: Property not found for key: BASE_URL
Causes:
@ConfigProperty
annotationSolutions:
# src/main/resources/api.properties
BASE_URL=https://api.example.com
@ConfigProperty("BASE_URL") // Must match property name exactly
String getBaseUrl();
# Ensure environment property is set
mvn test -Dfaasos_env=dev
src/main/resources/
├── api.properties
└── environment/
├── dev.properties
└── prod.properties
Error Message:
FileNotFoundException: requests/login.json
Causes:
@RequestTemplateFile
annotationSolutions:
src/main/resources/requests/login.json
@RequestTemplateFile("requests/login.json") // Relative to resources
RequestSpecBuilder loginRequest(String username, String password);
src/main/resources/
└── requests/
├── login.json
├── user-create.json
└── user-update.json
Error Message:
Schema validation failed: [path] is not a valid email
Causes:
Solutions:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email"
}
}
}
# Use online tools to generate schema from actual response
# https://transform.tools/json-to-json-schema
// Comment out schema validation for debugging
// .matchesSchema("response/schemas/user-schema.json")
Error Message:
No tests found
Causes:
Solutions:
src/test/resources/features/
package org.aditi.api_automation.steps;
@CucumberOptions(
features = "src/test/resources/features",
glue = "org.aditi.api_automation.steps"
)
<suite name="API Test Suite">
<test name="API Tests">
<classes>
<class name="org.aditi.api_automation.runner.TestNGParallelRunner"/>
</classes>
</test>
</suite>
Problem: Configuration properties not loading for specific environment.
Debug Steps:
echo $faasos_env
src/main/resources/environment/dev.properties
src/main/resources/environment/prod.properties
// Add debug logging
log.info("Loading environment: {}", environment);
log.info("Properties loaded: {}", properties);
Solution:
# Set environment explicitly
mvn test -Dfaasos_env=dev
Problem: Properties not resolving correctly.
Debug Steps:
// Properties are loaded in this order:
// 1. System properties
// 2. Environment-specific properties
// 3. Default properties
log.info("Property value for {}: {}", key, value);
Solution:
# Use explicit property definition
BASE_URL=https://api.example.com
API_VERSION=v1
Problem: ConfigFactoryProxy
not working correctly.
Debug Steps:
ConfigFactory proxy = ConfigFactoryProxy.newInstance(configManager);
log.info("Proxy created: {}", proxy.getClass().getName());
// Add debug logging in invoke method
log.info("Method called: {}", method.getName());
log.info("Property key: {}", propertyName);
Solution:
// Ensure proper exception handling
try {
String value = config.getBaseUrl();
} catch (PropertyNotFoundException e) {
log.error("Property not found: {}", e.getMessage());
// Handle gracefully
}
Problem: Step definitions not matching feature files.
Debug Steps:
@Given("I have valid user credentials") // Must match exactly
public void i_have_valid_user_credentials() {
// Implementation
}
@When("I request user with ID {string}") // Use {string} for strings
public void i_request_user_with_id(String id) {
// Implementation
}
@CucumberOptions(
features = "src/test/resources/features",
glue = "org.aditi.api_automation.steps" // Must include step package
)
Solution:
// Use proper Cucumber annotations
@Given("I have valid user credentials")
@When("I attempt to login")
@Then("I should receive a successful response")
Problem: Test data not available or incorrect.
Debug Steps:
@Before
public void setupTestData() {
// Ensure test data is properly initialized
log.info("Setting up test data");
}
@After
public void cleanupTestData() {
// Clean up test data after each test
log.info("Cleaning up test data");
}
Solution:
// Use proper test data management
public class TestDataManager {
public static UserLoginRequest getValidUser() {
return new UserLoginRequest("testuser", "testpass");
}
public static UserLoginRequest getInvalidUser() {
return new UserLoginRequest("invalid", "invalid");
}
}
Problem: Tests failing when run in parallel.
Debug Steps:
// Ensure shared state is thread-safe
private static final ThreadLocal<Response> responseHolder = new ThreadLocal<>();
@Before
public void setup() {
// Reset state for each test
responseHolder.remove();
}
Solution:
// Use thread-local variables for shared state
public class TestContext {
private static final ThreadLocal<Response> response = new ThreadLocal<>();
private static final ThreadLocal<String> userId = new ThreadLocal<>();
public static void setResponse(Response response) {
TestContext.response.set(response);
}
public static Response getResponse() {
return TestContext.response.get();
}
}
Error Message:
java.net.ConnectException: Connection refused
Causes:
Solutions:
curl -I https://api.example.com/health
BASE_URL=https://api.example.com
// Add connectivity test
@BeforeClass
public static void testConnectivity() {
try {
given().baseUri(AppConfig.getConfig().getBaseUrl())
.when().get("/health")
.then().statusCode(200);
} catch (Exception e) {
log.error("API server not accessible: {}", e.getMessage());
throw new RuntimeException("API server not accessible", e);
}
}
Error Message:
401 Unauthorized
Causes:
Solutions:
// Ensure proper authentication
given()
.header("Authorization", "Bearer " + getAuthToken())
.when()
.get("/protected-endpoint")
public static String getAuthToken() {
// Implement token generation/retrieval
return AppConfig.getConfig().getAuthToken();
}
public static String refreshTokenIfNeeded() {
// Check if token is expired and refresh if needed
return tokenManager.refreshToken();
}
Error Message:
java.net.SocketTimeoutException: Read timed out
Causes:
Solutions:
given()
.config(RestAssured.config()
.httpClient(HttpClientConfig.httpClientConfig()
.setConnectTimeout(30000)
.setReadTimeout(60000)))
.when()
.get("/slow-endpoint")
REQUEST_TIMEOUT=60000
CONNECT_TIMEOUT=30000
public static Response getWithRetry(String endpoint, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
try {
return given().when().get(endpoint);
} catch (Exception e) {
if (i == maxRetries - 1) throw e;
log.warn("Request failed, retrying... ({}/{})", i + 1, maxRetries);
}
}
throw new RuntimeException("Max retries exceeded");
}
Error Message:
Expected: "John Doe"
Actual: "Jane Smith"
Causes:
Solutions:
// Use correct JSON path syntax
.validateJsonPathData("data.name", "John Doe") // Correct
.validateJsonPathData("data.Name", "John Doe") // Wrong case
// Print response for debugging
log.info("Response body: {}", response.getBody().asString());
log.info("JSON path value: {}", response.jsonPath().getString("data.name"));
// Use contains instead of exact match
.validateJsonPathData("data.name", containsString("John"))
Error Message:
Schema validation failed: [path] is not a valid email
Solutions:
{
"properties": {
"email": {
"type": "string",
"pattern": "^[^@]+@[^@]+\\.[^@]+$"
}
}
}
# Validate schema syntax
npm install -g ajv-cli
ajv validate -s schema.json -d response.json
// Comment out for debugging
// .matchesSchema("response/schemas/user-schema.json")
Error Message:
Expected: 200
Actual: 201
Causes:
Solutions:
// POST requests typically return 201 for creation
.httpStatusCodeIs(201) // For resource creation
.httpStatusCodeIs(200) // For successful operations
// Validate status code is in success range
.httpStatusCodeIs(both(greaterThanOrEqualTo(200)).and(lessThan(300)))
if (response.getStatusCode() == 200 || response.getStatusCode() == 201) {
// Both are acceptable
} else {
fail("Unexpected status code: " + response.getStatusCode());
}
Problem: Tests taking too long to execute.
Solutions:
<suite name="API Test Suite" parallel="methods" thread-count="4">
// Use shared request specifications
public static RequestSpecification getDefaultSpec() {
return new RequestSpecBuilder()
.setBaseUri(AppConfig.getConfig().getBaseUrl())
.setContentType(ContentType.JSON)
.build();
}
private static final Map<String, Response> cache = new ConcurrentHashMap<>();
public static Response getCachedResponse(String key) {
return cache.computeIfAbsent(key, k -> makeApiCall(k));
}
Problem: Tests consuming too much memory.
Solutions:
@After
public void cleanup() {
// Clear caches
cache.clear();
// Reset thread locals
TestContext.clear();
}
given()
.queryParam("limit", 10) // Limit response size
.when()
.get("/users")
given()
.when()
.get("/large-dataset")
.then()
.body(hasSize(lessThan(1000))); // Validate reasonable size
Problem: Tests work in dev but fail in production.
Solutions:
# dev.properties
BASE_URL=https://dev-api.example.com
TIMEOUT=30000
# prod.properties
BASE_URL=https://api.example.com
TIMEOUT=60000
public static UserLoginRequest getTestUser() {
String env = System.getProperty("faasos_env", "dev");
if ("prod".equals(env)) {
return new UserLoginRequest("prod-user", "prod-pass");
} else {
return new UserLoginRequest("dev-user", "dev-pass");
}
}
@Test
@Ignore("Only run in production")
public void testProductionSpecificFeature() {
assumeTrue("prod".equals(System.getProperty("faasos_env")));
// Test implementation
}
Problem: Tests fail in CI/CD but pass locally.
Solutions:
# .github/workflows/test.yml
env:
faasos_env: dev
JAVA_OPTS: -Xmx2g
# ci.properties
BASE_URL=https://ci-api.example.com
TIMEOUT=120000
FROM openjdk:21-jdk
COPY . /app
WORKDIR /app
RUN mvn clean install
CMD ["mvn", "test"]
// Add to test class
@Before
public void enableDebugLogging() {
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
RestAssured.config = RestAssured.config()
.logConfig(LogConfig.logConfig().enableLoggingOfRequestAndResponseIfValidationFails());
}
@When("I attempt to login")
public void i_attempt_to_login() {
log.info("Attempting login with username: {}", loginRequest.getUsername());
log.info("Request body: {}", loginRequest);
response = UserApi.login(loginRequest);
log.info("Response status: {}", response.getStatusCode());
log.info("Response body: {}", response.getBody().asString());
}
public class TestListener implements ITestListener {
@Override
public void onTestStart(ITestResult result) {
log.info("Starting test: {}", result.getName());
}
@Override
public void onTestFailure(ITestResult result) {
log.error("Test failed: {}", result.getName());
log.error("Exception: {}", result.getThrowable());
}
}
public class DebugUtils {
public static void printResponseDetails(Response response) {
log.info("Status Code: {}", response.getStatusCode());
log.info("Headers: {}", response.getHeaders());
log.info("Body: {}", response.getBody().asString());
log.info("Time: {}", response.getTime());
}
public static void saveResponseToFile(Response response, String filename) {
try {
Files.write(Paths.get(filename), response.getBody().asString().getBytes());
log.info("Response saved to: {}", filename);
} catch (IOException e) {
log.error("Failed to save response", e);
}
}
}
# Enable verbose logging
mvn test -Dlogback.configurationFile=logback-debug.xml
# Check Maven logs
mvn test -X
# Check TestNG reports
open target/surefire-reports/index.html
// Add debug breakpoints in IDE
// Use conditional breakpoints for specific scenarios
// Step through code execution
// Create a minimal test case that reproduces the issue
@Test
public void minimalReproduction() {
// Minimal setup
// Minimal request
// Minimal validation
}
When contacting support, include:
This troubleshooting guide should help you resolve most common issues. If you encounter a problem not covered here, please create a detailed issue report with all the information mentioned in the “Getting Help” section.