【问题标题】:UnitTest coroutines Kotlin usecase MVPUnitTest 协程 Kotlin 用例 MVP
【发布时间】:2019-07-03 21:03:31
【问题描述】:

我正在尝试模拟来自我的用例的响应,这个用例适用于协程。

fun getData() {
    view?.showLoading()
    getProductsUseCase.execute(this::onSuccessApi, this::onErrorApi)
}

我的 useCase 被注入到 Presenter 上。

GetProductsUseCase 有这个代码:

class GetProductsUseCase (private var productsRepository: ProductsRepository) : UseCase<MutableMap<String, Product>>() {

    override suspend fun executeUseCase(): MutableMap<String, Product> {
        val products =productsRepository.getProductsFromApi()
        return products
    }
}

我的基本用例

abstract class UseCase<T> {

    abstract suspend fun executeUseCase(): Any

    fun execute(
        onSuccess: (T) -> Unit,
        genericError: () -> Unit) {
        GlobalScope.launch {
            val result = async {
                try {
                    executeUseCase()
                } catch (e: Exception) {
                    GenericError()
                }
            }
            GlobalScope.launch(Dispatchers.Main) {
                when {
                    result.await() is GenericError -> genericError()
                    else -> onSuccess(result.await() as T)
                }
            }
        }
    }

}

这个用例调用我的存储库:

override suspend fun getProductsFromApi(): MutableMap<String, Product> {
    val productsResponse = safeApiCall(
        call = {apiService.getProductsList()},
        error = "Error fetching products"
    )
    productsResponse?.let {
                    return productsMapper.fromResponseToDomain(it)!!
    }
    return mutableMapOf()
}

Y 尝试模拟我的响应,但测试总是失败。

@RunWith(MockitoJUnitRunner::class)
class HomePresenterTest {

    lateinit var presenter: HomePresenter

    @Mock
    lateinit var view: HomeView

    @Mock
    lateinit var getProductsUseCase: GetProductsUseCase

    @Mock
    lateinit var updateProductsUseCase: UpdateProductsUseCase

    private lateinit var products: MutableMap<String, Product>

    private val testDispatcher = TestCoroutineDispatcher()
    private val testScope = TestCoroutineScope(testDispatcher)

    @Mock
    lateinit var productsRepository:ProductsRepositoryImpl

    @Before
    fun setUp() {
        Dispatchers.setMain(testDispatcher)
        products = ProductsMotherObject.createEmptyModel()
        presenter = HomePresenter(view, getProductsUseCase, updateProductsUseCase, products)
    }

    @After
    fun after() {
        Dispatchers.resetMain()
        testScope.cleanupTestCoroutines()
    }

    //...

    @Test
    fun a() = testScope.runBlockingTest {
        setTasksNotAvailable(productsRepository)
        presenter.getDataFromApi()

        verify(view).setUpRecyclerView(products.values.toMutableList())
    }

    private suspend fun setTasksNotAvailable(dataSource: ProductsRepository) {
        `when`(dataSource.getProductsFromApi()).thenReturn((mutableMapOf()))
    }
}

我不知道发生了什么。日志说:

"Wanted but not invoked:
view.setUpRecyclerView([]);
-> at com.myProject.HomePresenterTest$a$1.invokeSuspend(HomePresenterTest.kt:165)

However, there was exactly 1 interaction with this mock:
view.showLoading();"

【问题讨论】:

    标签: android unit-testing kotlin-coroutines


    【解决方案1】:

    问题在于如何创建GetProductsUseCase

    您不是用ProductsRepositorymocked 版本创建它,而是在模拟ProductsRepository 调用。

    尝试手动创建GetProductsUseCase,而不是使用@Mock

    // no @Mock
    lateinit var getProductsUseCase: GetProductsUseCase
    
    
    @Before
    fun setUp() {
        // ...
        // after your mocks are initialized...
        getProductsUseCase = GetProductsUseCase(productsRepository) //<- this uses mocked ProductsRepository
    }
    

    【讨论】:

    • 非常感谢!有用。一个额外的问题是否可能。当我返回 mutableMapOf() 时,如何强制错误响应?我试图返回一个异常,但函数需要 mutableMap。
    • @ArisGuimerá 我不确定这是否是您所要求的,但您可以在 when(dataSource.getProductsFromApi()) 声明中使用 thenThrow 而不是 thenReturn
    猜你喜欢
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多