【问题标题】:How to mock private method which uses inject objects in java for unit test?如何模拟在java中使用注入对象进行单元测试的私有方法?
【发布时间】:2023-03-20 22:50:02
【问题描述】:

为了测试我的DedupServiceWrapperImpl 类,我需要模拟私有方法“isDedupEnabled”和“isNew”。这是被测试的类:

public class DedupServiceWrapperImpl implements DedupServiceWrapper {

    @Inject
    private DedupService dedupService;

    @Inject
    private EventTypeService eventTypeService;

    @Inject
    private MessageLogService messageLogService;

    private static final Logger logger = LoggerFactory.getLogger(DedupServiceWrapperImpl.class);

public Message dedup(Message message) {
   if (isDedupEnabled(eventTypeName)) {
     System.out.println("Dedup is enabled.");
     if (isNew(digest)) {
         System.out.println("it is new");
     } else {
        System.out.println("is not new");
     }
   } else {
       System.out.println("Dedup is not Enabled");
   }
}

private boolean isNew(Digest digest) {
        return !dedupService.checkIfExists(digest.getDigest());
    }

    private boolean isDedupEnabled(String eventTypeName) {
        boolean result = false;
        try {
            logger.info("Fetching eventType:{} to see if dedup is enabled on it.", eventTypeName);
            EventType eventType = eventTypeService.findByName(eventTypeName);
            if (eventType.isDedupEnabled()) {
                result = true;
            }
        } catch (NotFoundException nfe) {
            logger.warn("EventType '{}' not found!", eventTypeName);
        }
        return result;
    }
}

这是我的单元测试类:

import com.sun.org.apache.bcel.internal.classfile.Method;
import com.tosan.chapar.core.exception.NotFoundException;
import com.tosan.chapar.core.service.DedupService;
import com.tosan.chapar.core.service.MessageLogService;
import com.tosan.chapar.ivr.service.DedupServiceWrapper;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.messaging.MessageHeaders;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
//import org.junit.Test;
import org.springframework.messaging.Message;

import static org.junit.runner.Request.method;
import static org.mockito.Mockito.*;
import static org.testng.Assert.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest(DedupServiceWrapperImpl.class)
public class DedupServiceWrapperImpl_DedupUTest {

    private static final String payload = "Hello UTest.";
    private Message inputMessage;
    private MessageHeaders messageHeaders;
    //@Mock
    //private DedupService dedupService;
    //@InjectMocks
    private DedupServiceWrapper dedupServiceWrapper;

    @Test
    public void testName() throws Exception {

    }

    @SuppressWarnings("unchecked")
    @BeforeMethod
    public void setupMocks() throws NotFoundException {
        MessageLogService messageLogService = mock(MessageLogService.class);
        /*digest = mock(Digest.class);
        eventType = mock(EventType.class);
        eventType.setName("modern_chaparbo_free");
        eventType.setDedupEnabled(true);

        eventTypeService = mock(EventTypeServiceImpl.class);
        when(eventTypeService.findByName("modern_chaparbo_free")).thenReturn(eventType);*/

        messageHeaders = mock(MessageHeaders.class);
        when(messageHeaders.get("eventTypeName")).thenReturn("modern_chaparbo_free");
        when(messageHeaders.get("recipient")).thenReturn("+982112345678");
        when(messageHeaders.size()).thenReturn(2);

        inputMessage = mock(Message.class);
        when(inputMessage.getHeaders()).thenReturn(messageHeaders);
        when(inputMessage.getPayload()).thenReturn(payload);
    }

    /**
     * dedupEnabled is false
     */
    @Test//(expected = RuntimeException.class)
    public void dedup_Disablededup_dedupeIsNotDone()  throws Exception{
        //doNothing().when(cmd).dnsCheck(HOST, any(InetAddressFactory.class))
        DedupServiceWrapperImpl spy = PowerMockito.spy(new DedupServiceWrapperImpl());
        //DedupServiceWrapper spy = mock(DedupServiceWrapperImpl.class);
        PowerMockito.doReturn(false).when(spy, "isDedupEnabled", anyString(),);
        //PowerMockito.verifyPrivate(spy, times(0)).invoke("isDedupEnabled", anyString());
        //PowerMockito.when(spy, method(DedupServiceWrapperImpl.class, "isDedupEnabled", String.class)).withArguments(anyString()).thenReturn(false);
        //PowerMockito.verifyPrivate(spy, times(2)).invoke("isDedupEnabled", anyString());
        //PowerMockito.doReturn(false).when(spy, method(DedupServiceWrapperImpl.class, "isDedupEnabled", String.class)).withArguments(anyString());
        /*DedupServiceWrapper truck = new DedupServiceWrapperImpl();
        DedupServiceWrapper truckSpy = PowerMockito.spy(truck);
        PowerMockito.doReturn(false).when(truckSpy,
                "isDedupEnabled");*/
        Message message = spy.dedup(inputMessage);
        org.junit.Assert.assertEquals(inputMessage, message);
        assertNotNull(message);
        assertEquals(message, inputMessage);
    }


}

我已经在我的测试类中尝试了所有示例,但它们都不起作用,我让它们评论......

我不能模拟两个私有方法 - 我该怎么做?为什么它不起作用?

它抛出这个:

Method threw 'org.mockito.exceptions.misusing.WrongTypeOfReturnValue' exception. Cannot evaluate com.tosan.chapar.ivr.service.impl.DedupServiceWrapperImpl$$EnhancerByMockitoWithCGLIB$$9a511df1.toString()

【问题讨论】:

  • 为什么要模拟私有方法?唯一可以调用它的是模拟对象中的其他方法
  • 不清楚你为什么要这样做。单元测试需要模拟依赖然后调用 CUT 上的方法。然后,您验证 CUT 的行为方式是否正确 - 在依赖项上调用正确的方法,根据您存根的内容返回正确的值。没有理由在 CUT内部模拟 private 方法!
  • 我想测试调用私有方法“isDedupEnabled”和“isNew”的“dedup(Message message)”方法。我想模拟这些私有方法。
  • 尽管这听起来像是不好的做法(私有方法是一个实现细节),但我什至不确定这是否可行 - 你想模拟你正在测试的对象吗?
  • @m.mjn202 不,您真的不想模拟私有方法。例如,isDedupEnabled 调用 eventTypeService.findByName(eventTypeName) - 所以您 stub eventTypeService.findByName 为该调用提供正确的值,因为您正在测试什么。

标签: java unit-testing junit testng powermockito


【解决方案1】:

可以使用 PowerMock 模拟私有方法,或者您可以考虑使用 Mockito 间谍进行部分模拟。

但大多数情况下,需要这些技巧是测试中的设计不足。换句话说:而不是试图修复难以测试的生产代码的症状......而是花时间让你的代码易于测试。

【讨论】:

  • 我认为第二段比第一段更重要。
  • 但是您可以使用其中任何一种机制来模拟您要测试的对象吗?
  • @OliverCharlesworth 你可以。然而,这是一件非常奇怪的事情......
  • @GhostCat,谢谢...我已经通过编辑我的 pom.xml 解决了它:|
【解决方案2】:

您在同一个类中混合了 TestNG (org.testng.annotations.*) 和 JUnit (org.junit.runner.*),这是不允许的。

如果你想使用 PowerMock 和 TestNG,你需要扩展PowerMockTestCase。查看文档:https://github.com/powermock/powermock/wiki/TestNG_usage

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 1970-01-01
    • 2011-12-09
    • 2018-08-01
    • 2022-01-23
    相关资源
    最近更新 更多