How to Take Screenshot when an Assert fails
How to Take Screenshot when an Assert fails
Let’s learn the steps involved to take screenshot in Selenium automation Tests using TestNG framework when an assert fails in test methods.
It’s an awesome capability to capture a screenshot when an Assert fails with TestNG Framework. To get started, lets learn Assert life cycle in the TestNG Automation framework.
TestNG Assert Lifecycle
We can write custom assertions in TestNG by overriding the onAssertFailure() method in the Assert lifecycle. This capability hooks this method to capture the screen whenever an Assert Fails during the test runs. To know more about TestNG Assert Life Cycle:
https://www.testingdocs.com/testng-assert-lifecycle/
Sample Code Listing:
Custom implementation of SoftAssert class that overrides the lifecycle method.
package com.testingdocs.testng.tutorial; //############################################ // www.TestingDocs.com // CustomSoftAssert.java this class is-A subclass // of SoftAssert and overrides the life-cycle method // onAssertFailure(). //############################################# import java.io.File; import java.io.IOException; import java.util.Date; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; import org.testng.ITestContext; import org.testng.asserts.Assertion; import org.testng.asserts.IAssert; import org.testng.asserts.SoftAssert; import org.apache.commons.io.FileUtils; public class CustomSoftAssert extends SoftAssert { private WebDriver driver; public CustomSoftAssert(WebDriver driver) { this.driver=driver; } @Override public void onAssertFailure(IAssert<?> assertCommand, AssertionError ex) { System.out.println("Assert Failed..."); File scrFile = ((TakesScreenshot) driver) .getScreenshotAs(OutputType.FILE); try { FileUtils.copyFile(scrFile, new File("AssertFailure_" + assertCommand.getMessage() + ".jpg")); } catch (IOException e) { e.printStackTrace(); } } }
Failing Test – Assert Fails
Let’s write a failing test to capture the screenshot and We can instantiate the class and use it in the test classes.
package com.testingdocs.testng.tutorial; /********************************************************** // AssertFailScreenshot.java // Sample Failing Test to capture screenshot // when an assert fails. // MyCustomizationListener is used to highlight the // web element in the screenshot. // www.TestingDocs.com ************************************************************/ import java.io.IOException; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.events.EventFiringWebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; public class AssertFailScreenshot { public WebDriver tDocsDriver; public EventFiringWebDriver eFireDriver = null; public CustomSoftAssert csa; @BeforeSuite public void setup() { System.setProperty("webdriver.chrome.driver", "drivers/chromedriver.exe"); tDocsDriver=new ChromeDriver(); eFireDriver = new EventFiringWebDriver(tDocsDriver); MyCustomizationListener myListener = new MyCustomizationListener(); eFireDriver.register(myListener); csa = new CustomSoftAssert(eFireDriver); } @Test public void sampleAssertFailureTest() throws IOException { eFireDriver.get("https://www.twitter.com/login"); eFireDriver.manage().window().maximize(); eFireDriver.findElement( By.name("session[username_or_email]")).clear(); eFireDriver.findElement( By.name("session[username_or_email]")).sendKeys("TestingDocs.com"); WebDriverWait wait = new WebDriverWait(eFireDriver, Duration.ofSeconds(120, 1)); wait.until(ExpectedConditions .titleContains("Twitter")); csa.assertEquals("BogusTitle", eFireDriver.getTitle(), "AssertTwitterLoginPage"); csa.assertAll(); } @AfterSuite public void cleanUp() { eFireDriver.close(); } }
By running the above code, you can capture a screenshot of the failed assert. The screenshot filename will be created with the Assert message as shown in the above code snippet.
Custom Listener to paint the web elements found by the Selenium web tests.
package com.testingdocs.testng.tutorial; /************************************************* * MyCustomizationListener.java is-A * AbstractWebDriverEventListener adds implementation * to beforeFindBy() paints web element with * green color. ************************************************/ import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.events. AbstractWebDriverEventListener; public class MyCustomizationListener extends AbstractWebDriverEventListener { public void beforeFindBy(By by, WebElement element, WebDriver driver){ WebElement webelement = driver.findElement(by); if (driver instanceof JavascriptExecutor) { ((JavascriptExecutor)driver) .executeScript("arguments[0].style.border='10px solid green'", webelement); } } }
Common mistakes
Debugging why the screenshots are not created when tests and assert fails. Below are some of the common mistakes or pitfalls.
#Use Latest TestNG
Use the updated TestNG framework. TestNG testing framework might also have defects. So make sure you are using the updated and latest version of the tool. Remove deprecated code from your tests. Update your tests with the latest Selenium code.
#Use proper imports
Use of proper imports and dependencies in the tests. Screenshot code needs Webdriver driver object and file handling utilities.
For example:
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
POM dependency:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency>
Test exceptions like TimeOut, SocketException exceptions fail fast and do not make the assert validations.
#Illegal screenshot filenames
The above code appends the Assert message to the screenshot file. This might fail if the assert message has some illegal character that is not allowed by the operating system filenames. The test will encounter InvalidPathException.
For example: if the assert message is Assert -> will fail.
sampleAssertFailureTestjava.nio.file.InvalidPathException: Illegal char <>> at index 23:
AssertFailure_Assert1 -> will fail.jpg at
java.base/sun.nio.fs.WindowsPathParser
.normalize(WindowsPathParser.java:182)
at java.base/sun.nio.fs.WindowsPathParser
.parse(WindowsPathParser.java:153)
at java.base/sun.nio.fs.WindowsPathParser
.parse(WindowsPathParser.java:77)
at java.base/sun.nio.fs.WindowsPath
.parse(WindowsPath.java:92)
at java.base/sun.nio.fs.WindowsFileSystem
.getPath(WindowsFileSystem.java:229)
at java.base/java.io.File.toPath(File.java:2311)
Test Listener
We can mix this capability with Test listening to highlight web elements in the screenshots.
For more information:
https://www.testingdocs.com/eventfiringwebdriver/
https://www.testingdocs.com/questions/how-to-highlight-web-elements-on-the-page-in-selenium-tests/
Consider options for capabilities to capture every assert life cycle method. Try mixing the assert lifecycle hooks along with SoftAssert capability to listen to every assert in the @Test annotated method. You can now enhance these custom capabilities & hook them into the TestNG framework for enhanced, robust visual logging to the framework. In conclusion, we’ve created a powerful new capability to give greater control over the test runs.