如何让Selenium+TestNG+Java框架中软断言失败步骤在ExtentReports中展示
Hey there! Let's work through this problem together. You want to swap out hard assertions for soft ones so your test steps keep running even if one fails, and also make sure each soft assertion failure gets logged directly under its corresponding step in ExtentReports (instead of just a generic failure at the end). Here's a practical, step-by-step solution:
TestNG's native SoftAssert only reports all failures at once when you call assertAll(), which doesn't tie failures to individual test steps in ExtentReports. We need to bridge this gap by wrapping soft assertions with Extent logging logic.
We'll build a utility class that combines TestNG's SoftAssert with ExtentReports' logging. This ensures every assertion failure is immediately logged to the correct test step without stopping execution.
import com.aventstack.extentreports.ExtentTest; import com.aventstack.extentreports.Status; import org.testng.asserts.SoftAssert; public class ExtentSoftAssert { private final SoftAssert softAssert; private final ExtentTest extentTest; // Constructor ties the soft assert to a specific ExtentTest instance public ExtentSoftAssert(ExtentTest extentTest) { this.softAssert = new SoftAssert(); this.extentTest = extentTest; } // Example: Assert True with step-specific logging public void assertTrue(boolean condition, String failureMessage) { try { softAssert.assertTrue(condition, failureMessage); } catch (AssertionError e) { // Log failure directly to the current Extent test/step extentTest.log(Status.FAIL, String.format("%s | Failure Details: %s", failureMessage, e.getMessage())); } } // Example: Assert Equals with value comparison logging public void assertEquals(Object actual, Object expected, String failureMessage) { try { softAssert.assertEquals(actual, expected, failureMessage); } catch (AssertionError e) { extentTest.log(Status.FAIL, String.format( "%s | Expected: %s | Actual: %s | Failure Details: %s", failureMessage, expected, actual, e.getMessage() )); } } // Add more assertion methods (assertFalse, assertNotNull, etc.) as needed // Final check to trigger TestNG's failure reporting public void assertAll() { try { softAssert.assertAll(); } catch (AssertionError e) { extentTest.log(Status.FAIL, String.format( "All soft assertions completed. Total failures: %d", softAssert.getAllErrors().size() )); throw e; // Re-throw to mark the test as failed in TestNG } } }
In your test methods, initialize the custom soft assert with your ExtentTest instance, and log each test step explicitly. This ensures failures are linked to the correct step in the report.
import com.aventstack.extentreports.ExtentReports; import com.aventstack.extentreports.ExtentTest; import com.aventstack.extentreports.reporter.ExtentSparkReporter; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class LoginFlowTest { private WebDriver driver; private ExtentReports extentReports; private ExtentTest test; private ExtentSoftAssert extentSoftAssert; @BeforeMethod public void setup() { // Initialize Selenium driver driver = new ChromeDriver(); driver.manage().window().maximize(); // Initialize ExtentReports ExtentSparkReporter sparkReporter = new ExtentSparkReporter("test-output/login-test-report.html"); extentReports = new ExtentReports(); extentReports.attachReporter(sparkReporter); // Create test instance for the current test method test = extentReports.createTest("End-to-End Login Flow Test"); // Initialize our custom soft assert extentSoftAssert = new ExtentSoftAssert(test); } @Test public void invalidLoginTest() { // Step 1: Navigate to login page test.log(Status.INFO, "Navigating to login page"); driver.get("https://your-test-site.com/login"); extentSoftAssert.assertEquals(driver.getTitle(), "Secure Login", "Step 1: Login page title mismatch"); // Step 2: Enter username test.log(Status.INFO, "Entering username"); driver.findElement(By.id("username")).sendKeys("test_user"); extentSoftAssert.assertEquals( driver.findElement(By.id("username")).getAttribute("value"), "test_user", "Step 2: Username input value mismatch" ); // Step 3: Enter invalid password test.log(Status.INFO, "Entering invalid password"); driver.findElement(By.id("password")).sendKeys("wrong_pass"); extentSoftAssert.assertEquals( driver.findElement(By.id("password")).getAttribute("value"), "wrong_pass", "Step 3: Password input value mismatch" ); // Step 4: Click login button test.log(Status.INFO, "Clicking login button"); driver.findElement(By.id("login-btn")).click(); extentSoftAssert.assertTrue( driver.findElement(By.id("error-alert")).isDisplayed(), "Step 4: Error alert not displayed after invalid login" ); // Final check to ensure TestNG captures all failures extentSoftAssert.assertAll(); } @AfterMethod public void teardown() { if (driver != null) { driver.quit(); } // Flush ExtentReports to write all logs to the report file extentReports.flush(); } }
- Step-Specific Logging: Each test step uses
test.log(Status.INFO)to create a clear entry in ExtentReports. Failures from soft assertions are logged directly under these steps. - Custom Assert Methods: You can extend the
ExtentSoftAssertclass with any assertion types you need (e.g.,assertFalse,assertNotNull) following the same pattern. - Final
assertAll(): This method is critical—it tells TestNG to mark the test as failed if any soft assertions failed, and logs the total number of failures in the report.
内容的提问来源于stack exchange,提问作者JustNatural

