web-dev-qa-db-ja.com

Espressoでテストが失敗した時点でスクリーンショットを撮る方法は?

テストが失敗した後、閉じる前にデバイスのスクリーンショットを撮る方法を探しています。

11
think_better

私が見つけた最も簡単な方法:

@Rule
public TestRule watcher = new TestWatcher() {
  @Override
  protected void failed(Throwable e, Description description) {
    // Save to external storage (usually /sdcard/screenshots)
    File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
        + "/screenshots/" + getTargetContext().getPackageName());
    if (!path.exists()) {
      path.mkdirs();
    }

    // Take advantage of UiAutomator screenshot method
    UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    String filename = description.getClassName() + "-" + description.getMethodName() + ".png";
    device.takeScreenshot(new File(path, filename));
  }
};
15
Kevin Brotcke

以前の回答に対する別の改善。私は実験的なものを使用しています スクリーンショットAPI

public class ScreenshotTestRule extends TestWatcher {

  @Override
  protected void failed(Throwable e, Description description) {
    super.failed(e, description);

    takeScreenshot(description);
  }

  private void takeScreenshot(Description description) {
    String filename = description.getTestClass().getSimpleName() + "-" + description.getMethodName();

    ScreenCapture capture = Screenshot.capture();
    capture.setName(filename);
    capture.setFormat(CompressFormat.PNG);

    HashSet<ScreenCaptureProcessor> processors = new HashSet<>();
    processors.add(new CustomScreenCaptureProcessor());

    try {
      capture.process(processors);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

BasicScreenCaptureProcessor/ sdcard/Pictures /フォルダーを使用し、IOExceptionsが発生したため、CustomScreenCaptureProcessorを作成しましたフォルダ/イメージを作成するときのいくつかのデバイス。プロセッサを同じパッケージに入れる必要があることに注意してください

package Android.support.test.runner.screenshot;

public class CustomScreenCaptureProcessor extends BasicScreenCaptureProcessor {    
  public CustomScreenCaptureProcessor() {
    super(
        new File(
            InstrumentationRegistry.getTargetContext().getExternalFilesDir(DIRECTORY_PICTURES),
            "espresso_screenshots"
        )
    );
  }
}

次に、ベースのEspressoテストクラスに追加するだけです

@Rule
public ScreenshotTestRule screenshotTestRule = new ScreenshotTestRule();

保護されたフォルダーを使用したい場合、これはエミュレーターでトリックを実行しましたが、物理デバイスでは機能しませんでした

@Rule
public RuleChain screenshotRule = RuleChain
      .outerRule(GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE))
      .around(new ScreenshotTestRule());
9
Maragues

Androidのテストではまだスクリーンショットを使用していませんが、役立つ可能性のあるいくつかの解決策を知っています。

スプーン

そのための最良の方法は、EmmaまたはSpoonフレームワークを使用することです。

ここにそれを行う方法のいくつかの有用な情報があります: http://elekslabs.com/2014/05/creating-test-reports-for-Android-with-spoon-and-emma.html

Spoonの公式Githubサイトもご覧ください: https://github.com/square/spoon およびそのGradleのプラグイン: https://github.com/stanfy/spoon -gradle-plugin

関連トピックを確認してください: SpoonにEspressoテストのスクリーンショットを撮らせる方法は?

スクリーンショット-tests-for-Android /

このFacebookのライブラリを試すこともできます: https://facebook.github.io/screenshot-tests-for-Android/

RobotiumのScreenshotTaker

すでに知っているように、Robotiumを使用すると、テストフレームワークでスクリーンショットを使用してテストを作成できます。チェック: RobotiumとCucumberでスクリーンショットを撮る正しい方法

ライブラリを使用したくない場合は、Robotiumフレームワーククラスのソースコードを確認してください ScreenshotTaker.Java [リンクをクリックして表示]し、独自のScreenshotTakerクラスを作成します。

それが役立つことを願っています。

5
piotrek1543

説明されている他の回答のようにカスタムTestWatcherを作成するのが方法です。

[〜#〜] but [〜#〜](そしてそれに気付くのに長い時間がかかりました)警告があります:ルール- かもしれない発砲が遅すぎる、つまりあなたの活動がすでに破壊された後。これにより、失敗したアクティビティからではなく、デバイスのホーム画面のスクリーンショットが残ります。

RuleChain を使用してこれを解決できます:書く代わりに

@Rule
public final ActivityTestRule<MainActivity> _activityRule = new ActivityTestRule<>(MainActivity.class);

@Rule
public ScreenshotTestWatcher _screenshotWatcher = new ScreenshotTestWatcher();

あなたは書く必要があります:

private final ActivityTestRule<MainActivity> _activityRule = new ActivityTestRule<>(MainActivity.class);

@Rule
public final TestRule activityAndScreenshotRule = RuleChain
        .outerRule(_activityRule)
        .around(new ScreenshotTestWatcher());

これにより、最初にスクリーンショットが撮られ、次にアクティビティが破棄されます

2
PhilLab

@ Maragues Kotlinに移植された回答:

ヘルパークラス:

package utils

import Android.graphics.Bitmap
import Android.os.Environment.DIRECTORY_PICTURES
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.runner.screenshot.BasicScreenCaptureProcessor
import androidx.test.runner.screenshot.ScreenCaptureProcessor
import androidx.test.runner.screenshot.Screenshot
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import Java.io.File
import Java.io.IOException

class IDTScreenCaptureProcessor : BasicScreenCaptureProcessor() {
    init {
        mTag = "IDTScreenCaptureProcessor"
        mFileNameDelimiter = "-"
        mDefaultFilenamePrefix = "Giorgos"
        mDefaultScreenshotPath = getNewFilename()
    }

    private fun getNewFilename(): File? {
        val context = getInstrumentation().getTargetContext().getApplicationContext()
        return context.getExternalFilesDir(DIRECTORY_PICTURES)
    }
}

class ScreenshotTestRule : TestWatcher() {
    override fun finished(description: Description?) {
        super.finished(description)

        val className = description?.testClass?.simpleName ?: "NullClassname"
        val methodName = description?.methodName ?: "NullMethodName"
        val filename = "$className - $methodName"

        val capture = Screenshot.capture()
        capture.name = filename
        capture.format = Bitmap.CompressFormat.PNG

        val processors = HashSet<ScreenCaptureProcessor>()
        processors.add(IDTScreenCaptureProcessor())

        try {
            capture.process(processors)
        } catch (ioException: IOException) {
            ioException.printStackTrace()
        }
    }
}

使用法:

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import utils.ScreenshotTestRule

@RunWith(AndroidJUnit4::class)
@LargeTest
class DialogActivityTest {

    @get:Rule
    val activityRule = ActivityTestRule(DialogActivity::class.Java)

    @get:Rule
    val screenshotTestRule = ScreenshotTestRule()

    @Test
    fun dialogLaunch_withTitleAndBody_displaysDialog() {
        // setup
        val title = "title"
        val body = "body"

        // assert
        onView(withText(title)).check(matches(isCompletelyDisplayed()))
        onView(withText(body)).check(matches(isCompletelyDisplayed()))
    }


}

アプリのbuild.gradleで宣言されたライブラリ:

androidTestImplementation "androidx.test.espresso:espresso-core:3.1.1"
androidTestImplementation "androidx.test.espresso:espresso-intents:3.1.1"
androidTestImplementation "androidx.test.ext:junit:1.1.0"
androidTestImplementation "androidx.test:runner:1.1.1"
androidTestImplementation "androidx.test:rules:1.1.1"

この設定では、毎回スクリーンショットが保存されますテストはフォルダーで終了します:/sdcard/Android/data/your.package.name/files/Pictures Android Studioのデバイスファイルエクスプローラー(右側のサイドバー)

失敗したテストのスクリーンショットのみを保存する場合は、failedではなくTestWatcherfinishedメソッドをオーバーライドします。

1
giorgos.nl

this answerにいくつかの改善を加えました。 UiAutomatorに依存関係を追加する必要はなく、APIレベル18未満でも機能します。

public class ScreenshotTestWatcher extends TestWatcher
{
   private static Activity currentActivity;

   @Override
   protected void failed(Throwable e, Description description)
   {
      Bitmap bitmap;

      if (Android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
      {
         bitmap = getInstrumentation().getUiAutomation().takeScreenshot();
      }
      else
      {
         // only in-app view-elements are visible.
         bitmap = Screenshot.capture(getCurrentActivity()).getBitmap();
      }

      // Save to external storage '/storage/emulated/0/Android/data/[package name app]/cache/screenshots/'.
      File folder = new File(getTargetContext().getExternalCacheDir().getAbsolutePath() + "/screenshots/");
      if (!folder.exists())
      {
         folder.mkdirs();
      }

      storeBitmap(bitmap, folder.getPath() + "/" + getFileName(description));
   }

   private String getFileName(Description description)
   {
      String className = description.getClassName();
      String methodName = description.getMethodName();
      String dateTime = Calendar.getInstance().getTime().toString();

      return className + "-" + methodName + "-" + dateTime + ".png";
   }

   private void storeBitmap(Bitmap bitmap, String path)
   {
      BufferedOutputStream out = null;
      try
      {
         out = new BufferedOutputStream(new FileOutputStream(path));
         bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
      }
      catch (IOException e)
      {
         e.printStackTrace();
      }
      finally
      {
         if (out != null)
         {
            try
            {
               out.close();
            }
            catch (IOException e)
            {
               e.printStackTrace();
            }
         }
      }
   }

   private static Activity getCurrentActivity()
   {
      getInstrumentation().runOnMainSync(new Runnable()
         {
            public void run()
            {
               Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
                     RESUMED);
               if (resumedActivities.iterator().hasNext())
               {
                  currentActivity = (Activity) resumedActivities.iterator().next();
               }
            }
         });

      return currentActivity;
   }
}

次に、テストクラスに次の行を含めます。

@Rule
public TestRule watcher = new ScreenshotTestWatcher();
0
Wirling