horizon-test: Support for Parallel execution & report fix.
parent
823c84c95d
commit
823c8cfcd5
|
|
@ -13,27 +13,31 @@ import com.utopiadeals.framework.BrowserContextManager;
|
||||||
import org.junit.jupiter.api.extension.*;
|
import org.junit.jupiter.api.extension.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
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 ExtentReports extent;
|
||||||
|
private static final Map<String, ExtentTest> suite = new ConcurrentHashMap<>();
|
||||||
private static final ThreadLocal<ExtentTest> test = new ThreadLocal<>();
|
private static final ThreadLocal<ExtentTest> test = new ThreadLocal<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeAll(ExtensionContext context) throws Exception {
|
public void beforeAll(ExtensionContext context) {
|
||||||
if (extent == null) {
|
if (extent == null) {
|
||||||
createExtentReports();
|
createExtentReports();
|
||||||
}
|
}
|
||||||
|
suite.computeIfAbsent(getClassName(context), k -> extent.createTest(getClassName(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterAll(ExtensionContext context) throws Exception {
|
public void afterAll(ExtensionContext context) {
|
||||||
if (extent != null) {
|
if (extent != null) {
|
||||||
extent.flush();
|
extent.flush();
|
||||||
}
|
}
|
||||||
|
|
@ -43,18 +47,28 @@ public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallbac
|
||||||
@Override
|
@Override
|
||||||
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
|
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
|
||||||
// This is called WHEN the exception occurs, before @AfterEach
|
// 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
|
throw throwable; // Re-throw to maintain normal failure flow
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeEach(ExtensionContext context) throws Exception {
|
public void beforeEach(ExtensionContext context) {
|
||||||
String className = context.getTestClass().map(Class::getSimpleName).orElse("Unknown");
|
String className = getClassName(context);
|
||||||
String testName = context.getTestMethod().map(m -> m.getName()).orElse("Unknown Test");
|
String methodName = getMethodName(context);
|
||||||
|
|
||||||
ExtentTest extentTest = extent.createTest(testName)
|
ExtentTest testSuite = suite.get(className);
|
||||||
.assignCategory(className);
|
if (testSuite != null) {
|
||||||
test.set(extentTest);
|
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() {
|
private void createExtentReports() {
|
||||||
|
|
@ -118,7 +132,7 @@ public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallbac
|
||||||
|
|
||||||
// Attach screenshot to the failed test
|
// Attach screenshot to the failed test
|
||||||
extentTest.fail(cause,
|
extentTest.fail(cause,
|
||||||
MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot,"Application Screenshot").build()
|
MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot, "Application Screenshot").build()
|
||||||
);
|
);
|
||||||
|
|
||||||
extentTest.info("Page URL: " + page.url());
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
package apitestsuites;
|
package apitestsuites;
|
||||||
|
|
||||||
import com.utopiadeals.api.RestWebCall;
|
import com.utopiadeals.api.RestWebCall;
|
||||||
|
import com.utopiadeals.framework.extensions.ExtentReportExtension;
|
||||||
import com.utopiadeals.utils.config.Constants;
|
import com.utopiadeals.utils.config.Constants;
|
||||||
import io.restassured.response.Response;
|
import io.restassured.response.Response;
|
||||||
import org.junit.jupiter.api.Tag;
|
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.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
@ExtendWith(ExtentReportExtension.class)
|
||||||
@Tag("access-mgmt-api")
|
@Tag("access-mgmt-api")
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
public class AccessManagementApiTest {
|
public class AccessManagementApiTest {
|
||||||
private static final String BASE_URL = Constants.getApiBaseUrl();
|
private static final String BASE_URL = Constants.getApiBaseUrl();
|
||||||
private static final String USERS_ENDPOINT = "/api/access-management/users";
|
private static final String USERS_ENDPOINT = "/api/access-management/users";
|
||||||
|
|
@ -20,6 +26,7 @@ public class AccessManagementApiTest {
|
||||||
private static Integer userId;
|
private static Integer userId;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Order(1)
|
||||||
public void addUser_WithValidData_ShouldReturnSuccess() {
|
public void addUser_WithValidData_ShouldReturnSuccess() {
|
||||||
// Arrange
|
// Arrange
|
||||||
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
||||||
|
|
@ -50,6 +57,7 @@ public class AccessManagementApiTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Order(2)
|
||||||
public void getUsers_WithValidEmailId_ShouldReturnSuccess() {
|
public void getUsers_WithValidEmailId_ShouldReturnSuccess() {
|
||||||
// Act
|
// Act
|
||||||
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
||||||
|
|
@ -62,6 +70,7 @@ public class AccessManagementApiTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Order(3)
|
||||||
public void getUsers_WithValidId_ShouldReturnSuccess() {
|
public void getUsers_WithValidId_ShouldReturnSuccess() {
|
||||||
// Act
|
// Act
|
||||||
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
apiClient = new RestWebCall(BASE_URL, Constants.getApiTestUserName(), Constants.getApiTestPassword());
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@ import com.utopiadeals.framework.annotations.JsonTestDataExtension;
|
||||||
import com.utopiadeals.utils.TestDataProvider;
|
import com.utopiadeals.utils.TestDataProvider;
|
||||||
import com.utopiadeals.web.pages.LoginPage;
|
import com.utopiadeals.web.pages.LoginPage;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
|
||||||
public class LoginTest extends WebTestSuiteBase{
|
public class LoginTest extends WebTestSuiteBase {
|
||||||
|
|
||||||
|
|
||||||
@DisplayName("Login Test")
|
@Tag("LoginTest")
|
||||||
@ParameterizedTest()
|
@ParameterizedTest()
|
||||||
@JsonTestDataExtension("dataSet-0,dataSet-1")
|
@JsonTestDataExtension("dataSet-0,dataSet-1")
|
||||||
public void loginTest(TestDataProvider testDataProvider){
|
public void loginTest(TestDataProvider testDataProvider) {
|
||||||
LoginPage loginPage = new LoginPage();
|
LoginPage loginPage = new LoginPage();
|
||||||
//loginPage.navigate("https://app.sellercosmos.com/");
|
//loginPage.navigate("https://app.sellercosmos.com/");
|
||||||
String userName = testDataProvider.getString("username");
|
String userName = testDataProvider.getString("username");
|
||||||
String password = testDataProvider.getString("password");
|
String password = testDataProvider.getString("password");
|
||||||
loginPage.login(userName,password);
|
loginPage.login(userName, password);
|
||||||
// loginPage.login("utest@utopiadeals.com222222","utest0001");
|
// loginPage.login("utest@utopiadeals.com222222","utest0001");
|
||||||
Assertions.assertTrue(false,"Expecting true but found false");
|
Assertions.assertTrue(false, "Expecting true but found false");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"dataSets": {
|
"dataSets": {
|
||||||
"dataSet-0": {
|
"dataSet-0": {
|
||||||
"username": "utest@utopiadeals.com",
|
"username": "utest@utopiadeals.com",
|
||||||
"password": "utest0001"
|
"password": "utest001"
|
||||||
},
|
},
|
||||||
"dataSet-1": {
|
"dataSet-1": {
|
||||||
"username": "abdullah@utopiadeals.com",
|
"username": "abdullah@utopiadeals.com",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue