JUnit5:将对对象的每个方法调用转发给包装函数

JUnit5: Forward every method call on object to wrapper function

我有以下情况:

我有一个 class 我想用 JUnit/Mockito 进行测试,但我想添加一些应该在对该对象的每个方法调用时执行的代码。

例如,如果对被测试的 class 的调用花费的时间超过特定时间,我希望测试失败(忽略与 @Timeout 类似的功能已经存在,它只是一个简单的例子)。到目前为止,我所做的是编写一个方法,该方法将 method/lambda 作为参数并执行此操作。调用将如下所示:

Object result = failOnTimeout( () -> testedInstance.calledMethod(x, y) );

缺点是我需要记住包装每个方法调用。有没有更好的方法来规避这个?我想要的理想行为是这样的:

@BeforeAll
public void setup() {
    testedInstace = new testedClass();
    wrapMethodCall(testedInstance, failOnTimeout);
}

@Test
public void acualTest() {
    Object result = testedInstance.calledMethod(x, y); //Implicitly calls 'failOnTimeout'
    // ...
}

如果有人能告诉我是否存在可以解决我的问题的这个或功能,我将不胜感激。

在您的情况下,我看到了一些明显的解决方案。 假设我们有这样一个 class:

public class TestedClass {
    public void calledMethod(int x, int y) {
        System.out.println("Hello, Alexander");
    }
}

Mockito

首先,您可以使用 Mockito 方法。并使用 org.mockito.stubbing.Answer class 和 Mockito.spy(...) 方法。例如:

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doAnswer;

public class TestedClassTestMockitoWay {
    @Test
    void calledMethodTest() {
        TestedClass original = new TestedClass();
        TestedClass spy = Mockito.spy(original);
        Answer<Void> answer = new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocationOnMock) {
                timeoutWrapper(() -> {
                    try {
                        invocationOnMock.callRealMethod();
                    } catch (Throwable ignore) {
                    }
                });
                return null;
            }
            private void timeoutWrapper(Runnable runnable) {
                //todo: timeout implementation
                System.out.println("before");
                runnable.run();
                System.out.println("after");
            }
        };
        doAnswer(answer).when(spy).calledMethod(anyInt(), anyInt());
        spy.calledMethod(1, 2);
    }
}

继承

第二个选项是测试的继承class。如果您测试的 class 不是最终的,那么您可以继承它。例如:

import org.junit.jupiter.api.Test;

class TestedClassTestInheritanceWay {
    @Test
    void calledMethodTest() {
        TimeoutTestedClass testedClass = new TimeoutTestedClass();
        testedClass.calledMethod(1, 2);
    }

    private static class TimeoutTestedClass extends TestedClass {
        @Override
        public void calledMethod(int x, int y) {
            timeoutWrapper(() -> super.calledMethod(x, y));
        }

        private void timeoutWrapper(Runnable runnable) {
            //todo: timeout implementation
            System.out.println("before");
            runnable.run();
            System.out.println("after");
        }
    }
}

装修

或者如果您测试的 class 是最终的。你可以装饰它们:

import org.junit.jupiter.api.Test;

public class TestedClassTestCompositionWay {

    @Test
    void calledMethodTest() {
        TestedClass original = new TestedClass();
        TimeoutTestedClass testedClass = new TimeoutTestedClass(original);
        testedClass.calledMethod(1, 2);
    }

    private static class TimeoutTestedClass {

        private final TestedClass testedClass;

        private TimeoutTestedClass(TestedClass testedClass) {
            this.testedClass = testedClass;
        }

        public void calledMethod(int x, int y) {
            timeoutWrapper(() -> testedClass.calledMethod(x, y));
        }

        private void timeoutWrapper(Runnable runnable) {
            //todo: timeout implementation
            System.out.println("before");
            runnable.run();
            System.out.println("after");
        }

    }
}