diff --git a/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java b/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java index 6139a18..e2beaab 100644 --- a/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java +++ b/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java @@ -13,27 +13,31 @@ import com.utopiadeals.framework.BrowserContextManager; import org.junit.jupiter.api.extension.*; import java.io.File; +import java.lang.reflect.Method; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; -public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, TestWatcher, TestExecutionExceptionHandler { +public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, TestWatcher, TestExecutionExceptionHandler { private static ExtentReports extent; + private static final Map suite = new ConcurrentHashMap<>(); private static final ThreadLocal test = new ThreadLocal<>(); @Override - public void beforeAll(ExtensionContext context) throws Exception { + public void beforeAll(ExtensionContext context) { if (extent == null) { createExtentReports(); } - + suite.computeIfAbsent(getClassName(context), k -> extent.createTest(getClassName(context))); } @Override - public void afterAll(ExtensionContext context) throws Exception { + public void afterAll(ExtensionContext context) { if (extent != null) { extent.flush(); } @@ -43,18 +47,28 @@ public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallbac @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { // This is called WHEN the exception occurs, before @AfterEach - captureFailureScreenshot(test.get(),throwable.getCause()); + captureFailureScreenshot(test.get(), throwable.getCause()); throw throwable; // Re-throw to maintain normal failure flow } @Override - public void beforeEach(ExtensionContext context) throws Exception { - String className = context.getTestClass().map(Class::getSimpleName).orElse("Unknown"); - String testName = context.getTestMethod().map(m -> m.getName()).orElse("Unknown Test"); + public void beforeEach(ExtensionContext context) { + String className = getClassName(context); + String methodName = getMethodName(context); - ExtentTest extentTest = extent.createTest(testName) - .assignCategory(className); - test.set(extentTest); + ExtentTest testSuite = suite.get(className); + if (testSuite != null) { + ExtentTest extentTestNode = testSuite.createNode(methodName); + test.set(extentTestNode); + } + } + + @Override + public void afterEach(ExtensionContext context) { + // Do not clear the ThreadLocal here because JUnit invokes TestWatcher callbacks + // (testSuccessful/testFailed/testAborted) after AfterEach, which would make test.get() null + // inside those callbacks and cause NPEs. The ThreadLocal will be overwritten in beforeEach + // for the next test method and cleared at JVM end. } private void createExtentReports() { @@ -118,7 +132,7 @@ public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallbac // Attach screenshot to the failed test extentTest.fail(cause, - MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot,"Application Screenshot").build() + MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot, "Application Screenshot").build() ); extentTest.info("Page URL: " + page.url()); @@ -152,5 +166,17 @@ public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallbac } } + private String getClassName(ExtensionContext context) { + return context.getTestClass() + .map(Class::getSimpleName) + .orElse("UnknownClass"); + } + + private String getMethodName(ExtensionContext context) { + return context.getTestMethod() + .map(Method::getName) + .orElse("UnknownMethod"); + } + } diff --git a/src/test/java/apitestsuites/AccessManagementApiTest.java b/src/test/java/apitestsuites/AccessManagementApiTest.java index 0c3fbc7..03f7683 100644 --- a/src/test/java/apitestsuites/AccessManagementApiTest.java +++ b/src/test/java/apitestsuites/AccessManagementApiTest.java @@ -1,17 +1,23 @@ package apitestsuites; import com.utopiadeals.api.RestWebCall; +import com.utopiadeals.framework.extensions.ExtentReportExtension; import com.utopiadeals.utils.config.Constants; import io.restassured.response.Response; import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; - +@ExtendWith(ExtentReportExtension.class) @Tag("access-mgmt-api") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class AccessManagementApiTest { private static final String BASE_URL = Constants.getApiBaseUrl(); private static final String USERS_ENDPOINT = "/api/access-management/users"; @@ -20,6 +26,7 @@ public class AccessManagementApiTest { private static Integer userId; @Test + @Order(1) public void addUser_WithValidData_ShouldReturnSuccess() { // Arrange apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword()); @@ -50,6 +57,7 @@ public class AccessManagementApiTest { } @Test + @Order(2) public void getUsers_WithValidEmailId_ShouldReturnSuccess() { // Act apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword()); @@ -62,6 +70,7 @@ public class AccessManagementApiTest { } @Test + @Order(3) public void getUsers_WithValidId_ShouldReturnSuccess() { // Act apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword()); diff --git a/src/test/java/webtestsuites/LoginTest.java b/src/test/java/webtestsuites/LoginTest.java index 41afe9b..0f77422 100644 --- a/src/test/java/webtestsuites/LoginTest.java +++ b/src/test/java/webtestsuites/LoginTest.java @@ -4,23 +4,23 @@ import com.utopiadeals.framework.annotations.JsonTestDataExtension; import com.utopiadeals.utils.TestDataProvider; import com.utopiadeals.web.pages.LoginPage; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; -public class LoginTest extends WebTestSuiteBase{ +public class LoginTest extends WebTestSuiteBase { - @DisplayName("Login Test") + @Tag("LoginTest") @ParameterizedTest() @JsonTestDataExtension("dataSet-0,dataSet-1") - public void loginTest(TestDataProvider testDataProvider){ + public void loginTest(TestDataProvider testDataProvider) { LoginPage loginPage = new LoginPage(); //loginPage.navigate("https://app.sellercosmos.com/"); String userName = testDataProvider.getString("username"); String password = testDataProvider.getString("password"); - loginPage.login(userName,password); - // loginPage.login("utest@utopiadeals.com222222","utest0001"); - Assertions.assertTrue(false,"Expecting true but found false"); + loginPage.login(userName, password); + // loginPage.login("utest@utopiadeals.com222222","utest0001"); + Assertions.assertTrue(false, "Expecting true but found false"); } } diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties new file mode 100644 index 0000000..a329bf2 --- /dev/null +++ b/src/test/resources/junit-platform.properties @@ -0,0 +1,12 @@ +junit.jupiter.execution.parallel.enabled = true +# Run test METHODS sequentially within each class to preserve intended order +junit.jupiter.execution.parallel.mode.default = same_thread +# Allow different test classes to run in parallel +junit.jupiter.execution.parallel.mode.classes.default = concurrent + +# Fixed parallelism for class-level concurrency +junit.jupiter.execution.parallel.config.strategy = fixed +junit.jupiter.execution.parallel.config.fixed.parallelism = 4 + +# Execute test methods in the order specified by @Order annotations +junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$OrderAnnotation \ No newline at end of file diff --git a/src/test/resources/test-data/test-data.json b/src/test/resources/test-data/test-data.json index 23fd1c4..cc1c959 100644 --- a/src/test/resources/test-data/test-data.json +++ b/src/test/resources/test-data/test-data.json @@ -4,7 +4,7 @@ "dataSets": { "dataSet-0": { "username": "utest@utopiadeals.com", - "password": "utest0001" + "password": "utest001" }, "dataSet-1": { "username": "abdullah@utopiadeals.com",