android - 使用 Mockito.verify() 时,不只是检查是否在模拟对象上调用了该函数,而是调用来自真实对象的方法
问题描述
我正在为视图模型编写我的第一个单元测试。我指的是 GithubBrowserArchitectureComponents 示例。我正在测试是否执行为实时数据设置值的函数,是否为来自存储库类的实时数据调用在 switch 映射中调用的函数。为此,我使用了 Mocktio.verify 函数,在这个函数中,我传递了一个参数,该参数是 Repository 类的模拟对象,并验证是否调用了 getPosts 方法。但我发现它实际上调用了该方法,而不是仅仅检查调用。而样本中的那个没有
我也在使用 Dagger 2,所以我怀疑存储库是被注入而不是被模拟的,因此如示例中所示,我已将 testInstrumentationRunner 更改为使用不同应用程序类即 TestApp 的自定义
@RunWith(JUnit4::class)
class PostViewModelTest {
private val testContext = TestCoroutineContext()
@ExperimentalCoroutinesApi
@get:Rule
val coroutinesDispatcherRule = ViewModelScopeMainDispatcherRule(testContext)
@Rule
@JvmField
val instantTaskExecutorRule = InstantTaskExecutorRule()
private var repository = mock(PostRepository::class.java)
private var appExecutor = mock(AppExecutors::class.java)
private var postDao = mock(PostDao::class.java)
private val postViewModel = PostViewModel(postDao, repository, appExecutor)
@Test
fun fetchWhenObserved(){
postViewModel.showPosts("a", "b")
postViewModel.posts.observeForever(mock())
verify(repository).getPosts("a", "b")
}
}
构建.gradle
defaultConfig {
applicationId "com.example.test"
minSdkVersion 16
targetSdkVersion 28
multiDexEnabled true
versionCode 3
versionName "3.0"
testInstrumentationRunner "com.example.test.util.MyTestRunner"
vectorDrawables.useSupportLibrary = true
}
我的TestRunner
/**
* Custom runner to disable dependency injection.
*/
open class MyTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader, className: String, context: Context): Application {
return super.newApplication(cl, TestApp::class.java.name, context)
}
}
后视图模型
class PostViewModel @Inject
constructor(var postDao: PostDao,
var repository: PostRepository,
var appExecutors: AppExecutors
): ViewModel(){
private val showPosts = MutableLiveData<Pair<String, String>>()
// Get Post Live Data
var posts: LiveData<PagedList<Post>> = Transformations.switchMap(showPosts) { groupIdToUserId ->
repository.getPosts(groupIdToUserId.first, groupIdToUserId.second)
}
fun showPosts(groupId: String, userId: String) {
showPosts.value = groupId to userId
}
}
存储库
open class PostRepository @Inject constructor(
private var db: AppDatabase,
private var postDao: PostDao,
private var appExecutors: AppExecutors,
private var apiService: ApiService,
private var user: User
) {
fun getPosts(groupId: String, userId: String): LiveData<PagedList<Post>> {
val factory = postDao.allPosts(groupId)
factory.create()
val boundaryCallback = PostBoundaryCallback(groupId, userId, postDao, apiService, appExecutors)
return LivePagedListBuilder(factory, 10)
.setBoundaryCallback(boundaryCallback)
.setFetchExecutor(appExecutors.diskIO())
.build()
}
}
解决方案
单元测试正在测试单个代码单元,您的一些测试正在测试不同类之间的连接。这没关系,但它是集成测试而不是单元测试。
要测试单元,您需要测试输入和输出。例如方法的输入参数和输出返回值。
因此,对于您的 ViewModel,您只想测试调用您的showPosts
方法是否将传递的输入分配给 LiveData。您需要模拟 LiveData 并验证已使用您的输入调用该方法。
另外,您应该通过传入输入并验证返回的 LiveData 是否符合您的期望来测试您的存储库。(我个人不会从存储库返回 LiveData,我会返回您的值并将其包装在视图模型内的 LiveData 中,但这是您的选择)。
一旦你完成了这两个测试,你就已经测试了这两个单元。如果您想创建一个集成测试(测试实时数据在您的存储库运行其代码并且系统观察结果时更新),那么这样做的好处就更少,因为您正在测试系统代码而不是您自己的代码。它也会更混乱(正如你所发现的那样)并且对变化更脆弱。
希望这能让您朝着正确的方向前进,即使它不是直接的编码答案。:)
推荐阅读
- android - 水平 recyclerview 有项目 cardview 底部 textview 部分未完全显示
- mongodb - 如何使用 mongoDB 比较查询运算符来处理 createdAt 日期?
- mainframe - 将返回唯一字符串的 REXX 例程
- scala - SparkStream:在 SQL 查询中将字符串转换为浮点数
- c# - 如何在 Unity 中显示类似于 android 中的 Toast 消息?
- c++ - 如何在 CodeLite IDE 中构建和运行位于不同项目或同一项目中的多个主 cpp 文件?
- html - 响应式 SVG 填充形状
- python - Scrapy:合并来自不同站点的项目
- php - 根据另一个数组的内容过滤多维数组的内容
- verilog - 为什么大多数verilog代码在posedge时钟中工作?