如何对参数中的回调进行单元测试 - Kotlin

How to unit test a callback in parameter - Kotlin

我有一个客户端 class(在 Android 应用程序中用 Kotlin 编写)实现了一个接口 ReadyCallback(在应用程序库中用 Java 编写) ,该应用程序依赖于此库)。在客户端中,我有一个 createClient() 方法,它将创建一个带有 ReadyCallback 参数的客户端。如果准备好了,我会通过调用classC.otherMethod()来执行其他任务,如果没有准备好,我只是创建客户端而不做其他事情:

在图书馆:

// Somewhere in this library, I have logic to call `readyCallback.onReady()` when I consider it's "ready"
interface ReadyCallback {
    void onReady()
}

class Manager {
    private final ReadyCallback readyCallback;
    public void onConnected(final boolean isConnected) {
        if (isConnected) {
            readyCallback.onReady();
        }
    }
}

在应用程序中:

class ClassA internal constructor(private val clientProvider: ClassB, private val classC: ClassC, private val classD: ClassD) : ReadyCallback {
    fun createClient() {
        val client = clientProvider.create(getReadyCallback())
    }

    private fun getReadyCallback() {
        return ReadyCallback { onReady() }
    }

    override fun onReady() {
        logInfo { "It's ready! Now do some stuff by calling classC.otherMethod()" }
        classC.otherMethod()
    }
}

在单元测试中,我想验证当我创建客户端并准备就绪时,classC 的 otherMethod() 将被调用。我尝试执行以下操作,但它不正确:

import com.nhaarman.mockitokotlin2.*
import org.junit.*

class ClassATest {
    lateinit var unitUnderTest: ClassA
    lateinit var clientProviderMock: ClassB
    lateinit var classCMock: ClassC
    lateinit var clientMock: ClassD

    @Before
    override fun setup() {
        super.setup()
        clientProviderMock = mock()
        classCMock = mock()
        clientMock = mock()
        unitUnderTest = ClassA(clientProvider = clientProviderMock, classC = classCMock, classD = classDMock)

        whenever(clientProviderMock.create(any()).thenReturn(client)
    }

    @Test
    fun `when create client then call otherMethod`() {
        unitUnderTest.createClient()
        verify(classCMock).otherMethod()
    }
}

错误信息显示:

Wanted but not invoked:
classC.otherMethod();
Actually, there were zero interactions with this mock.

我认为我收到此错误的原因是,如果我不调用 getReadyCallback(),这意味着我没有调用回调,所以没有调用 classC.otherMethod()。但除此之外我真的坚持这一点,我不知道如何对我的欲望行为进行单元测试(如果准备好了, classC.otherMethod() 将被调用,如果没有准备好,这个方法将不会被调用).

我知道我不能做下面这样的事情,因为 unitUnderTest 不是模拟对象:

callbackMock = mock()
whenever(unitUnderTest.getReadyCallback()).thenReturn(callbackMock)
whenever(clientProviderMock.create(callbackMock).thenReturn(client)

有人可以帮我吗?

我能想到的唯一方法是在回调的 onReady() 方法中添加一个布尔标志。所以会变成:

图书馆中:

interface ReadyCallback {
    void onReady(final boolean isReady)
}

class Manager {
    private final ReadyCallback readyCallback;
    public void onConnected(final boolean isConnected) {
        if (isConnected) {
            readyCallback.onReady(true);
        } else {
            readyCallback.onReady(false);
        }
    }
}

在应用程序中:

class ClassA internal constructor(private val clientProvider: ClassB, private val classC: ClassC, private val classD: ClassD) : ReadyCallback {
    fun createClient() {
        val client = clientProvider.create(getReadyCallback())
    }

    private fun getReadyCallback() {
        return ReadyCallback { isReady -> onReady(isReady) }
    }

    override fun onReady(isReady: Boolean) {
        if (isReady) {
            logInfo { "It's ready! Now do some stuff by calling classC.otherMethod()" }
            classC.otherMethod()
        }
    }
}

在单元测试中:

import com.nhaarman.mockitokotlin2.*
import org.junit.*

class ClassATest {
    lateinit var unitUnderTest: ClassA
    lateinit var clientProviderMock: ClassB
    lateinit var classCMock: ClassC
    lateinit var clientMock: ClassD

    @Before
    override fun setup() {
        super.setup()
        clientProviderMock = mock()
        classCMock = mock()
        clientMock = mock()
        unitUnderTest = ClassA(clientProvider = clientProviderMock, classC = classCMock, classD = classDMock)

        whenever(clientProviderMock.create(any()).thenReturn(client)
    }

    @Test
    fun `when create client and ready then call otherMethod`() {
        unitUnderTest.onReady(true)
        unitUnderTest.createClient()
        verify(classCMock).otherMethod()
    }

    @Test
    fun `when create client and not ready then do not call otherMethod`() {
        unitUnderTest.onReady(false)
        unitUnderTest.createClient()
        verifyZeroInteractions(classCMock)
    }
}

但我仍然不知道如何在回调方法中没有布尔参数的情况下进行测试。有人知道怎么做吗?

我想我明白了。我不需要 onReady() 中的参数。 在图书馆:

interface ReadyCallback {
    void onReady()
}

// place to determine when is "ready"
class Manager {
    private final ReadyCallback readyCallback;
    public void onConnected(final boolean isConnected) {
        if (isConnected) {
            readyCallback.onReady();
        }
    }
}

在应用程序中:

class ClassA internal constructor(private val clientProvider: ClassB, private val classC: ClassC, private val classD: ClassD) : ReadyCallback {
    fun createClient() {
        val client = clientProvider.create(getReadyCallback())
    }

    private fun getReadyCallback() {
        return ReadyCallback { onReady() }
    }

    override fun onReady() {
        logInfo { "It's ready! Now do some stuff by calling classC.otherMethod()" }
        classC.otherMethod()
    }
}

在单元测试中:

import com.nhaarman.mockitokotlin2.*
import org.junit.*

class ClassATest {
    lateinit var unitUnderTest: ClassA
    lateinit var clientProviderMock: ClassB
    lateinit var classCMock: ClassC
    lateinit var clientMock: ClassD

    @Before
    override fun setup() {
        super.setup()
        clientProviderMock = mock()
        classCMock = mock()
        clientMock = mock()
        unitUnderTest = ClassA(clientProvider = clientProviderMock, classC = classCMock, classD = classDMock)

        whenever(clientProviderMock.create(any()).thenReturn(client)
    }

    @Test
    fun `when create client and ready then call otherMethod`() {
        unitUnderTest.onReady()
        unitUnderTest.createClient()
        verify(classCMock).otherMethod()
    }

    @Test
    fun `when create client and not ready then do not call otherMethod`() {
        unitUnderTest.createClient()
        verifyZeroInteractions(classCMock)
    }
}