【问题标题】:NPE when stubbing a mock's method with Mockito使用 Mockito 存根模拟方法时的 NPE
【发布时间】:2020-10-16 16:45:14
【问题描述】:

我正在尝试测试存储库ExpensesRepository

class ExpensesRepository @Inject constructor(
    private val store: DataStore
) : AbstractRepository<Expense> {

    override fun get(offset: Int, limit: Int): List<Expense?> =
        store.getAll(offset, limit)

    override fun get(key: String): Expense? = store.getOne(key = key)

    override fun create(input: Expense) =
        store.create(key = input.key, element = input)


    override fun delete(key: String) = store.remove(key = key)

    override fun update(
        key: String,
        update: Expense
    ): Expense =
        store.update(key = key, element = update)

    override fun removeAll() = store.flush()
}

在测试套件中,我使用 mockito 模拟了 store 参数,然后我将该模拟的方法 getOne(String) 存根以从我的固定装置返回一个费用对象。

这是我的测试套件

class ExpensesRepositoryTest {

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var repository: ExpensesRepository
    private val dataStore = mock<DataStore>()
    private val expense = Expense(
        "key",
        1,
        "name",
        "date",
        12.12
    )

    private val expenseList = listOf(
        expense,
        expense,
        expense
    )

    private val dummyKey = "expense_key"

    @Before
    fun setup() {
        repository = ExpensesRepository(dataStore)
    }

    @Test
    fun `should get an item with existing key`() {
        whenever(dataStore.getOne<Expense>(dummyKey)).thenReturn(expense)
        val result = repository.get(dummyKey)
        verify(dataStore, Times(1)).getOne<Expense>(dummyKey)
        assertThat(result).isEqualTo(expense)
    }
}

当我运行测试时它失败了,因为 NullPointerException 并且那是 DataStore 调用我之前应该存根的函数 getOne(String) 的时候。如果它在这里有帮助,那就是DataStore

class DataStore(val store: Persistence, val gson: Gson) {

    inline fun <reified R : Persistable> getAll(
        offset: Int,
        limit: Int
    ): List<R?> =
        store.get(offset, limit).map { it.maybeMapTo<R>(mapper = gson) }

    inline fun <reified R : Persistable> getOne(key: String): R? =
        store.get(key).maybeMapTo<R>(mapper = gson)

    fun create(key: String, element: Persistable) {
        store.create(
            key = key,
            element = when (store) {
                is JsonBased -> gson.toJson(element)
                else -> element
            }
        )
    }

    inline fun <reified R : Persistable> update(
        key: String,
        element: R
    ): R {
        return store.update(key, element) as R
    }

    fun remove(key: String) {
        store.remove(key)
    }

    fun flush() {
        store.flush()
    }
}

我在这里做错了吗?

【问题讨论】:

  • 为什么不模拟 Persistence 类并将其注入 DataStore
  • 在这种情况下,DataStore 成为一个真实的实例,而不是一个模拟。理想情况下,只有被测类才是真实对象。因此,如果您这样做,您将不得不监视 DataStore,而我不想这样做。

标签: unit-testing kotlin junit mockito


【解决方案1】:

恐怕你运气不好:内联函数不可模拟。

【讨论】:

  • kotlin 内联函数在 java 中被翻译为 final 函数。根据 Mockito 的新版本,模拟最终类和方法现在是可能的。 baeldung.com/mockito-final 这在 android studio 中似乎对我不起作用,对你有用吗?
  • @Aouledlssa 不,它不会工作。您的单元测试是用 kotlin 编写的,因此内联函数会被编译器内联(不使用翻译后的最终函数)。您可以尝试用 java 编写测试,但我不确定它是否有效。
  • 这是一件可悲的事情。你知道其他支持内联函数的模拟框架吗?
猜你喜欢
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
相关资源
最近更新 更多