Android 如何在浓缩咖啡测试失败时截图?
我正在寻找一种方法,在测试失败后和关闭前拍摄设备的屏幕截图。我还没有在Android测试中使用屏幕截图,但我知道一些可能有用的解决方案: 勺子 最好的方法是使用Android 如何在浓缩咖啡测试失败时截图?,android,testing,automated-tests,android-espresso,Android,Testing,Automated Tests,Android Espresso,我正在寻找一种方法,在测试失败后和关闭前拍摄设备的屏幕截图。我还没有在Android测试中使用屏幕截图,但我知道一些可能有用的解决方案: 勺子 最好的方法是使用Emma或Spoon框架 在这里,您可以找到一些有用的信息,了解如何做到这一点: 还可以访问Spoon的官方Github网站:及其Gradle插件: 并查看相关主题: android的屏幕截图测试/ 您也可以尝试此Facebook的库: 机器人截屏机 正如我已经知道的,使用Robotiumtestframework可以创建带有屏幕截图的
Emma
或Spoon
框架
在这里,您可以找到一些有用的信息,了解如何做到这一点:
还可以访问Spoon
的官方Github网站:及其Gradle插件:
并查看相关主题:
android的屏幕截图测试/
您也可以尝试此Facebook的库:
机器人截屏机
正如我已经知道的,使用Robotium
testframework可以创建带有屏幕截图的测试。检查:
如果您不想使用任何库,请检查名为[单击链接查看]的Robotium
框架类的源代码,并编写自己的ScreenshotTaker
类
希望能有所帮助。我找到的最简单的方法是:
@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));
}
};
我对答案做了一些改进。无需为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();
对先前答案的另一个改进。我用的是实验性的 然后,在你的基础浓缩咖啡测试课程中,只需添加
@Rule
public ScreenshotTestRule screenshotTestRule = new ScreenshotTestRule();
如果您希望使用某个受保护的文件夹,这在模拟器上起到了作用,尽管它在物理设备上不起作用
@Rule
public RuleChain screenshotRule = RuleChain
.outerRule(GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE))
.around(new ScreenshotTestRule());
编写一个定制的TestWatcher,就像前面解释的其他答案一样,是一个不错的选择 但是(我们花了很长时间才注意到)有一个警告:规则可能启动得太晚,即在您的活动已被破坏之后。这将为您留下设备主屏幕的屏幕截图,而不是失败活动的屏幕截图 你可以用:代替书写来解决这个问题
@Rule
public final ActivityTestRule<MainActivity> _activityRule = new ActivityTestRule<>(MainActivity.class);
@Rule
public ScreenshotTestWatcher _screenshotWatcher = new ScreenshotTestWatcher();
@规则
公共最终ActivityTestRule _activityRule=新ActivityTestRule(MainActivity.class);
@统治
公共ScreenshotTestWatcher_screenshotWatcher=新ScreenshotTestWatcher();
你必须写:
private final ActivityTestRule<MainActivity> _activityRule = new ActivityTestRule<>(MainActivity.class);
@Rule
public final TestRule activityAndScreenshotRule = RuleChain
.outerRule(_activityRule)
.around(new ScreenshotTestWatcher());
private final ActivityTestRule\u activityRule=new ActivityTestRule(MainActivity.class);
@统治
公共最终测试规则活动和屏幕快照规则=规则链
.outerRule(_activityRule)
.around(新的ScreenshotTestWatcher());
这将确保首先拍摄屏幕截图,然后销毁活动将答案移植到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()
}
}
}
在应用程序的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的设备文件资源管理器(位于右侧边栏)在那里导航
如果您希望仅保存失败测试的屏幕截图,请覆盖
TestWatcher
的failed
方法,而不是finished
您可以使用Spoon()我最终遇到了以下错误:未能将屏幕截图保存到文件java.io.FileNotFoundException:/storage/emulated/0/screenshots/com.myappname/asdffdsa.png(没有这样的文件或目录)通过授予写外部存储权限修复了上述问题。现在我的问题是,当测试失败时,上面的代码不会被调用。我在一次手动调用中尝试了这段代码,它成功了,但我无法让它在发生故障时自动工作。@ZeekAran没有使用TestWatcher
尝试注册espresso故障处理程序,正如这里所述,我最后这样做:@After public void tearDown(场景){if(Scenario.isFailed()){}PostTestCleanup.tearDown();}@如果不这样做,屏幕截图将存储在设备上。这确实有效,但我必须使用CustomScreenCaptureProcessor
。否则我会得到以下信息:java.io.IOException:目录/storage/emulated/0/Pictures/screenshots不存在,无法创建或无法写入。
。您能用详细信息更新我的答案吗?我也时不时地体验到这一点,但我还没有时间去研究它。嗨@Maragues,我不确定我是否理解你想要什么。我刚刚使用了您提供的CustomScreenCaptureProcessor
。提示:截图保存在存储/模拟/0/Android/data/{package}/files/Pictures/espresso_截图中。我花了一段时间才找到他们!您可以使用Android Studio设备文件资源管理器(右键单击文件夹->另存为…)一次性获取所有文件。更多详细信息,请停止从internet@markw. 您好,这是2016年8月的答案,没有。。。我没有从网上复制粘贴的内容。我检查了工具并提出了答案;-)
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()))
}
}
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"