LoginSignup
3
4

More than 5 years have passed since last update.

Seleniumで自動テストが失敗した時にハードコピーを取得する

Posted at

webアプリのテストをselenium+Jenkinsで自動化していますが、seleniumのエラーレポートだけだと何が原因でエラーになったのか判断するのが難しいです。
そこで、テストが失敗した時に自動で画面のハードコピーを取得するようにしました。

まず、TestWatchクラスを継承したクラスを作成します。
public class SeleniumTestWatcher extends TestWatcher

静的初期化子を使って、テスト起動時に画像格納用のフォルダを作成するようにします。
Jenkinsから起動した場合とローカルでテストした場合で格納先を分けており、
Jenkinsから起動した場合はSystem.getProperty("BUILD_NUMBER"); でjenkinsのビルド番号を取得しています。

private WebDriver driver;

private static String imgpath;

static {

    if(imgpath == null) {
        File jenkins = new File("C:\\Program Files (x86)\\Jenkins");
        // for server
        if(jenkins.exists()) {
            String buildnum = System.getProperty("BUILD_NUMBER");    
            imgpath = "【Jenkinsのworkspace】\\target\\error-img\\BUILD" + buildnum + "\\";
        // for local
        } else {
            Date date = new Date();
            Format fmt = new SimpleDateFormat("yyyyMMdd_HHmmss");
            String time = fmt.format(date);
            imgpath = "C:\\temp\\" + time + "\\";
        }
        File dir = new File(imgpath);

        // フォルダがなかったら作成する
        if (!dir.exists()) {
            dir.mkdir();
        }

    }
}

failedメソッドをオーバーライドして、テストがfailになった時にスクリーンショットを撮るようにします。

// 失敗したときはスクリーンショットを撮る
@Override
protected void failed(Throwable e, Description description) {
    super.failed(e, description);
    String[] classname = description.getClassName().split("\\.");
    try {
        capAll(String.format("%s_%s",
                  classname[classname.length - 1], description.getMethodName()));
    } catch (Exception e2) {
        e2.printStackTrace();
    }
}

スクリーンショットを取得するメソッドはこちら。
エラー等でアラートが出ているとスクリーンショットが撮れないのでアラートを閉じてから撮影するようにしています。

/**
 * screenshotを取得する
 * @param title
 * @throws WebDriverException
 * @throws IOException
 */
public void capture(String title) throws WebDriverException, IOException {

    WebDriverWait wait = new WebDriverWait(driver, 3);
    try {
        Alert alert = wait.until(ExpectedConditions.alertIsPresent());
        alert.accept();
    } catch (TimeoutException e) {
    }

    String path = "";
    path = imgpath + title +".png";

    File file = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(file, new File(path));

}

スクロールの発生するような画面で、画面全体のハードコピーを撮りたい場合はこちら。
自動でスクロールして一枚の画像として保存してくれます。
画面全体をスクロールする画面であればCSSセレクタは"body"でOK。
※JQueryを使う前提となっています。

/**
 * screenshotを取得する
 * @param title
 * @throws WebDriverException
 * @throws IOException
 */
