Appium - Capture Screenshot On Failure Or Pass In Android Test Automation

Earlier In previous post, We learnt how to capture screenshot of android mobile software app screen on any stage In android appium software automation test. Now supposing I wants to capture screenshot on test failure or I wants to capture screenshot on software test pass. In this case,  ITestListener Interface of TestNG can help us to capture screenshot on certain occurrence like test failure or software test pass.

ITestListener Interface
ITestListener Is an Interface of TestNG which can Invoke it's specific method on test failure or test pass or test skips, ect.. There are bellow given useful methods available In ITestListener Interface.
  • onFinish(ITestContext context) - Invoked after all the software tests have run.
  • onStart(ITestContext context) -  Invoked after the test class is instantiated.
  • onTestFailure(ITestResult result) - Invoked each time a test fails.
  • onTestSkipped(ITestResult result) - Invoked each time a test is skipped.
  • onTestStart(ITestResult result) -  Invoked each time before a test will be invoked.
  • onTestSuccess(ITestResult result) - Invoked each time a test succeeds.
You can view more detail on ITestListener Interface on THIS PAGE. Earlier we have used It In data driven framework. View THIS PAGE for more detail.

App To Use Capture Screenshot Test
We will use android mobile's calculator app In this software automation test example.

Aim to Achieve
We wants to capture screenshot on test pass or failure occurrence and store file in related folder. If test Is fail then capture screenshot and store It In Failures folder and If test Is pass then capture screenshot and store It In Pass folder under screenshots folder of your project as shown in bellow Image.


How To Do
We will create separate class file to write logic of capture screenshot on test fail or pass. Inside It, We will create separate method captureScreenShot to write logic to capture screenshot. We will use testng assertion to verify our software test is fail or pass and use ITestListener Interface to Invoke onTestFailure method on assertion failure and onTestSuccess on assertion pass and then call captureScreenShot method to take screenshot.

In test class file, We will write two test methods TestForFailure() and TestForPass(). Test method TestForFailure() Is written In such a way to fail It Intentionally.

Screenshot of TestForFailure() method will be stored Inside Failure folder and screenshot of TestForPass() method will be stored Inside Success folder.

Create And Run Test
I have created very simple example to demonstrate you how to capture screenshot on test failure or pass. You need to create two class files under Android package of your project as bellow.

ScreenshotUtility.java file contains logic to capture screenshot on test failure or pass.

