在单元测试中测试私有方法。

huangapple go评论68阅读模式
英文:

Testing a private method in unit testing

问题

我知道这个问题的答案是:你不测试私有方法,而只测试最终会导致调用该私有方法的公有方法。

但是,在我的情况下,公共方法实际上启动了一个与kafka的消费者/连接,因此我只想测试接收kafka消息时执行的逻辑。
我不想将逻辑方法公开,因为除了kafka基础设施之外没有人会使用它,但我仍然想对其中执行的逻辑进行单元测试。

最佳实践解决方案是什么?如果需要,我可以更改代码。

以下是一些示例:

有问题的私有方法

private void handleConsumerRecord(ConsumerRecord<PK, byte[]> cr, Acknowledgment acknowledgment) throws IOException {
    //要测试的逻辑
}

调用私有逻辑方法的私有方法

/**
 * 初始化kafka消息监听器
 */
private void initConsumerMessageListenerContainer(ProducerFactory<PK, V> producerFactory) {
    if (!processAsBatch) {
        // 启动确认消息监听器以允许手动提交
        acknowledgingMessageListener =  (cr, acknowledgment) -> {
            try {
                handleConsumerRecord(cr, acknowledgment);
            } catch (IOException e) {
                log.error("Failed to handle consumed message, commiting message and performing irrecoverableException actions");
                exceptionHandlerManager.getIrrecoverableExceptionHandler().performAction(null, cr.value(), cr.topic(), cr.key());
            }
        };

        // 启动和初始化消费者容器
        container = initContainer(acknowledgingMessageListener, producerFactory);
    }
}

这是启动一切的公共方法

/**
 * 启动消息消费者
 * 记录事件将委托给onMessage()
 */
public void start(ProducerFactory<PK, V> producerFactory) {
    initConsumerMessageListenerContainer(producerFactory);
    container.start();
}

我尝试编写的单元测试

kafkaByteArrConsumer.getAcknowledgingMessageListener().onMessage(record, acknowledgment);
doThrow(TemporaryException.class).when(kafkaByteArrConsumer).getConsumerMessageLogic().onMessage(record.value(), acknowledgment);
Mockito.verify(exceptionHandlerManager.getTemporaryExceptionHandler(), Mockito.times(1))
        .performAction();

正如你所看到的,getAcknowledgingMessageListener 将不会由 initConsumerMessageListenerContainer() 初始化,因此我将无法在模拟 .getConsumerMessageLogic().onMessage 时访问 handleConsumer 方法(这是由 "//要测试的逻辑" 部分调用的方法)。

英文:

I know the answer to this question is: you do not test private method but only the public method that will eventually lead to that private method invocation.

BUT
In my case, the public method actually starting up a consumer/connection to kafka, so i only want to test the logic done when a kafka message is received.
I do not want to make the logic method public as no one will use it out side of the kafka infrastructure, but still i want to unit test the logic done there.

What is the best practice solution? I can change the code if need be

Heres some examples:

the private method in question

 private void handleConsumerRecord(ConsumerRecord<PK, byte[]> cr, Acknowledgment acknowledgment) throws IOException {
//logic to be tested
}

the private method that calls the private logic method

/**
     * Initialize the kafka message listener
     */
    private void initConsumerMessageListenerContainer(ProducerFactory<PK, V> producerFactory) {
        if (!processAsBatch) {
            // start a acknowledge message listener to allow the manual commit
            acknowledgingMessageListener =  (cr, acknowledgment) -> {
                try {
                    handleConsumerRecord(cr, acknowledgment);
                } catch (IOException e) {
                    log.error("Failed to handle consumed message, commiting message and performing irrecoverableException actions");
                    exceptionHandlerManager.getIrrecoverableExceptionHandler().performAction(null, cr.value(), cr.topic(), cr.key());
                }
            };

            // start and initialize the consumer container
            container = initContainer(acknowledgingMessageListener, producerFactory);
}

and this is the public method that starts everything

/**
     * Start the message consumer
     * The record event will be delegate on the onMessage()
     */
    public void start(ProducerFactory<PK, V> producerFactory) {
        initConsumerMessageListenerContainer(producerFactory);
        container.start();
    }

The unit test i tried to write

    kafkaByteArrConsumer.getAcknowledgingMessageListener().onMessage(record, acknowledgment);
        doThrow(TemporaryException.class).when(kafkaByteArrConsumer).getConsumerMessageLogic().onMessage(record.value(), acknowledgment);
        Mockito.verify(exceptionHandlerManager.getTemporaryExceptionHandler(), Mockito.times(1))
                .performAction();

As you can see, the getAcknowledgingMessageListener will not be initialized by the initConsumerMessageListenerContainer() and therefore i wont be able to access the handleConsumer method when mocking .getConsumerMessageLogic().onMessage

(which is called by the //some logic to be tested part)

答案1

得分: 1

一个很好的妥协方案是将您的方法更改为protected,甚至是package-private,然后将您的单元测试放在同一个包中。单元测试类不必位于相同的根目录下。假设您的源代码位于“source”文件夹下,而测试位于“test”文件夹下。只需将测试类放置在同一个包中。关于这个问题可以在这个问题中找到非常详细的答案:我们如何测试package-private类?。您的问题可能是该问题的重复。

英文:

A good compromise would be to change your method to protected or even to package-private, and then have your unit test located in the same package. The unit-test class doesn't have to reside under the same root. Say your source resides under "source" folder and tests reside under "test" folder. Just have the testing class reside in the same package. A very detailed answer could be found in this question: How can we test package-private class?
. Your question may be a duplicate of that question

答案2

得分: 0

好的,以下是您要翻译的内容:

嗯,有些人使用PowerMock来测试私有方法等,但我始终建议避免那样做。也许您应该考虑稍微更改您的逻辑,甚至更好地考虑使用Kafka的MockConsumer等。

英文:

Well, there are people who use PowerMock to test private methods etc. but I always recommend to avoid that. Maybe you should consider change your logic a little there or even better consider Kafka MockConsumer etc.

huangapple
  • 本文由 发表于 2020年9月30日 18:23:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64135554.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定