public void capAll(String title) throws WebDriverException, IOException {

    driver.switchTo().defaultContent();
    TakesScreenshot ts = (TakesScreenshot) new Augmenter().augment(driver);

    //JS実行用のExecuter
    JavascriptExecutor jexec = (JavascriptExecutor) driver;

    final String SCROLLAREA = "【スクロールエリアのcssセレクタ】";

    // スクロールエリアのある画面
    String isContent = String.valueOf(jexec.executeScript("return $('" + SCROLLAREA + "').length"));
    if("1".equals(isContent)) {

        //画面サイズで必要なものを取得
        int innerH = Integer.parseInt(String.valueOf(jexec.executeScript("return $('" + SCROLLAREA + "').height()")));
        int innerW =Integer.parseInt(String.valueOf(jexec.executeScript("return $('" + SCROLLAREA + "').width()")));
        int scrollH = Integer.parseInt(String.valueOf(jexec.executeScript("return $('" + SCROLLAREA + "')[0].scrollHeight")));
        int scrollW = Integer.parseInt(String.valueOf(jexec.executeScript("return $('" + SCROLLAREA + "')[0].scrollWidth")));
        int windowH = Integer.parseInt(String.valueOf(jexec.executeScript("return window.innerHeight")));
        int windowW = Integer.parseInt(String.valueOf(jexec.executeScript("return window.innerWidth")));
        if(scrollW > innerW) {
            windowH -= 12; // scrollbarの分
            innerH -= 12;
        }
        if(scrollH > innerH) {
            windowW -= 12; // scrollbarの分
            innerW -= 12;
        }

        int headerH = windowH - innerH;
        int headerW = windowW - innerW;
        //イメージを扱うための準備
        BufferedImage img = new BufferedImage(headerW + scrollW, headerH + scrollH, BufferedImage.TYPE_INT_ARGB);
        Graphics g = img.getGraphics();


        int j = 0;
        int scrollableW = scrollW + innerW;
        // 横スクロールのループ
        while(scrollableW > innerW){
            scrollableW -= innerW;
            int scrollableH = scrollH + innerH;
            int i = 0;
            // 縦スクロールのループ
            while(scrollableH > innerH){
                scrollableH -= innerH;
                // スクリーンショットを取得
                BufferedImage imageParts = ImageIO.read(ts.getScreenshotAs(OutputType.FILE));

                if(i == 0 && j == 0) {
                    // 1枚目は画面をそのまま貼る
                    g.drawImage(imageParts, windowW * j, windowH * i, null);
                } else if(scrollableH <= innerH && scrollableW <= innerW) {
                    // 縦、横ともに最後は右下を埋めるように貼り付け
                    g.drawImage(imageParts, headerW + innerW * j, headerH + innerH * i, headerW + innerW * (j + 1), headerH + innerH * (i + 1),
                            windowW-scrollableW, windowH - scrollableH, windowW, windowH, null);
                } else if(scrollableH <= innerH) {
                    // 縦スクロールの最後は下から埋めるように貼り付け
                    g.drawImage(imageParts, headerW + innerW * j, headerH + innerH * i, headerW + innerW * (j + 1), headerH + innerH * (i + 1),
                            headerW, windowH - scrollableH, windowW, windowH, null);
                } else if(scrollableW <= innerW) {
                    // 横スクロール最後は右から埋めるように貼り付け
                    g.drawImage(imageParts, headerW + innerW * j, headerH + innerH * i, headerW + innerW * (j + 1), headerH + innerH * (i + 1),
                            windowW-scrollableW, headerH, windowW, windowH, null);
                } else {
                    g.drawImage(imageParts, windowW * j, windowH * i, null);
                    // 途中の場合はscrollした分だけ貼る
                    //g.drawImage(imageParts, headerW + innerW * j, headerH + innerH * i, headerW + innerW * (j + 1), headerH + innerH * (i + 1),
                    //      headerW, headerH, windowW, windowH, null);
                }

                // 縦に1画面分スクロール
                i++;
                jexec.executeScript("$('" + SCROLLAREA + "').animate({scrollTop:" + innerH * i + "});");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }

            }        

            // 縦スクロールが終わったら1回右にスクロールしてまた縦スクロール
            j++;
            jexec.executeScript("$('" + SCROLLAREA + "').animate({scrollTop: 0, scrollLeft:" + innerW * j + "});");     
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }

        }

        // 画像をファイルに出力する
        String path = imgpath + title +".png";
        ImageIO.write(img, "png", new File(path));

    } else {
        capture(title);
    }

}

TestWatcherクラスが出来上がったら、
テストクラス内で上記で作成したクラスを@Ruleアノテーションをつけてインスタンス化します。

@Rule
public SeleniumTestWatcher watcher = new SeleniumTestWatcher();

すると、JUnitのテスト失敗時に日付_時刻フォルダの下に失敗時のハードコピーが保存されるようになります。
WS000016.jpg

Jenkinsで「ビルド後の処理」の「成果物を保存」に以下のように書いておくとJenkins上から画像を確認できるようになります。

target/error-img/BUILD${BUILD_NUMBER}/*.png

WS000017.jpg

「ビルドの成果物」に保存した画像が表示されるようになります。
WS000018.jpg

クラス全体は一応githubに置いてあります。
https://github.com/widora99/seleniumTest

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4