[JAVA] Add screen recording capabilities to Selenium test cases

Add screen recording capabilities to Selenium test cases

Recently, there are increasing opportunities to use it, such as autopilot with Selenium + Junit to automate various operations and pre-delivery test of Web applications, but especially the operation of services made by others. If you are automating with Selenium, you will not be able to identify the element even if you make a small change such as changing the page layout or modifying the typo, and the automatic operation will not continue in many cases. In such a case, usually, the same operation is performed while visually monitoring the screen again to check where the problem is occurring, but in some cases, it is a phenomenon that rarely occurs or is reproduced. I found the following article on Qiita when I thought that it would take time and that it would be convenient if I could record the state of the screen when the phenomenon actually occurred.

Record at runtime with Selenium (Selenide)

So, when I actually adopted the method described in this article, I was able to improve the efficiency of the correction work more than I expected, so I summarized the procedure for incorporating the screen recording function into my code.

Import the classes needed to record the screen

python


import java.io.File;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import org.junit.rules.TestName;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.monte.media.Format;
import org.monte.media.math.Rational;
import static org.monte.media.FormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;

Define methods and rules for screen recording

You can specify the folder to store the recorded file in the part of File file = new File ("record"); on the second line. Encoding and compressor when recording by Monte Media Library with the parameters ʻEncodingKey and CompressorNameKeygiven to the methodScreenRecorder` from the 14th line. You can specify the compression method). It seems that the Monte Media Library can use encodings and compressors as shown in the reference URL below, but I couldn't find a description of which combination is effective, so that is a trial and error process.

As a result of my own trial and error, it is best to use TechSmith Screen Capture Codec (ʻENCODING_AVI_TECHSMITH_SCREEN_CAPTURE) which is specified in the sample for both encoding and compressor. The size of the recorded file has been reduced, but to play the file recorded with ʻENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, use a dedicated player or [Additional codec] for QuickTime Player (https://www.techsmith.com/codecs.html) ) Is required to be installed.

Additional Codec for QuickTime Player (Tech Smith Screen Capture Codec) https://www.techsmith.com/codecs.html

JavaDoc page in Monte Media Library http://www.randelshofer.ch/monte/javadoc/index.html

When ʻENCODING_QUICKTIME_CINEPAKis specified as the encoding andCOMPRESSOR_NAME_QUICKTIME_CINEPAK` is specified as the compressor, the recorded file is created in a state where it can be played as it is with QuickTime Player of macOS, but the file is compared with the case of using TechSmith Screen Capture Codec. Since the size of is quite large, please devise the encoding and compressor specification in the right place.

python


  public void startRecording(String name) throws Exception {
    File file = new File("record");

    Dimension screenSize = driver.manage().window().getSize();
    int width = screenSize.width;
    int height = screenSize.height;

    java.awt.Rectangle captureSize = new java.awt.Rectangle(0,0, width, height);

    GraphicsConfiguration gc = GraphicsEnvironment
      .getLocalGraphicsEnvironment()
      .getDefaultScreenDevice()
      .getDefaultConfiguration();

    this.screenRecorder = new ScreenRecorder(gc, captureSize,
      new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_QUICKTIME),
      new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
        CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
        DepthKey, 24, FrameRateKey, Rational.valueOf(15),
        QualityKey, 1.0f,
        KeyFrameIntervalKey, 15 * 60),
      new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, "black",
        FrameRateKey, Rational.valueOf(30)),
      null, file, name);
    this.screenRecorder.start();
  }

  public void stopRecording() throws Exception {
    this.screenRecorder.stop();
  }

  private ScreenRecorder screenRecorder;

  @Rule
  public TestName testName = new TestName();

  @Rule
  public TestWatcher watchman = new TestWatcher() {

    @Override
    protected void succeeded(Description d) {
      System.out.println("I am succeeded() method. name -> " + d.getMethodName());
      //If you want to keep the recording file even if the test is successful, comment out the following line
      screenRecorder.getCreatedMovieFiles().stream().filter(a -> a.toPath().toString().contains(testName.getMethodName())).forEach(a -> a.deleteOnExit());
    }

    @Override
    protected void failed(Throwable th, Description d) {
      System.out.println("I am failed() method. name -> " + d.getMethodName());
      System.out.println(th.toString());
    }
  };

Added screen recording start method call to initialization process

python


  @Before
  public void setUp() throws Exception {

    driver = new FirefoxDriver();

    baseUrl = "https://console.firebase.google.com";
    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    startRecording(testName.getMethodName());
  }

python


  @After
  public void tearDown() throws Exception {
    //If you stop recording immediately after the test ends, the result may not be recorded to the end, so sleep
    Thread.sleep(5000);
    stopRecording();
    driver.quit();
    String verificationErrorString = verificationErrors.toString();
    if (!"".equals(verificationErrorString)) {
      fail(verificationErrorString);
    }
  }

Added repository and dependency of Monte Media Library to maven project file (pom.xml)

pom.xml


  <repositories>
      ...
    <repository>
      <id>additional-repo1</id>
      <name>Alfresco Public Repository</name>
      <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
    </repository>
  </repositories>

pom.xml


  <dependencies>
    <dependency>
       ...
    </dependency>
    <dependency>
      <groupId>org.monte</groupId>
      <artifactId>monte-screen-recorder</artifactId>
      <version>0.7.7</version>
    </dependency>
  </dependencies>

Added class for screenshots

By inheriting ScreenRecorder of Monte Media Library and overriding createMovieFile, the location and file name to output the recorded file are name + yyyy-MM-dd HH.mm.ss (date and time) under the folder specified by movieFolder. ) Has been changed.

ScreenRecorder.java


package net.hoge2.test;

import java.awt.AWTException;
import java.awt.GraphicsConfiguration;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.monte.media.Format;
import org.monte.media.Registry;

public class ScreenRecorder extends org.monte.screenrecorder.ScreenRecorder {
  private String name;

  public ScreenRecorder(GraphicsConfiguration cfg,
    Rectangle captureArea, Format fileFormat, Format screenFormat,
    Format mouseFormat, Format audioFormat, File movieFolder,
    String name) throws IOException, AWTException {
    super(cfg, captureArea, fileFormat, screenFormat, mouseFormat,
      audioFormat, movieFolder);
    this.name = name;
  }

  @Override
  protected File createMovieFile(Format fileFormat) throws IOException {
    if (!movieFolder.exists()) {
      movieFolder.mkdirs();
    }
    else if (!movieFolder.isDirectory()) {
      throw new IOException("\"" + movieFolder + "\" is not a directory.");
    }

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss");
    return new File(movieFolder,
      name + "-" + dateFormat.format(new Date()) + "."
      + Registry.getInstance().getExtension(fileFormat));
  }
}

I'm using the Monte Media Library to record screens while running test cases in Selenium, but this library does things like remote ssh login. If you try to execute the test case while it is in the state, an error will occur, so if you need to execute it by remote login, you need to comment out the relevant part.

Recommended Posts

Add screen recording capabilities to Selenium test cases
How to test file upload screen in Spring + Selenium
Add image selector to Selenium Grid
How to dynamically write iterative test cases using test / unit (Test :: Unit)