I wanted to include screenshots with my failed Selenium tests in the ReportNG output, but it was harder than I thought. I started with taking a screenshot in an @AfterMethod since I need access to the WebDriver to take the screenshot. The screenshot worked, but it was too late to add it to the report output since the @AfterMethod runs after the reporter listener. I eventually got it working by putting the driver into the test context, extending ReportNG's HTMLReporter to implement onTestFailure and take the screenshot, and specifying a different utils class that adds the screenshot to the report output.Some things that would have made it easier:
- Don't use the selenium-server-standalone jar. It includes an old (6.0.1) version of testng that doesn't have the getTestContext method on ITestResult. It conflicted with my newer testng jar that did have this method.
- I was surprised that @AfterMethod does not run immediately after the method, but after the reporter listeners. I know it's probably too late to change that, so what about a new annotation (@ImmediatelyAfterMethod?) that runs immediately after the test method, before any listeners?
- I'd like to be able to override the ReportNG templates easily. They are loaded directly from the jar file with no way to specify your own. If I could do that, I could add a new call $utils.getTestScreenshot($testResult) that would output the img tag for the screenshot without having to turn off escaping for all of the output. I noticed you can override the stylesheet (org.uncommons.reportng.stylesheet), so why not templates?
- The Velocity templates use a separate ReportNGUtils class to get their data. That made it harder to override, since I had to substitute my class in the createContext method, and the key (in AbstractReporter) was defined private.
- Figuring out where to put the output was a little messy. I got the output path from the test context in the onTestFailure method, but it included the TestNG suite directory, which ReportNG doesn't know about. The ReportNG HTMLReporter puts its files in subdirectory, which I couldn't access in my subclass because it was declared private.
Here are the details; I hope this helps someone.in test class, save driver into test context:@BeforeSuite(alwaysRun = true)public void setupBeforeSuite(ITestContext context) {driver = new FirefoxDriver();context.setAttribute(ScreenshotReportNGUtils.DRIVER_ATTR, driver);}extend ReportNG's HTML reporter: override createContext to use a custom utils class; implement onTestFailure from ITestListener to get the driver from the test context, take a screenshot, and save the screenshot filename and driver URL as result attributespublic class ScreenshotHTMLReporter extends HTMLReporter implements ITestListener {protected static final ScreenshotReportNGUtils SS_UTILS = new ScreenshotReportNGUtils();/* (non-Javadoc)* @see org.uncommons.reportng.AbstractReporter#createContext()* override to use a custom utils class*/protected VelocityContext createContext() {VelocityContext context = super.createContext();// UTILS_KEY is privatecontext.put("utils", SS_UTILS);return context;}public void onTestFailure(ITestResult tr) {ITestContext context = tr.getTestContext();WebDriver driver = (WebDriver)context.getAttribute("driver");try {File f = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);// output dir includes suite, so go up one levelString outputDir = tr.getTestContext().getOutputDirectory();outputDir = outputDir.substring(0, outputDir.lastIndexOf("/"));File saved = new File(outputDir, "ss_"+tr.getMethod().getMethodName()+".png");FileUtils.copyFile(f, saved);// save screenshot path as result attribute so generateReport can access ittr.setAttribute("screenshot", saved.getName());tr.setAttribute("screenshotURL", driver.getCurrentUrl());} catch (Exception e) {System.out.println("error generating screenshot: "+e);}}...}custom utils class that looks for screenshot info in result attributes and adds it to the output if foundpublic class ScreenshotReportNGUtils extends ReportNGUtils {public static final String DRIVER_ATTR = "driver";/* (non-Javadoc)* @see org.uncommons.reportng.ReportNGUtils#getTestOutput(org.testng.ITestResult)* override to add screenshot from result attribute*/public List<String> getTestOutput(ITestResult result) {List<String> output = super.getTestOutput(result);// add screenshot if there is oneString screenshot = (String)result.getAttribute("screenshot");if (screenshot != null) {String url = (String)result.getAttribute("screenshotURL");if (url == null)url = "";// ReportNG output directory is private, so get screenshot from output rootoutput.add("screenshot for "+result.getName()+" "+url+"<br><img src=\"../"+screenshot+"\">");}return output;}}in ant build file
- replace ReportNG HTMLReporter listener with my new ScreenshotHTMLReporter
- add <sysproperty key="org.uncommons.reportng.escape-output" value="false" /> to prevent escaping of img tag
--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/testng-users/-/t_2Ks7iq-PQJ.
To post to this group, send email to testng...@googlegroups.com.
To unsubscribe from this group, send email to testng-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/testng-users?hl=en.
To view this discussion on the web visit https://groups.google.com/d/msg/testng-users/-/GLCngFk4DEwJ.
Kimberly,
Can you attach the method you created? I am successfully taking the screenshots, but I cant get them to show up in my HTML report- on the location is showing up in the log.
"I added a Reporter.log method with a boolean parameter to skip the HTML escaping. I can upload that to github if you want."
Thank you so much for the info!
Hi Cedric,
File f = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
The first part of question could be addressed by accessing following menu - Run -> Run|Debug Configuration -> Your Configuration -> 'Arguments' tab -> VM arguments. Add following line to the text area:
-Dorg.uncommons.reportng.escape-output=false
Thanks so much to Kimberly and everyone for the great example!