当前位置: 首页 > 知识库问答 >
问题:

使用Mockito模拟挂起函数返回null

督翰学
2023-03-14

我有以下几门课

interface CarsApi {
    suspend fun fetchCar() : Car
}

class FetchCarUseCase(private val carsApi: CarsApi) {
    suspend fun execute: Car = withContext(dispatcherProvider.io()) {
        carsApi.fetchCar()
    }
}

class ViewModel(private val fetchCarUseCase: FetchCarUseCase) {

     private var car: Car

     suspend fun retrieveCar() {
        car = fetchCarUseCase.execute()
     }
}

我想为viewModel和UseCase编写一个ermetic测试:

@Test
fun testCarFetching() = runBlockingTest {
  val aCar = Car()
  val mockApi = mock<CarsApi>()
  `when`(mockApi.fetchCar()).thenReturn(aCar)
  val fetchCarUseCase = FetchCarUseCase(mockApi)
  val viewModel = ViewModel(fetchCarUseCase)

  viewModel.retrieveCar()

  /* assert stuff on viewModel.car*/
}

但ViewModel.car似乎总是为空。在测试体中mockapi.fetchcar()检索提供的值,但在FetchCarUseCase中不检索。此外,如果我从界面中移除suspend关键字,那么嘲弄似乎可以很好地工作。

目前,由于一些其他条件,我无法使用Mockk库,所以我只能使用mockito。

我是不是漏掉了什么?

使用的依赖项:testImplementation“junit:junit:4.12”testImplementation“org.mockito:mockito-core:2.28.2”testImplementation(“com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"){exclude module:”mockito-core“}testImplementation”org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2

共有1个答案

许博易
2023-03-14

万一其他人必须处理这个问题,这里是我已经建立的基础设施。

首先,在启动线程的所有类中,通过构造函数或属性注入一个kotlinx.coroutines.DispatcherProvider。在我的例子中,它只是useCase,但viewModel也可能需要它。

    class FetchCarUseCase(private val dispatcher: CoroutineDispatcher,
                          private val carsApi: CarsApi) {
    suspend fun execute: Car = withContext(dispatcher) {
        carsApi.fetchCar()
    }
}

在单元测试项目中,添加一个帮助器规则类,以提取某些功能

 @ExperimentalCoroutinesApi
class CoroutineTestRule(val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : TestWatcher() {

    val testDispatcherProvider = object : DispatcherProvider {
        override fun default(): CoroutineDispatcher = testDispatcher
        override fun io(): CoroutineDispatcher = testDispatcher
        override fun main(): CoroutineDispatcher = testDispatcher
        override fun unconfined(): CoroutineDispatcher = testDispatcher
    }

    override fun starting(description: Description?) {
        super.starting(description)
        Dispatchers.setMain(testDispatcher)
    }

    override fun finished(description: Description?) {
        super.finished(description)
        Dispatchers.resetMain()
        testDispatcher.cleanupTestCoroutines()
    }
}

最后单元测试如下所示:

    @ExperimentalCoroutinesApi
    @RunWith(MockitoJUnitRunner::class)
    class ViewModelTest {
        @get:Rule
        var coroutinesTestRule = CoroutineTestRule()

        @Test
    fun testCarFetching() = coroutinesTestRule.testDispatcher.runBlockingTest {
      val aCar = Car()
      val mockApi = mock<CarsApi>()
      `when`(mockApi.fetchCar()).thenReturn(aCar)
      val fetchCarUseCase = FetchCarUseCase(mockApi)
      val viewModel = ViewModel(fetchCarUseCase)

      viewModel.retrieveCar()

      /* assert stuff on viewModel.car*/
    }

   @Test
    fun testCarFetchingError() = coroutinesTestRule.testDispatcher.runBlockingTest {
      val aCar = Car()
      val mockApi = mock<CarsApi>()
      `when`(mockApi.fetchCar()).then {
            throw Exception()
        }

      val fetchCarUseCase = FetchCarUseCase(mockApi)
      val viewModel = ViewModel(fetchCarUseCase)

      viewModel.retrieveCar()

      /* assert stuff on erros*/
    }
}

这样,单元测试中的所有代码都运行在同一线程和同一上下文中。

 类似资料:
  • 我遇到了Mockito的一个bug,但我想知道是否有其他人可以解释为什么这个测试不起作用。 基本上,我有两个对象,如下所示: 第一个对象是通过注释和之前的方法模拟的: 在方法中模拟第二个对象: 当包含对此方法的直接调用以设置并获取第二个对象的模拟时,它将失败: 但是,当同一方法返回的模拟被分配给一个局部变量(在中使用)时,它可以工作: 我们是否做错了什么,或者这确实是Mockito中的一个缺陷/限

  • 问题内容: 我想将构造函数模拟为方法。 在我的测试中,我想做这样的事情: 但是给我这个 错误 知道为什么吗? 问题答案: 您可以使用PowerMock模拟构造函数。 如果由于某种原因而无法使用PowerMock,则最可行的解决方案是将工厂注入到包含此方法的任何类中。然后,您将使用工厂创建对象并模拟工厂。

  • 我正在尝试测试另一个方法使用的getCookieByName方法。但是,我不确定我是否正确执行了此操作,因为该方法似乎被多次调用,并且它在第一次尝试时设置了值,但在最后一次调用时为空。我认为模拟调用的顺序可能是错误的,或者其中一些可能不需要,但是如果我删除了其中任何一个,我仍然会得到其他错误,因此不确定我到底做错了什么。 这是我的测试和模拟调用,以及在同一个方法中两次调用getCookieByNa

  • 我有一个带有私有构造函数的单例类,我想为此编写单元测试。 如何使用mockito框架模拟具有私有构造函数的类。 谢谢

  • 我还没有看到这个问题的答案能解决我的问题。 我有一个服务类,它是用一个存储库和一个Orika MapperFacade构建的。在测试中,我正在安排存储库调用返回的实体列表。my doReturn(testList).when(testRepository).findAllBy...似乎正在工作。我的实际结果列表包含我在模拟列表中指定的相同数量的元素(.size()),但是这些值都是空的。我对Spr