简体   繁体   English

在 mockk 中模拟嵌套属性

[英]mock nested properties in mockk

I have a function which uses a field in DisplayMetrics of Resources of Context class:我有一个 function,它使用DisplayMetrics of Resources of Context class 中的一个字段:

fun getIconForDevice(context: Context, iconUrl: String): String {
    val metrics = context.resources.displayMetrics
    var suffix = ""
    //below checks MUST be in this increasing order or it may failed
    if (metrics.densityDpi <= DisplayMetrics.DENSITY_MEDIUM)
        suffix = "-m"
    else if (metrics.densityDpi <= DisplayMetrics.DENSITY_HIGH)
        suffix = "-h"
    else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XHIGH)
        suffix = "-xh"
    else if (metrics.densityDpi <= DisplayMetrics.DENSITY_XXHIGH || metrics.densityDpi > DisplayMetrics.DENSITY_XXHIGH)
        suffix = "-xxh"
    val pasvand = iconUrl.substring(iconUrl.lastIndexOf("."))
    val str = iconUrl.substring(0, iconUrl.lastIndexOf(".")) + suffix + pasvand
    return str
}

In order to test it, I need to mock Context and metrics.densityDpi to give it a value.为了测试它,我需要模拟Contextmetrics.densityDpi来给它一个值。
I'm using Mockk (1.9.3) library to do that.我正在使用Mockk (1.9.3)库来做到这一点。

@Test
fun getIconForDevice_ReturnsUrlWithXxhForXxhDisplay() {
    val context: Context = mockk(relaxed = true)
    every { context.resources.displayMetrics.densityDpi } returns 450
    assertEquals(IconHelper.getIconForDevice(context,
      "https://website.com/image.png"), "https://website.com/image-xxh.png")
}

Running the test following error stacktrace is given:给出以下错误堆栈跟踪运行测试:

java.lang.ClassCastException: java.lang.Integer cannot be cast to android.util.DisplayMetrics

    at ...Resources.getDisplayMetrics(Resources.java)
    at ....IconHelper.getIconForDevice(IconHelper.kt:39)
    at ...IconHelperTest.getIconForDevice_ReturnsUrlWithXxhForXxhDisplay(IconHelperTest.kt:30)

The first link refers to val metrics = context.resources.displayMetrics line of the actual function第一个链接是指实际 function 的val metrics = context.resources.displayMetrics

So how can I mock such a nested field in mockk?那么如何在 mockk 中模拟这样一个嵌套字段呢?
context.resources.displayMetrics.densityDpi

Your setup of mocking Context and densityDpi is correct.您对 mocking ContextdensityDpi的设置是正确的。

The test fails because you are calling getIconForDevice with a new mockk instead of your mocked Context .测试失败,因为您使用新的模拟调用getIconForDevice而不是模拟的Context

@Test
fun getIconForDevice_ReturnsUrlWithXxhForXxhDisplay() {
    val context: Context = mockk(relaxed = true)
    every { context.resources.displayMetrics.densityDpi } returns 450

    // pass in mocked context
    assertEquals(IconHelper.getIconForDevice(context, "https://website.com/image.png"), "https://website.com/image-xxh.png")
}

Update更新

Regarding your ClassCastException , because you are accessing the DisplayMetrics first:关于您的ClassCastException ,因为您首先访问DisplayMetrics

val metrics = context.resources.displayMetrics

I think you need to separate the mock of displayMetrics and densityDpi :我认为您需要将displayMetricsdensityDpi的模拟分开:

@Test
fun getIconForDevice_ReturnsUrlWithXxhForXxhDisplay() {
  val context: Context = mockk(relaxed = true)
  val displayMetrics: DisplayMetrics = mockk(relaxed = true) // maybe relaxed is not needed, I just put it here in case
  every { context.resources.displayMetrics } returns displayMetrics
  every { displayMetrics.densityDpi } returns 450

  assertEquals(IconHelper.getIconForDevice(context, "https://website.com/image.png"), "https://website.com/image-xxh.png")
}

In version 1.12.0 Run as unit test kotlin 1.5.10在 1.12.0 版本中作为单元测试运行 kotlin 1.5.10

    // WORKS
    val context: Context = mockk()
    every { context.resources.displayMetrics } returns DisplayMetrics().apply {

        this.densityDpi = DisplayMetrics.DENSITY_LOW
    }

ERROR - returns class java.lang.Integer cannot be cast to class android.util.DisplayMetrics ERROR - returns class java.lang.Integer cannot be cast to class android.util.DisplayMetrics

    val context: Context = mockk()
    every { context.resources.displayMetrics.densityDpi } returns DisplayMetrics.DENSITY_LOW

ERROR - returns Missing mocked calls inside every {... } block: make sure the object inside the block is a mock错误 - 在每个 {... } 块内返回缺失的模拟调用:确保块内的 object 是模拟的

    val context: Context = mockk()
    val displayMetrics: DisplayMetrics = mockk()
    every { context.resources.displayMetrics } returns displayMetrics
    every { displayMetrics.densityDpi } returns DisplayMetrics.DENSITY_LOW

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM