Create screenshots on failure for Java / TestNG based projects


#1

Starting from Ranorex Webtestit version 1.1.0, Create screenshot on failure is a built-in feature!

Taking a screenshot

First of all, we need to add a new dependency to our pom.xml file, which can be found at the root level of our project. Here we just add following lines to the <dependencies> section to add commons-io:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>

Then, in order to take a screenshot, we navigate to src\java\TestNGTestBase.java and add a new method takeScreenshot. After the screenshot has been taken the method saves it to the path we define in there.

public void takeScreenshot(String fileName) {
  try {
    File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    String path = String.format("reports%1$sscreenshots%1$s%2$s.jpg", File.separator, fileName);
    FileUtils.copyFile(scrFile, new File(path));
  } catch (IOException e) {
    e.printStackTrace();
  }
}

In order to get our method working, we have to import the required classes, and that is easily done with the help of quick fix option. Simply select the red underlined (“File”) and you will notice a light bulb symbol at the beginning of the line.
44
Clicking on it opens a list of quick fixes. Select the import 'File'(Java.io) to import the required package. Same scenario is applicable for importing the org.openqa.selenium.TakesScreenshot , 'FileUtils'(org.apache.commons.io) and any other classes that are indicated (underlined) and required.

Add a custom listener and take screenshots on failure

Now we are able to take a screenshot, but we just want to capture the moments of failure. Therefore, we add a new java file to the package folder in order to create a custom TestNG listener. This class should implement the IResultListener2 interface and in this sample it is called TestNgCustomListener. Sometimes, re-opening the project is required, so that the changes take effect.

package uitest;

import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.internal.IResultListener2;

public class TestNgCustomListener implements IResultListener2 {
  ...
}

Import the unimplemented methods, and in the method onTestFailure we will just add one line, which prints out the path to the screenshot and we add a method getFilePath. Note that the string starts and ends with a delimiter (we can choose any delimiter here, but need to use the same one when adding the screenshot to the report) which we will then need to filter out the path as other code might also print to system-out.

@Override
public void onTestFailure(ITestResult result) {
    System.out.println(getFilePath(result));
}

private String getFilePath(ITestResult result) {
    try {
        SuiteConfiguration config = new SuiteConfiguration();
        DesiredCapabilities capabilities = (DesiredCapabilities) config.getCapabilities();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String testDate = df.format(result.getTestContext().getStartDate());
        String fileName = String.format("%s-%s-%s", capabilities.getBrowserName(), result.getName(), testDate);
            
        return String.format("%1$sreports%2$sscreenshots%2$s%3$s.jpg%1$s", "++++", File.separator, fileName);
    } catch (IOException e) {
        e.printStackTrace();
        return result.getName();
    }
}

To include our custom listener, we need to modify the pom.xml file and add a property which tells TestNG about the listener.

<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.21.0</version>
    <configuration>
      ...
      <properties>
          <property>
              <name>listener</name>
              <value>uitest.TestNgCustomListener</value>
          </property>
      </properties>
      ...
    </configuration>
</plugin>

If you now execute a failing test case you should see the screenshot file path in the log panel:

And when inspecting the generated report file with an editor, the path can now be found inside the <system-out> tag.
image

The only thing left is to take a screenshot on failure. We again navigate to TestNgTestBase.java and add a method takeScreenshotOnFailure with the @AfterMethod annotation.

@AfterMethod
public void takeScreenshotOnFailure(ITestResult result) throws IOException {
    if (result.getStatus() == ITestResult.FAILURE) {
        takeScreenshot(getFileName(result));
    }
}

private String getFileName(ITestResult result) throws IOException {
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    String testDate = df.format(result.getTestContext().getStartDate());
    return String.format("%s-%s-%s", capabilities.getBrowserName(), result.getName(), testDate);
}

Adjust the Webtestit report

Finally, we have everything set and are ready to adjust the report template to actually show the screenshots inside the report.
Webtestit Reports are html templates (handlebars), which can be fully customized by yourself. Take a look at this in depth tutorial.

In order to customize your reports create a folder templates/report/partials inside our root folder. Open the templates folder via “menu → templates → open templates folder” which will open your finder/explorer in the location of the original templates.

Copy the file templates/report/partials/test-case.hbs over into your newly created partials folder. This will override the default template for the test-case rendered repeater of the report.
image

Here we can now add a new table row in the {{# if expanded}} section beneath the trace message. The findDelimitedString helper takes the inner text of the <system-out> and as a second argument a delimiter and returns the string between two of those delimiters.

{{#if expanded}}
<tr class="test-report__testcase-failed">
  <td colspan="5" class="test-report__trace"><pre>{{failure.trace}}</pre></td>
</tr>
{{!-- add following lines to include the screenshot in the report --}}
<tr class="test-report__testcase-failed">
  <td colspan="5" class="test-report__message">
    <br />
    <img alt="{{@root.projectRoot}}/{{findDelimitedString (extraElementValue extraElements 'system-out') '++++'}}"
         style="max-width: 500px"
         src="{{@root.projectRoot}}/{{findDelimitedString (extraElementValue extraElements 'system-out') '++++'}}" />
  </td>
</tr>

Then just save this file and the failed test cases in your reports should show a screenshot.
For example, if your test fails on some assertion step, screenshot will be captured point where the assertion failed, and shown in your report on the point where the assertion failed.

Conclusion

You are now able to take screenshots on failure and also include them in your reports. However, you can access the method takeScreenshot from class TestNgTestBase in all your test files meaning you can take a screenshot not only on failure but also at certain test points.

And as already mentioned before, for more details on customizing your report look here and if you want to know how to make screenshots for TypeScript / Protractor based project you might want to check this out.


Including the screenshot on failure out of the box
Screenshot on failure :Online documentation need to be improved
New dependencies are not added when pom.xml file is saved
Screenshot on failure :Online documentation need to be improved
Screenshot on failure :Online documentation need to be improved
How to access endpoint information from within tests
Release notes v1.1.0
Full webpage is not captured in takeScreenshot method
How to add parameter information to report
Sample report with screenshot on failure shall be shown at the end of the documentation
Screenshot is not shown in the report on failure (Java) is NOT working
Online documentation review feedback : Create screenshots on failure for Java
listed #2