如何从外部依赖中模拟受保护的超类方法

问题描述 投票:0回答:1

我正在尝试测试一个方法,该方法在内部调用父类的受保护方法(外部依赖)。

考虑这个简化的例子:

public class Parent {

    protected void parentAction() {
        System.out.println("Parent action");
    }
}
public class Child extends Parent {

    private final Runnable backupAction;
    private final Runnable normalAction;

    public Child(Runnable backupAction, Runnable normalAction) {
        this.backupAction = backupAction;
        this.normalAction = normalAction;
    }

    public void childAction() {
        try {
            super.parentAction();
        } catch (Exception exc) {
            this.backupAction.run();
            return;
        }
        this.normalAction.run();
    }
}

我想测试一下,在异常情况下,会调用备份操作,而正常情况下则不会 - 使用

verify()
这很容易。前提是模拟
super.parentAction()
抛出异常。

这个答案不适用于我的用例,因为父类是外部依赖项,我无权访问该包(我无法在测试文件夹中创建相同的包)。

java mockito junit5
1个回答
0
投票

考虑到您必须遵循的约束,我认为一种选择可能是创建特定于测试的实现来覆盖

parentAction
以覆盖特定的用例。

class ChildTest {

    private Runnable normalAction = Mockito.mock(Runnable.class);
    private Runnable backupAction = Mockito.mock(Runnable.class);

    @Test
    public void shouldUseNormalWhenNoExceptionThrown() {

        Child child = new Child(normalAction, backupAction) {
            @Override
            protected void parentAction() throws Exception {
                System.out.println("invoked");
            }
        };
        child.childAction();

        Mockito.verify(normalAction).run();
        Mockito.verify(backupAction, never()).run();
    }

    @Test
    public void shouldUseBackupWhenExceptionThrown() {

        Child child = new Child(normalAction, backupAction) {

            @Override
            public void childAction() {
                super.childAction();
            }

            @Override
            protected void parentAction() throws Exception {
                throw new Exception();
            }
        };
        child.childAction();

        Mockito.verify(backupAction).run();
        Mockito.verify(normalAction, never()).run();
    }
}

请注意,这需要更改您的

Child
类以避免调用
super.parentAction()
,而只使用
parentAction()
来代替 – 否则它将始终调用
Parent
中的方法而不是测试中的实现:

public void childAction() {
    try {
        parentAction();
        this.normalAction.run();
    } catch (Exception e) {
        this.backupAction.run();
    }
}

作为替代方案,您可以使用 PowerMock 来模拟受保护的方法:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Child.class})
class ChildTest {

    private Runnable normalAction = Mockito.mock(Runnable.class);
    private Runnable backupAction = Mockito.mock(Runnable.class);

    @Test
    public void shouldUseNormalWhenNoExceptionThrown() throws Exception {

        Child child = PowerMockito.spy(new Child(normalAction, backupAction));
        PowerMockito.doNothing().when(child, "parentAction", new Object[] {});

        child.childAction();

        Mockito.verify(normalAction).run();
        Mockito.verify(backupAction, never()).run();
    }

    @Test
    public void shouldUseBackupWhenExceptionThrown() throws Exception {

        Child child = PowerMockito.spy(new Child(normalAction, backupAction));
        doThrow(new Exception()).when(child, "parentAction");
        child.childAction();

        Mockito.verify(backupAction).run();
        Mockito.verify(normalAction, never()).run();
    }
}

但是,请注意,如果您使用的是 JDK17+,则需要向编译器指定额外的参数 - 请参阅Powermock 与 JDK 17 的兼容性

© www.soinside.com 2019 - 2024. All rights reserved.