2

I'm using TestNG 6.8 + Selenium WebDriver 2.32 to test the GUI of a web app. In cases of failed tests I would like to take a screenshot of application GUI.

What I have:

  • I can detect test failure by implementing testng's IInvokedMethodListener
  • I also know how to use a webdriver to take a screenshot

What I need:

  • to get at my WebDriver instance declared in my AbstractGuiTest class to take a screenshot.

Here's a skeleton of my code:

import org.testng.annotations.Listeners;
...
@Listeners(GuiTestListener.class)
public abstract class AbstractGuiTest {
    protected WebDriver driver; //Used by all tests
    ...
}

And here's my test listener class, that reacts to failed tests:

public class GuiTestListener implements IInvokedMethodListener {
    @Override
    public void afterInvocation(IInvokedMethod method, ITestResult itr) {
        if (method.isTestMethod() && !itr.isSuccess()) {
            //Take a screenshot here. But how do I get at the intance of WebDriver declared in the AbstractGuiTest?
        }
    }
}

Can you please suggest a way how to get at the instance of WebDriver declared in the AbstractGuitTest, so that I can use it to take a screenshot in the GuiTestListener class?

dda
  • 6,030
  • 2
  • 25
  • 34
Jan Hrcek
  • 626
  • 11
  • 24
  • A possible solution that came to my mind was to create a static WebDriver field in GuiTestListener which I would set from AbstractGuiTest @BeforeClass setup method. But I was wondering if there was any more systemic solution.. – Jan Hrcek May 21 '13 at 09:49

3 Answers3

2

you can get it from ITestResult:

Object x = itr.getInstance();
AbstractGuiTest currentCase = (AbstractGuiTest)x;
WebDriver driver  = currentCase.getDriver();
Justin
  • 1,050
  • 11
  • 26
1

Your possible solution captures the idea well - there should be some place where WebDriver instance is shared between tests and listener. Two other solutions come to my mind, which were used on the projects I was involved in or I have read on the internet about.

  1. Sharing it through the Singleton "holder" class, like in this blogpost. It is about Groovy and Geb, but it gives the idea.

  2. Using dependency injection (e.g. guiceberry) to inject WebDriver in all the places where it is needed. There is an example in guiceberry tutorial.

duemir
  • 1,211
  • 9
  • 8
1

I have something simmilar, I need to take screenshots when a test fails with selenium. All my Test Classes extends AbstractTestNGSpringContextTests implements TestWithSeleniumDriver, they have annotation @Listeners(ScreenshotForFailedTestListener.class)

public interface TestWithSeleniumDriver {
    public RemoteWebDriver getDriver();
}

public class ScreenshotForFailedTestListener implements IInvokedMethodListener {


    @Override
    public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {

        // nothing
    }

    @Override
    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {

        if (method.isTestMethod() && ITestResult.FAILURE == testResult.getStatus()) {
            if (method.getTestResult().getInstance() instanceof TestWithSeleniumDriver) {
                TestWithSeleniumDriver instance = (TestWithSeleniumDriver) method.getTestResult().getInstance();
                RemoteWebDriver driver = instance.getDriver();
                if (driver != null) {
                    TestingUtils.captureScreen(driver, method);
                }
            }
        }

    }

}

public class TestingUtils {

    private static final Logger logger = LogManager.getLogger(TestingUtils.class);



    public static String captureScreen(RemoteWebDriver driver, IInvokedMethod method) {


        String path;
        try {
            Throwable throwable = method.getTestResult().getThrowable();
            String testClass = method.getTestMethod().getRealClass().getName();
            String packageName = method.getTestMethod().getRealClass().getPackage().getName();
            String shortClassName = method.getTestMethod().getRealClass().getSimpleName();
            String testMethod = method.getTestMethod().getMethodName();

            StackTraceElement stackTraceElement = null;
            for (StackTraceElement traceElement : throwable.getStackTrace()) {
                if (traceElement.getClassName().equals(testClass) && traceElement.getMethodName().equals(testMethod)) {
                    stackTraceElement = traceElement;
                    break;
                }
            }
            WebDriver augmentedDriver = new Augmenter().augment(driver);
            File source = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
            String drvName = "unknown";
            if (driver instanceof FirefoxDriver)
                drvName = "firefox";
            else if (driver instanceof ChromeDriver)
                drvName = "chrome";
            else if (driver instanceof OperaDriver)
                drvName = "opera";
                // else if ( driver instanceof AndroidDriver )
                // drvName = "android";
            else if (driver instanceof RemoteWebDriver)
                if (driver.getCapabilities().getBrowserName() != null)
                    drvName = driver.getCapabilities().getBrowserName();
            String day = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            String hour = new SimpleDateFormat("HH-mm-ss").format(new Date());
            if (stackTraceElement == null) {
                path = ("./target/screenshots/" + drvName + "/" + "failed" + "/" + //
                        day + "/" + //
                        packageName + "/" + shortClassName + "/" + testMethod + "_noLineInfo_" + //
                        driver.getCurrentUrl().replaceAll("https://", "").replaceAll("/", "_") + "_" + //
                        hour + ".png").replaceAll("__", "_");//
            } else {
                path = ("./target/screenshots/" + drvName + "/" + "failed" + "/" + //
                        day + "/" + //
                        packageName + "/" + shortClassName + "/" + testMethod + "_line-" + stackTraceElement.getLineNumber() + "_" + //
                        driver.getCurrentUrl().replaceAll("https://", "").replaceAll("/", "_") + "_" + //
                        hour + ".png").replaceAll("__", "_");//
            }

            FileUtils.copyFile(source, new File(path));
            String stackTrace = ExceptionUtils.getStackTrace(throwable);
            logger.error("===========================");
            logger.error("screnshot captured to : " + path);
            logger.error("===========================");
        } catch (IOException e) {
            path = "Failed to capture screenshot: " + e.getMessage();
        }
        return path;
    }
}

Code must be adapted a little for your needs

Radu Toader
  • 1,521
  • 16
  • 23
  • The solution with the interface is very clean and saves me at least some reflection calls. Thanks for the idea. – Christian.D Jan 28 '20 at 11:18