ScreenshotUtility.java
package Android;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class ScreenshotUtility implements ITestListener {
 // This method will execute before starting of Test suite.
 public void onStart(ITestContext tr) {

 }

 // This method will execute, Once the Test suite is finished.
 public void onFinish(ITestContext tr) {

 }

 // This method will execute only when the test is pass.
 public void onTestSuccess(ITestResult tr) {
  captureScreenShot(tr, "pass");
 }

 // This method will execute only on the event of fail test.
 public void onTestFailure(ITestResult tr) {
  captureScreenShot(tr, "fail");
 }

 // This method will execute before the main test start (@Test)
 public void onTestStart(ITestResult tr) {

 }

 // This method will execute only if any of the main test(@Test) get skipped
 public void onTestSkipped(ITestResult tr) {
 }

 public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {
 }

 // Function to capture screenshot.
 public void captureScreenShot(ITestResult result, String status) {
  // AndroidDriver driver=ScreenshotOnPassFail.getDriver();
  String destDir = "";
  String passfailMethod = result.getMethod().getRealClass().getSimpleName() + "." + result.getMethod().getMethodName();
  // To capture screenshot.
  File scrFile = ((TakesScreenshot) ScreenshotOnPassFail.driver).getScreenshotAs(OutputType.FILE);
  DateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy__hh_mm_ssaa");
  // If status = fail then set folder name "screenshots/Failures"
  if (status.equalsIgnoreCase("fail")) {
   destDir = "screenshots/Failures";
  }
  // If status = pass then set folder name "screenshots/Success"
  else if (status.equalsIgnoreCase("pass")) {
   destDir = "screenshots/Success";
  }

  // To create folder to store screenshots
  new File(destDir).mkdirs();
  // Set file name with combination of test class name + date time.
  String destFile = passfailMethod + " - " + dateFormat.format(new Date()) + ".png";

  try {
   // Store file at destination folder location
   FileUtils.copyFile(scrFile, new File(destDir + "/" + destFile));
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}


ScreenshotOnPassFail.java file contain android cal app test. If you see In bellow given test, We have used @Listeners annotation. It mansion that testng listeners are Implemented In ScreenshotUtility class file to listen.

ScreenshotOnPassFail.java
package Android;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners({ ScreenshotUtility.class })
public class ScreenshotOnPassFail {
 static AndroidDriver driver;

 @BeforeClass
 public void initialize() throws MalformedURLException {
  DesiredCapabilities capabilities = new DesiredCapabilities();
  capabilities.setCapability("deviceName", "ZX1B32FFXF");
  capabilities.setCapability("browserName", "Android");
  capabilities.setCapability("platformVersion", "4.4.2");
  capabilities.setCapability("platformName", "Android");
  capabilities.setCapability("appPackage", "com.android.calculator2");
  capabilities.setCapability("appActivity", "com.android.calculator2.Calculator");
  driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
  driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
 }

 // Method Is written In such a way to fail It Intentionally.
 // This method will fail as actual result 6 will not match with expected 7.
 @Test
 public void TestForFailure() throws IOException {
  // Click on DELETE/CLR button to clear result text box before running test.
  ((WebElement) driver.findElements(By.xpath("//android.widget.Button")).get(0)).click();
  // Click on 2, +, 4 and = buttons.
  driver.findElement(By.name("2")).click();
  driver.findElement(By.name("+")).click();
  driver.findElement(By.name("4")).click();
  driver.findElement(By.name("=")).click();
  // Get result from calc result textbox.
  String result = driver.findElement(By.className("android.widget.EditText")).getText();
  // Compare actual and expected result using testng assertion and mark test pass or fail based on result.
  // Assertion will fail. So It will call onTestFailure method from ScreenshotUtility.
  assert result.equals("7") : "Expected value : 7 did not match with Actual value: "+ result;
 }

 @Test
 public void TestForPass() throws IOException {
  // Click on DELETE/CLR button to clear result text box before running test.
  ((WebElement) driver.findElements(By.xpath("//android.widget.Button")).get(0)).click();
  // Click on 3, +, 4 and = buttons.
  driver.findElement(By.name("3")).click();
  driver.findElement(By.name("+")).click();
  driver.findElement(By.name("4")).click();
  driver.findElement(By.name("=")).click();
  // Get result from calc result textbox.
  String result = driver.findElement(By.className("android.widget.EditText")).getText();
  // Compare actual and expected result using testng assertion and mark test pass or fail based on result.
  // Assertion will pass. So It will call onTestSuccess method from ScreenshotUtility.
  assert result.equals("7") : "Expected value : 7 did not match with Actual value: " + result;
 }

 @AfterClass
 public void End() {
  driver.quit();
 }
}

Connect your device with PC and run above ScreenshotOnPassFail test using testng and appium.
  • TestForFailure() method : Here assertion will fail so onTestFailure method will be called from ScreenshotUtility.java. It will capture screenshot and store It in Failures folder under screenshots folder.
  • TestForPass() method : Here assertion will pass so onTestSuccess method will be called from ScreenshotUtility.java. It will capture screenshot and store It in Success folder under screenshots folder.
This Is the way to capture screenshot on failure or pass of any android appium software automation test.

2 comments:

  1. i am getting error on dateFormat.format(new Date())

    ReplyDelete
  2. i have implemented this code in my framework and its worked for me

    ReplyDelete