Community in read only mode June 18 & 19
This community will be set in READ ONLY mode for a while on Tuesday June 18 into Wednesday June 19 while we import content and users from our Micro Focus Forums community site. MORE INFORMATION

ScreenshotOnError in SilkTest 4Test, NUnit and JUnit

ScreenshotOnError in SilkTest 4Test, NUnit and JUnit

Writing SilkTest scripts for automated GUI-level tests is like writing a unit test. Basically you can use any testing framework as long as you can use the SilkTest Java/.Net API. You are only limited to a Java or .Net Runtime for the test execution. The key difference between unit testing and GUI-level testing is if you have a failing test. Most of the current xUnit frameworks have very good verification logic and show the problem with detailed information. But  this might not be enough for GUI-level tests. Sometimes it would be much easier to find the problem if you knew what was happening on the screen at the moment of the failing verification. We have the option to replay the test again and look at it, or we can try to get a screen capture of the moment we are interested in. The following is an introduction to a simple ‘ScreenshotOnError’ feature for JUnit, NUnit and 4Test. I’m sure the idea can be used in other testing frameworks, too.

JUnit

This sample is based on the JUnit framework 4.10.x. You can build a ‘ScreenshotOnError’ feature on previous versions too, but you need another approach (at the end there is a hint how that could work). Version 4.8.x JUnit introduced Rules [1]. Rules are like extension points where you can bring in some new logic (look at [2] to get an idea). We use an already implemented rule, the TestWatcher, for our problem. TestWatcher provides some methods we can override. We focus on the failed(..) method for our experiment. This method gets called every time a test fails, or more concrete, if a method annotated with @Test throws an exception. So this is the perfect place where to implement some lines of java code to get a screenshot. But let’s summarize what we have until now.

public static class SomeTest {
  @Rule
  public TestRule watchman= new TestWatcher() {
    @Override
    public void failed(Throwable e, Description description) {
      // TODO: implement the screenshot logic here
    }
  };
 
  @Test
  public void fails() {
    fail();
  }
 
  @Test
    public void succeeds() {}
}

We have a test class implementing some test methods (annotated with @Test). And we have introduced a new Rule based on the existing TestWatcher.
Now we need to implement some code which takes a picture of the current screen.

@Override
public void failed(Throwable e, Description description) {
  try {
    GraphicsEnvironment graphicsEnvironment =
        GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice screenDevice =
        graphicsEnvironment.getDefaultScreenDevice();
    Robot robot = new Robot(screenDevice);
    GraphicsConfiguration defaultConfiguration =
        screenDevice.getDefaultConfiguration();
    Rectangle bounds = defaultConfiguration.getBounds();
    bounds.setLocation(0, 0);
    BufferedImage screenCapture = robot.createScreenCapture(bounds);
    File bitmapFile = new File(„aRealyGoodName.png“);
    ImageIO.write(screenCapture, "png", bitmapFile);
  } catch (Throwable t) {
    // TODO: do some error handling
  }
}

Now we have a picture of the failed test, but we have only a picture of the default screen (what if there is more than one screen?), and the name of the picture should be improved of course.
To get a picture of each screen we should look at the documentation of GraphicsDevice [3]. So instead of getting the default screen we ask for an array of all screens and iterate over this array to take a picture of each screen device (The final code sample will implement this).
Finding a better name for the taken picture to associate the taken picture with the failing test is not difficult. In the method parameter ‘Description description‘ the framework provides us enough information about the failing test method to find a good name for our pictures.

NUnit

In NUnit we can implement the same idea but with a different approach. First of all we should define the requirement of at least NUnit version 2.5.10.
NUnit has a nice Addin feature which we can use to extend the core functionality with our screenshot on error idea. For more detailed information how the Addin mechanism works please read [4]. For our Addin we use the EventListener [5] extension point. Like in JUnit we get some call back methods. We can override especially the TestFinished(..) method. This method is called by the core framework after every test method, so we have a perfect place to bring in our screen capture code. This is how it should look like:

namespace ScreenshotOnErrorAddin {
  [NUnitAddin(Name = "Screenshot on Error Addin", Description = "This Addin takes a screenshot if a test fails or has an error.")]
  public class ScreenshotOnErrorAddin : IAddin, EventListener {

    #region IAddin Members

    public bool Install(IExtensionHost host) {
      IExtensionPoint listeners = host.GetExtensionPoint("EventListeners");
      if (listeners == null)
        return false;

      listeners.Install(new ScreenshotOnErrorAddin());
      return true;
    }

    #endregion

    #region EventListener Members
    public void TestFinished(TestResult result) {
        // TODO: put screen capture code here
      }
    }
// all other methods introduced by EventListener extension point need to be
// implemented with an empty code block
    #endregion
  }
}

The Addin needs to be registered in the core framework. This is what the Install(..) method does. If you place our compilation unit to the addins folder of your NUnit framework it should already work .
To make some screen captures we finally need to implement the TestFinished(..) method.

public void TestFinished(TestResult result) {
    if (result.ResultState == ResultState.Error
        || result.ResultState == ResultState.Failure) {
      Screen screen = Screen.PrimaryScreen;
      Bitmap bmp = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
      Graphics graphics = Graphics.FromImage(bmp);
      graphics.CopyFromScreen(0, 0, 0, 0, new Size(screen.Bounds.Width, screen.Bounds.Height));
      bmp.Save(result.FullName + ".png", ImageFormat.Png);
    }
  }

First of all we need to check the result if the test fails (this is the difference to JUnit) and then we take a screen capture and store it to a file. Similar to the JUnit approach we have the problem that we take only a picture of the primary screen. Looking into the MSDN [6] we find a solution for more screens if we use Screen.AllScreens and iterate over this array.

4Test

Similar to the previous test runner technologies the 4Test runner also has the possibility to add logic on the end of each test. To get an idea what we need to do we look in the documentation for ‘Overriding the default recovery system’. We need an INC file where we can place the new logic. Basically we need to define the ScriptExit function.

void ScriptExit(BOOLEAN bException)
  if (bException)
    window wCapture = DesktopOA
    String testName = GetTestCaseName()
    withoptions
      Agent.SetOption (OPT_BITMAP_MATCH_COUNT, 0)
    Agent.SetOption (OPT_BITMAP_PIXEL_TOLERANCE,0)
    Agent.SetOption(OPT_BITMAP_MATCH_INTERVAL,0)
    
    STRING dirPath = “C:\bitmaps”
    wCapture.CaptureBitmap("{dirPath}\{sBitmapName}")

To activate the feature it is necessary to use the newly introduced INC file in the test scripts.

Attached are the sources for the JUnit part (based on JUnit 4.10) and for the NUnit part (based on NUnit 2.5.10). Please change the .txt file extension to .java or .cs.

JUnit part
NUnit part

[1] http://kentbeck.github.com/junit/javadoc/latest
[2] http://java.dzone.com/articles/writing-your-own-junit
[3] http://download.oracle.com/javase/6/docs/api/index.html
[4] http://www.nunit.org/index.php?p=nunitAddins&r=2.5.10
[5] http://www.nunit.org/index.php?p=eventListeners&r=2.5.10
Devil http://msdn.microsoft.com/en-us/library/system.windows.forms.screen.aspx

DISCLAIMER:

Some content on Community Tips & Information pages is not officially supported by Micro Focus. Please refer to our Terms of Use for more detail.
Version history
Revision #:
1 of 1
Last update:
‎2012-06-21 18:37
Updated by:
 
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.