Android测试初体验
- 前言
- 介绍
- 编码
- gradle配置
- 编写普通类
- 编写测试类
- 注意点
- Point one
- Point two
- Point Three
- 资料
前言
对于任何一个开发人员来说,测试必然是不可缺少的一部分。以前开发都是直接run代码到模拟器或者实体机上面进行实体测试,虽然说这样的测试,可以更加直观的看出问题。但是进行单元测试的话,需要较大的代码量来写测试用例,而且实体测试耗时更长,费时费力。所以想要学习如何更高效的进行代码测试。
介绍
稍微了解了一下(看了半天的样子吧,这篇文章只是为了暂时记录一下遇到的坑,而不是个全方位系统的介绍,所以如果有错或者需要补充的地方请在评论区指出),测试大致分为三类:
- 小型测试是指单元测试,用于验证应用的行为,一次验证一个类。
- 中型测试是指集成测试,用于验证模块内堆栈级别之间的互动或相关模块之间的互动。
- 大型测试是指端到端测试,用于验证跨越了应用的多个模块的用户操作流程。
本文主要进行了单元测试,使用了Junit4和 Robolectric。主要是跟着资源5的内容进行了代码的编写,但是由于资源5的年代久远,所以有一些内容发生了改变。这里主要是为了记录和资源5所描述的不一样的地方。
编码
gradle配置
向app的build.gradle中加入
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:4.2.1'
testImplementation 'androidx.test:core:1.2.0'
}
编写普通类
这里写了两个Activity,在MainActivity里面使用一个TextView,点击TextView跳转到SecondActivity
1.MainActivity
package com.example.androidtestlearning
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv_turn_new_activity.setOnClickListener {
startActivity(Intent(this,SecondActivity::class.java))
}
}
}
2.SecondActivity(就是一个空的什么都没写的activity)
package com.example.androidtestlearning
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
}
}
编写测试类
用来测试是否点击了MainActivity里的tv_turn_new_activity能跳转到SecondActivity
package com.example.androidtestlearning
import android.content.Intent
import androidx.test.core.app.ActivityScenario
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlinx.android.synthetic.main.activity_main.*
import org.robolectric.Shadows
@RunWith(RobolectricTestRunner::class)
class MainActivityTest {
@Test
@Throws(Exception::class)
fun clickText() {
val scenario = ActivityScenario.launch(
MainActivity::class.java
)
scenario.onActivity { activity ->
//点击tv
activity.tv_turn_new_activity.performClick()
//预期的Intent
val expectedIntent = Intent(activity, SecondActivity::class.java)
//用来获取MainActivity对应的ShadowActivity的instance
val shadowActivity = Shadows.shadowOf(activity)
//实际获得的Intent
val actualIntent = shadowActivity.nextStartedActivity
//判断两个Intent是否一样
assertEquals(expectedIntent, actualIntent)
}
}
}
注意点
Point one
旧的资料使用Robolectric.setupActivity(MainActivity.class); 来获取activity,然后发现setupActivity方法已经被弃用了,文档上写的
- Use {@link androidx.test.core.app.ActivityScenario} instead, which works with instrumentation tests too.
于是就直接跳转到ActivityScenario类里去看,是这样推荐使用的
* <pre>{@code
* Before:
* MyActivity activity = Robolectric.setupActivity(MyActivity.class);
* assertThat(activity.getSomething()).isEqualTo("something");
*
* After:
* try(ActivityScenario<MyActivity> scenario = ActivityScenario.launch(MyActivity.class)) {
* scenario.onActivity(activity -> {
* assertThat(activity.getSomething()).isEqualTo("something");
* });
* }
那么我就直接把这一段代码ctrl+c ctrl+v了上去,然后发现找不到ActivityScenario。我就google了一下,android developer是这样配置的gradle
dependencies {
// Core library
androidTestImplementation 'androidx.test:core:1.0.0'
// AndroidJUnitRunner and JUnit Rules
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
// Assertions
androidTestImplementation 'androidx.test.ext:junit:1.0.0'
androidTestImplementation 'androidx.test.ext:truth:1.0.0'
androidTestImplementation 'com.google.truth:truth:0.42'
// Espresso dependencies
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.0'
androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.1.0'
// The following Espresso dependency can be either "implementation"
// or "androidTestImplementation", depending on whether you want the
// dependency to appear on your APK's compile classpath or the test APK
// classpath.
androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.1.0'
}
然后发现这样添加之后还是找不到ActivityScenario,我的测试类是写在test文件夹而不是androidtest里面的。而androidTestImplementation是为androidTest里面的文件所依赖的,所以改为testImplementation,即可找到此类。
Point two
我之前根据资料没有添加这几行代码
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
导致一直报错
No such manifest file: .\AndroidManifest.xml
Point Three
虽然我的程序运行没有错误了,但是它的expectedIntent和actualIntent判断出来不一样emmmm我也不知道为什么(有大佬知道的话,能告诉我是为什么吗)
java.lang.AssertionError: expected: android.content.Intent<Intent { cmp=com.example.androidtestlearning/.SecondActivity }> but was: android.content.Intent<Intent { cmp=com.example.androidtestlearning/.SecondActivity }>
Expected :android.content.Intent<Intent { cmp=com.example.androidtestlearning/.SecondActivity }>
Actual :android.content.Intent<Intent { cmp=com.example.androidtestlearning/.SecondActivity }>
资料
- Android Developer Test
- AndroidX Test Document
- Github Android testing-samples
- Robolectric
- 用Robolectric来做Android unit testing
- Codelab