我开始使用浓缩咖啡,并编写了一些简单的UI测试,用于单击按钮,输入文本并检查所有内容是否都在应有的位置。现在我想截取屏幕截图并测试一些像素值,但我找不到一种方法来做到这一点。有人有什么建议吗?浓缩咖啡甚至可能吗?
由于浓缩咖啡测试是简单的检测测试用例,您可以使用狗仔队和名人来截取应用程序的屏幕截图。
勺子也是一个非常好的截图系统。比狗仔队更清晰,但只能截取活动的屏幕截图,例如对话框。
最后但并非最不重要的一点是,从android SDK 16开始,您可以使用屏幕盖实用程序来拍摄非常快速,清晰的应用程序屏幕截图。我认为这个解决方案确实是最好的,但它仅适用于 SDK 16 .
最后要对照参考截图测试截图,没有真正的工具。你可以找到在stack over flow上实现的好主意,但是到目前为止,还没有人提供可以在android上使用的参考工具。
脸书有一个工具可以比较屏幕截图,如果这就是你要找的:http://facebook.github.io/screenshot-tests-for-android/
迭代 UI 代码很困难。如何快速验证布局或视图更改是否在所有配置中正常工作?Android 的屏幕截图测试可以通过提供一个测试框架来检查更改之间的视觉差异来解决这些问题。
我花了很大力气为我的自定义视图设置截图测试。< br >以下是我是如何做到这一点的,以及我在这个过程中学到的一切。< br >它也可以由常规应用程序和活动使用。
如果你愿意,你可以使用JUnit 4。我用的是JUnit 5。因为JUnit 5是完全基于Java 8构建的,所以它的工具测试只能在运行Android 8.0 (API 26)或更新版本的设备上运行。旧手机或模拟器会完全跳过这些测试的执行,将它们标记为忽略。
如果您想在Android上运行JUnit 5测试,请参阅此答案以了解如何设置它。
屏幕截图测试可能无法在其他设备上工作,即使它们具有相同的屏幕 DPI(它们可能根本不适用于具有不同屏幕 DPI 的设备)。例如,即使我在本地计算机和 GitHub 操作中使用相同的设备来运行测试,它们也不会产生相同的结果(GitHub 操作断言失败)。因此,我不得不在GitHub Actions上禁用它们。
如果要禁用 GitHub 操作上的屏幕截图测试,请参阅此答案。
如果您在插入指令的测试中有资源(在androidTest目录的子目录中),并且希望引用它们的id,那么应该这样使用它们:
my.package.name.test.R.id.an_id
例如,如果您的软件包名称是com.example
,那么要在测试中访问src/androidTest/res/layout/my_layout.xml中的布局文件,请使用com.example.test.R.layout.my_layout
。
由于我们将测试屏幕截图保存在设备/模拟器的外部存储上,因此我们需要确保在清单中添加了WRITE_EXTERNAL_STORAGE权限,并在构建脚本中配置了 adb 安装选项 -g 和 -r。当运行时 Marshmallow ,我们还需要在运行测试之前授予这些权限。-g 用于在安装应用程序时授予权限(仅适用于Marshmallow),而 -r 用于允许重新安装应用程序。这些对应于 adb 外壳 pm 安装
选项。请注意,这还不适用于Android Studio。
创建AndroidManifest。xml文件,并向其中添加以下内容:
<manifest package="com.example">
<!-- For saving screenshots in tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage"
tools:remove="android:maxSdkVersion"/>
<application android:requestLegacyExternalStorage="true">
<!-- In case your test activity is in *androidTest* source set
(I have it like this to test my custom view), define it here -->
<activity android:name=".MyActivityThatContainsTheView"/>
</application>
</manifest>
并在应用程序构建文件中添加adb安装选项:
android {
// adbOptions block is deprecated in newer version of Android Gradle Plugin;
// replace adbOptions block with installation block
adbOptions {
installOptions("-g", "-r")
}
}
我在src/androidTest/assets目录下保存参考截图(我要和当前截图比较的那个)。因此,在应用程序构建脚本中将该目录指定为资产条目:
android {
sourceSets {
get("debug").assets.srcDirs("src/androidTest/assets")
}
在运行测试时传递检测参数(如下面代码中的应保存
和应处理
):
后传递参数。/grad Lew my task-pan droid . testinstrumentationrunnerarguments . should save = true
< li >使用Android Studio运行任务:在Arguments:field < br > < code >-pan droid . testinstrumentationrunnerarguments . should save = true 中传递您的参数看到这篇文章和这个帖子。
确保使用 -ktx 版本的 AndroidX Core 库。
-ktx 版本包含有用的 Kotlin 扩展函数:
implementation("androidx.core:core-ktx:1.6.0")
确保设备屏幕打开并解锁,以便活动进入恢复状态。
这是我在src/androidTest/Java/com/example/目录下测试的活动,该目录有一个属性指向我想要获取其截图的视图:
class MyActivityThatContainsTheView : AppCompatActivity() {
lateinit var myView: MyView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(com.example.test.R.layout.my_layout_that_contains_the_view)
myView = findViewById(com.example.test.R.id.my_view_id_in_the_layout_file)
}
}
最后,这是我的测试,以及我如何保存、加载和比较屏幕截图:
@DisabledIfBuildConfigValue(named = "CI", matches = "true")
class ScreenshotTestView {
@JvmField
@RegisterExtension
val scenarioExtension = ActivityScenarioExtension.launch<MyActivityThatContainsTheView>()
lateinit var scenario: ActivityScenario<MyActivityThatContainsTheView>
// See ⚠ Caution #6 above in the post
val shouldSave = InstrumentationRegistry.getArguments().getString("shouldSave", "false").toBoolean()
val shouldAssert = InstrumentationRegistry.getArguments().getString("shouldAssert", "true").toBoolean()
@BeforeEach fun setUp() {
scenario = scenarioExtension.scenario
scenario.moveToState(Lifecycle.State.RESUMED)
}
@Test fun test1() {
val screenshotName = "screenshot-view-1"
scenario.onActivity { activity ->
val view = activity.myView
view.drawToBitmap()
.saveIfNeeded(shouldSave, screenshotName)
.assertIfNeeded(shouldAssert, screenshotName)
}
}
fun Bitmap.saveIfNeeded(shouldSave: Boolean, name: String): Bitmap {
if (shouldSave) save(name)
return this
}
fun Bitmap.assertIfNeeded(shouldCompare: Boolean, screenshotName: String) {
if (shouldCompare) assert(screenshotName)
}
/**
* The screenshots are saved in /Android/data/my.package.name.test/files/Pictures
* on the external storage of the device.
*/
private fun Bitmap.save(name: String) {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File(path, "$name.png")
file.outputStream().use { stream ->
compress(Bitmap.CompressFormat.PNG, 100, stream)
}
}
private fun Bitmap.assert(screenshotName: String) {
val reference = loadReferenceScreenshot(screenshotName)
// I'm using AssertJ library; you can simply use assertTrue(this.sameAs(reference))
assertThat(this.sameAs(reference))
.withFailMessage { "Screenshots are not the same: $screenshotName.png" }
.isTrue()
}
private fun loadReferenceScreenshot(name: String): Bitmap {
val context = InstrumentationRegistry.getInstrumentation().context
val assets = context.resources.assets
val reference = assets.open("compose/$name.png").use { stream ->
BitmapFactory.decodeStream(stream)
}
return reference
}
}