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");
}
}
}
我有以下情况:
我有一个 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");
}
}
}