我正在为视图模型编写我的第一个单元测试。我指的是GithubBrowserArchitectureComponents示例。我正在测试是否执行为实时数据设置值的函数,是否从存储库类为该实时数据调用在切换映射中调用的函数。为此,我使用了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")
}
}
build.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
}
MyTestRunner
/**
* 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)
}
}
PostViewModel
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()
}
}
答案 0 :(得分:0)
单元测试正在测试单个代码单元,您的一些测试正在测试不同类之间的连接。可以,但是它是集成测试而不是单元测试。
要测试单元,您要测试输入和输出。例如方法的输入参数和输出返回值。
因此,对于您的ViewModel,您只想测试调用showPosts
方法是否将传递的输入分配给LiveData。您将需要模拟LiveData并验证是否已使用您的输入调用了该方法。
另外,您应该通过传递输入并验证返回的LiveData是否符合您的期望来测试存储库。 (就我个人而言,我不会从存储库返回LiveData,我会返回您的值并将其包装在viewmodel中的LiveData中,但这是您的选择。)
一旦进行了这两个测试,就已经测试了两个单元。如果随后要创建集成测试(在存储库运行其代码并测试系统观察结果时测试实时数据更新),那么这样做的好处就更少了,因为您正在测试系统代码而不是自己的代码。这也将变得更加混乱(如您所发现的),并且对更改更脆弱。
希望您能朝正确的方向前进,即使它不是直接的编码答案。 :)