使用Mockito的Android单元测试:无法获得正确的模拟行为



我正在使用Mockito测试我的存储库类,特别是getProducts()功能:

class Repository private constructor(private val retrofitService: ApiService) {
companion object {
@Volatile
private var INSTANCE: Repository? = null
fun getInstance(retrofitService: ApiService): Repository {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Repository(retrofitService)
}
INSTANCE = instance
return instance
}
}
}
suspend fun getProducts(): ProductsResponse = withContext(IO) {
retrofitService.getProducts()
}
}

这是我的测试类:

@ExperimentalCoroutinesApi
@RunWith(MockitoJUnitRunner::class)
class RepositoryTest {
// Class under test
private lateinit var repository: Repository
// Executes each task synchronously using Architecture Components.
@get:Rule
val instantExecutorRule = InstantTaskExecutorRule()
// Set the main coroutines dispatcher for unit testing.
@ExperimentalCoroutinesApi
@get:Rule
var mainCoroutineRule = MainCoroutineRule()
@Mock
private lateinit var retrofitService: ApiService
@Before
fun createRepository() {
MockitoAnnotations.initMocks(this)
repository = Repository.getInstance(retrofitService)
}
@Test
fun test() = runBlocking {
// GIVEN
Mockito.`when`(retrofitService.getProducts()).thenReturn(fakeProductsResponse)
// WHEN
val productResponse: ProductsResponse = repository.getProducts()
println("HERE = ${retrofitService.getProducts()}")
// THEN
println("HERE: $productResponse")
MatcherAssert.assertThat(productResponse, `is`(fakeProductsResponse))
}
}

和我的蜂服务:

interface ApiService {
@GET("https://www...")
suspend fun getProducts(): ProductsResponse
}

当我调用repository.getProducts()时,它返回 null,尽管我明确设置了retrofitService.getProducts()返回fakeProductsResponse,这是在存储库的getProducts()方法中调用的。它应该返回fakeProductsResponse,但它返回空值。

我是否做错了嘲笑或问题可能是什么?谢谢。。。

编辑:这是我MainCoroutineRule,如果你需要它

@ExperimentalCoroutinesApi
class MainCoroutineRule(val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()):
TestWatcher(),
TestCoroutineScope by TestCoroutineScope(dispatcher) {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
cleanupTestCoroutines()
Dispatchers.resetMain()
}
}

这可能不是您问题的完整解决方案,但我看到的是您的MainCoroutineRule覆盖了mainDispatcherDispatchers.setMain(dispatcher)

但在

suspend fun getProducts(): ProductsResponse = withContext(IO)

您正在显式设置 IO 调度程序。

我建议始终从通过构造函数传递的属性设置调度程序:

class Repository private constructor(
private val retrofitService: ApiService,
private val dispatcher: CoroutineDispatcher) {
companion object {

fun getInstance(retrofitService: ApiService,
dispatcher: CoroutineDispatcher = Dispatchers.IO): Repository {
// ommit code for simplicity
instance = Repository(retrofitService, dispatcher)
// ...
}
}
}
suspend fun getProducts(): ProductsResponse = withContext(dispatcher) {
retrofitService.getProducts()
}
}

将其作为默认参数,您无需在常规代码中传递它,但您可以在单元测试中交换它:

class RepositoryTest {
private lateinit var repository: Repository
@get:Rule
var mainCoroutineRule = MainCoroutineRule()
@Mock
private lateinit var retrofitService: ApiService
@Before
fun createRepository() {
MockitoAnnotations.initMocks(this)
repository = Repository.getInstance(retrofitService, mainCoroutineRule.dispatcher)
}
}

对于我自己的单元测试,我正在使用CoroutineRuleTestCoroutineDispatcher的阻止功能,例如:

@Test
fun aTest() = mainCoroutineRule.dispatcher.runBlockingTest {
val acutal = classUnderTest.callToSuspendFunction()
// do assertions
}

我希望这会对您有所帮助。

最新更新