简体   繁体   中英

Is it possible to test arguments in mocked constructor with mockk?

Purpose

I want to test the result of a function of a class. And it returns an instance of an Android class, which should be mocked in a unit test since it seems to be stubbed in any unit test.

Example code

Here's a minimized example code of the simulated android class, AndroidData , and the class to be tested, MyHelper .

/**
 * Should be mocked in a unit test.
 */
class AndroidData(val value: Float)

/**
 * Target class to be tested.
 */
class MyHelper() {
    fun createData(flag: Boolean): AndroidData {
        // simplified logic
        return if (flag) AndroidData(20f) else AndroidData(10f)
    }
}

What I want to achieve is something like this( note that this is incorrect code ):

class MyHelperTest : TestCase() {
    @Test
    fun testCreateData() {
        mockkConstructor(AndroidData::class)
        val valueSlot = slot<Float>()
        every { constructedWith<AndroidData>(capture(valueSlot)) }  // Type mismatch in IDE
        val helper = MyHelper()
        
        val returnedData = helper.createData(true)
        assertTrue(returnedData.value >= 15f)
    }
}

I want to test that returned AndroidData instance has a correct value , or matches some conditions. I cannot find any tutorial about this case or any correct way to implement it.

What I tried

These 2 test cases would both fail.

    @Test
    fun testCreateData1() {
        mockkConstructor(AndroidData::class)

        val helper = mockk<MyHelper>(relaxed = true)
        val createdData = helper.createData(true)

        println("createdData=$createdData")  // AndroidData(child of #1#2)
        println("createdData.value=${createdData.value}")  // 0.0

        // test if the argument in AndroidData's constructor was larger than 15
        assertTrue(createdData.value >= 15f)  // assertion failed
    }

    @Test
    fun testCreateData2() {
        mockkConstructor(AndroidData::class)
        // raise error: Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock
        every {
            constructedWith<AndroidData>(LargerThan15Matcher())
        }

        val helper = mockk<MyHelper>(relaxed = true)
        val createdData = helper.createData(true)

        println("createdData=$createdData")  // AndroidData(child of #1#2)
        println("createdData.value=${createdData.value}")  // 0.0

        // test if the argument in AndroidData's constructor was larger than 15
        assertTrue(createdData.value >= 15f)
    }

The testing idea was pretty easy and intuitive for Python unittest in my experience. Yet it seems impossible in Java or Android? I haven't tried the mockito and roboletric library because I was told that mockk provides support on Android projects. Or I just haven't found the correct way to do this, or the whole testing idea is completely wrong? Please end my days of searching and struggle.

It is not really clear to me what you are trying to achieve, but I can try to help you with what I think I understand.

Let's start with your test-case testCreateData1 :

  • A relaxed mock in MockK tries to return a "simple value" (mostly "default" values or null if possible) for each function call of the mock.
  • Therefore, your relaxed mock of MyHelper returns AndroidData(0.0) which does not satisfy your assertion.

Regarding test-case testCreateData2 :

  • every {... } alone is not a proper expression, but it is intended to specify what should happen on every ... . For instance, if you have a mock m with a function f you could write something like the following:
every { m.f() } returns 42
every { m.f() } answers { callOriginal() }
  • In pretty much the same way, constructedWith<AndroidData>(LargerThan15Matcher()) is not a proper expression, because it is intended to specify what should happen with an AndroidData constructed with an argument matched by the LargerThan15Matcher . For instance, if you want to say that the value of such an object should always return 1 you could write
 every { constructedWith<AndroidData>(LargerThan15Matcher()).value } returns 1f

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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