【Selenium】テストFail時にスクリーンショットを保存する方法

はじめに

 Seleniumでは、テストがFailになると、実際にその操作を行って画面がどうなっているか確かめるようなフローで運用を行うことがあります。

 1回あたりのテストでFailとなるようなテストケースが数件であれば大した問題はないのですが、Failの件数が10件を超えてきたあたりから、実際に画面を確認するという作業が非常に煩雑になってきます。

 今回は、エラーが発生した状況をスクリーンショットに残し、すぐに確認できるようにすることで、「画面を確認しにいく」という工数を削減します。

getScreenshotAsメソッドを利用する

 Seleniumで使うWebDriverインターフェースは、getScreenshotAsメソッドを備えていませんから、TakeScreenshot型にキャストしてから利用します。

File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
String screenshotBase64 = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64);

 参考:TakesScreenshot

 ファイルを出力する場合は、ApacheCommonsのFileUtilsを使うのが簡単です。

FileUtils.copyFile(screenshotFile, new File("/path/to/file.png"));

 単純にスクリーンショットを撮るだけならこれで完結します。

スクリーンショットをTestNGのReporterで出力する

 今回はTestNGのReporter機能を利用して、Test Reportのhtmlファイルから直接画像を見られるようにしてみます。

Base64で出力する

 環境などに依存することなく、簡単に出力できる方法がBase64エンコード文字列をHTMLに埋め込む方法です。

String screenshotBase64 = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64);
Reporter.log("<img src='data:image/png;base64," + screenshotBase64 + "'>");
画像サイズを調整したい場合は、style=’height:80vh; weight:auto;’などのスタイル要素を加えることで自在に変更できます。

 これで、生成されたHTMLの「Reporter output」メニューから画像を閲覧することができます。

ファイルで出力する

 上記のBase64エンコードの方法では、セキュリティ上の理由によりaタグやwindow.openなどで遷移することができず、いわゆるLightBoxなどを用いて拡大する方法しかありません。(-> Lightbox using tags or <a href=[base64]>

 なので、aタグを利用して拡大を行いたい場合は、ファイルを出力してリンクを貼る方法があります。

File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
String uuid = UUID.randomUUID().toString();
String destFilePath = "/path/to/" + uuid + ".png"
FileUtils.copyFile(screenshotFile, new File(destFilePath));
Reporter.log("<a href='" + destFilePath + "');

 ファイル名は重複しないようにタイムスタンプなどで生成します。例のようにUUIDなどを使っても特に問題はありません。

テスト失敗(Fail)時に出力する

 TestNGでは、TestListenerAdapterを継承したリスナクラスを作成し、イベントに対応したメソッドをオーバーライドすることで処理を追加できます。

リスナクラスを作成する

 テスト失敗用のメソッドであるonTestFailureをオーバーライドして、先ほどの処理を追加します。

Base64でそのまま出力する場合

public class ScreenshotListener extends TestListenerAdapter{
  @Override
  public void onTestFailure(ITestResult itr){
    // テストが成功している場合は出力しない
    if(itr.isSuccess()){
      return;
    }
    String screenshotBase64 = ((TakesScreenshot) AnyDriverHolder.getDriver()).getScreenshotAs(OutputType.BASE64);  // driverは管理クラスや保持クラスなどから取ってくる
    Reporter.log("<img src='data:image/png;base64," + screenshotBase64 + "'>");
  }
}

ファイルに出力する場合

public class ScreenshotListener extends TestListenerAdapter{
  @Override
  public void onTestFailure(ITestResult itr){
    // テストが成功している場合は出力しない
    if(itr.isSuccess()){
      return;
    }

    File screenshotFile = ((TakesScreenshot) AnyDriverHolder.getDriver()).getScreenshotAs(OutputType.FILE);  // driverは管理クラスや保持クラスなどから取ってくる
    String uuid = UUID.randomUUID().toString();
    String destFilePath = "/path/to/" + uuid + ".png"
    FileUtils.copyFile(screenshotFile, new File(destFilePath));
    Reporter.log("<a href='" + destFilePath + "');
  }
}

testng.xmlに定義する

 xml上の適用させたいsuite配下に、先ほど作成したListenerを使うように定義します。

<suite name="example">
  <listeners>
    <listener class-name="package.to.ScreenshotListener"></listener>
  </listeners>
</suite>

 あとはこのxmlファイルを読み込んでテストを実行すれば、failure時に自動的にスクリーンショットが掲載されます。

まとめ

 テスト自動化は、人間がやっていた作業を機械にやらせることで、テスト工数の大幅な削減が期待できます。今回のように、業務上少しでも自動化できそうな要素を見つけたら、自動化を検討してみることをお勧めします。