有没有办法合法地转换 Mockito 模拟对象?

Is there a way to legally cast a Mockito mock object?

我在 Java 11、JUnit Jupiter 5.6.0、Mockito 3.3.3 和 Java Discord 中为我的小型 Discord 机器人编写了几个测试用例 classes API 4.1.1_122.

我意识到我的测试用例有多个重复行为实例,包括用于相同目的的相等模拟的声明,以及 given([method call]).willReturn([object]); 在我的 @BeforeEach 启动方法中的重复。我希望将重复的行为分成基础 classes 来组织自己,但我被卡住了。 我所有的测试 classes 都在 Java Discord API 框架中测试某种事件处理程序,所以我制作了一个名为 GenericEventTest 的顶级 class:

public abstract class GenericEventTest {
    @Mock
    protected GenericEvent eventMock;

    @Mock
    protected JDA jdaMock;

    protected GenericEventTest() {
        MockitoAnnotations.initMocks(this);

        given(   eventMock.getJDA()   )
                .willReturn(jdaMock);
    }
}

我希望为事件实现一个高级模拟以多态地适合我的测试 classes,然后将指定的 given() 命令放入它所属的 class ,因为我所有的测试都重复同一行。 接下来,我意识到我所有的测试都是在测试与 Discord 中的消息相关的某种事件。所以我扩展了 GenericEvent 并制作了 GuildMessageEventTest:

public abstract class GuildMessageEventTest extends GenericEventTest {
    @Mock
    protected GenericGuildMessageEvent eventMock;

    @Mock
    protected TextChannel eventChannelMock;

    @Mock
    protected MessageAction messageActionMock;

    protected GuildMessageEventTest() {
        super();

        MockitoAnnotations.initMocks(this);

        given(   eventMock.getChannel()   )
                .willReturn(eventChannelMock);

        given(   eventChannelMock.sendMessage(anyString())   )
                .willReturn(messageActionMock);
    }
}

我的想法是在此 class 中实施下一级事件,GenericGuildMessageEvent 将应用与 GenericEvent 相同的 given() 规则,并且然后在此 class 中添加适当的 given() 规则。但我相信我已经找到了我的问题。

当我声明 @Mock protected GenericGuildMessageEvent eventMock; 时,我正在 遮挡 来自 GenericEventTestGenericEvent eventMock。因此,我正在声明 GenericGuildMessageEvent 实例,但我不会 保留 GenericEventTest 中附加到模拟的 given() 规则。像 @Mock protected GenericGuildMessageEvent eventMock = (GenericGuildMessageEvent) super.eventMock; 这样的操作是行不通的,因为用 @Mock 注释的对象不能以这种方式合法地转换。

我的问题是:如何在让 Mockito 复制附加到 GenericEventgiven() 规则的同时合法地将我的 GenericEvent 实例向上转换为 GenericGuildMessageEvent ] super中的实例class?

在您的 GenericEventTest 中声明一个 abstract GenericEvent getEventMock(); 方法并让 GuildMessageEventTest 实施它。然后将实例字段 protected GenericEvent eventMock 从您的基础 class 移动到子 class。使用 getEventMock() 访问基地 class 中的模拟。请注意,您的子 class 中不再需要 MockitoAnnotations.initMocks(this);。这将覆盖来自您的基地 class.

的模拟行为
// GenericEventTest
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.BDDMockito.given;

public abstract class GenericEventTest {

    protected GenericEventTest() {
        MockitoAnnotations.initMocks(this);

        given(getEventMock().getJDA()).willReturn("jdaMock");
    }

    abstract GenericEvent getEventMock();
}

// GuildMessageEventTest
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.BDDMockito.given;

public abstract class GuildMessageEventTest extends GenericEventTest {
    @Mock
    protected GenericGuildMessageEvent eventMock;

    @Mock
    protected GenericGuildMessageEvent myMock;

    @Override
    public GenericGuildMessageEvent getEventMock() {
        return eventMock;
    }

    protected GuildMessageEventTest() {
        super();

//        MockitoAnnotations.initMocks(this);

        given(eventMock.getChannel())
                .willReturn("eventChannelMock");

    }
}

//The Test
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

public class ActualMessageTest extends GuildMessageEventTest {

    @org.junit.jupiter.api.Test
    public void testX() {
        assertEquals("eventChannelMock", eventMock.getChannel());
        assertEquals("jdaMock", eventMock.getJDA());
        assertNotNull(myMock);
    }

}