Java Mockito 如何只模拟超类方法的调用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3467801/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mockito How to mock only the call of a method of the superclass
提问by mada
I'm using Mockito in some tests.
我在一些测试中使用 Mockito。
I have the following classes:
我有以下课程:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
//some code
super.save();
}
}
I want to mock only the second call (super.save
) of ChildService
. The first call must call the real method. Is there a way to do that?
我想嘲笑只有第二个呼叫(super.save
的)ChildService
。第一次调用必须调用真正的方法。有没有办法做到这一点?
采纳答案by iwein
No, Mockito does not support this.
不,Mockito 不支持这一点。
This might not be the answer you're looking for, but what you're seeing is a symptom of not applying the design principle:
这可能不是您正在寻找的答案,但您所看到的是不应用设计原则的症状:
If you extract a strategy instead of extending a super class the problem is gone.
如果您提取策略而不是扩展超类,问题就消失了。
If however you are not allowed to change the code, but you must test it anyway, and in this awkward way, there is still hope. With some AOP tools (for example AspectJ) you can weave code into the super class method and avoid its execution entirely (yuck). This doesn't work if you're using proxies, you have to use bytecode modification (either load time weaving or compile time weaving). There are be mocking frameworks that support this type of trick as well, like PowerMock and PowerMockito.
但是,如果您不允许更改代码,但无论如何您必须对其进行测试,并且以这种尴尬的方式,仍然有希望。使用一些 AOP 工具(例如 AspectJ),您可以将代码编织到超类方法中并完全避免其执行(糟糕)。如果您使用代理,这不起作用,您必须使用字节码修改(加载时编织或编译时编织)。还有一些模拟框架也支持这种技巧,比如 PowerMock 和 PowerMockito。
I suggest you go for the refactoring, but if that is not an option you're in for some serious hacking fun.
我建议您进行重构,但如果这不是一个选项,那么您将获得一些严肃的黑客乐趣。
回答by jgonian
If you really don't have a choice for refactoring you can mock/stub everything in the super method call e.g.
如果你真的没有选择重构,你可以模拟/存根超级方法调用中的所有内容,例如
class BaseService {
public void validate(){
fail(" I must not be called");
}
public void save(){
//Save method of super will still be called.
validate();
}
}
class ChildService extends BaseService{
public void load(){}
public void save(){
super.save();
load();
}
}
@Test
public void testSave() {
ChildService classToTest = Mockito.spy(new ChildService());
// Prevent/stub logic in super.save()
Mockito.doNothing().when((BaseService)classToTest).validate();
// When
classToTest.save();
// Then
verify(classToTest).load();
}
回答by Luke
create a package protected (assumes test class in same package) method in the sub class that calls the super class method and then call that method in your overridden sub class method. you can then set expectations on this method in your test through the use of the spy pattern. not pretty but certainly better than having to deal with all the expectation setting for the super method in your test
在调用超类方法的子类中创建一个受保护的包(假设测试类在同一包中)方法,然后在覆盖的子类方法中调用该方法。然后,您可以通过使用间谍模式在测试中设置对此方法的期望。不漂亮但肯定比在测试中处理超级方法的所有期望设置要好
回答by Mohammed Misbahuddin
Consider refactoring the code from ChildService.save() method to different method and test that new method instead of testing ChildService.save(), this way you will avoid unnecessary call to super method.
考虑将 ChildService.save() 方法的代码重构为不同的方法并测试该新方法而不是测试 ChildService.save(),这样您将避免对超级方法的不必要调用。
Example:
例子:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
newMethod();
super.save();
}
public void newMethod(){
//some codes
}
}
回答by zoufeiyy
The reason is your base class is not public-ed, then Mockito cannot intercept it due to visibility, if you change base class as public, or @Override in sub class (as public), then Mockito can mock it correctly.
原因是你的基类不是public-ed,然后Mockito由于可见性无法拦截它,如果你将基类更改为public,或者子类中的@Override(作为public),那么Mockito可以正确地模拟它。
public class BaseService{
public boolean foo(){
return true;
}
}
public ChildService extends BaseService{
}
@Test
@Mock ChildService childService;
public void testSave() {
Mockito.when(childService.foo()).thenReturn(false);
// When
assertFalse(childService.foo());
}
回答by Rubasace
Maybe the easiest option if inheritance makes sense is to create a new method (package private??) to call the super (lets call it superFindall), spy the real instance and then mock the superFindAll() method in the way you wanted to mock the parent class one. It's not the perfect solution in terms of coverage and visibility but it should do the job and it's easy to apply.
如果继承有意义,也许最简单的选择是创建一个新方法(包私有??)来调用超级(让我们称之为 superFindall),监视真实实例,然后以您想要模拟的方式模拟 superFindAll() 方法父类一。就覆盖范围和可见性而言,这不是完美的解决方案,但它应该可以完成工作并且易于应用。
public Childservice extends BaseService {
public void save(){
//some code
superSave();
}
void superSave(){
super.save();
}
}
回答by dams50
Even if i totally agree with iwein response (
即使我完全同意 iwein 的回应(
favor composition over inheritance
偏向于组合而不是继承
), i admit there are some times inheritance seems just natural, and i don't feel breaking or refactor it just for the sake of a unit test.
),我承认有时候继承看起来很自然,我不觉得为了单元测试而破坏或重构它。
So, my suggestion :
所以,我的建议:
/**
* BaseService is now an asbtract class encapsulating
* some common logic callable by child implementations
*/
abstract class BaseService {
protected void commonSave() {
// Put your common work here
}
abstract void save();
}
public ChildService extends BaseService {
public void save() {
// Put your child specific work here
// ...
this.commonSave();
}
}
And then, in the unit test :
然后,在单元测试中:
ChildService childSrv = Mockito.mock(ChildService.class, Mockito.CALLS_REAL_METHODS);
Mockito.doAnswer(new Answer<Void>() {
@Override
public Boolean answer(InvocationOnMock invocation)
throws Throwable {
// Put your mocked behavior of BaseService.commonSave() here
return null;
}
}).when(childSrv).commonSave();
childSrv.save();
Mockito.verify(childSrv, Mockito.times(1)).commonSave();
// Put any other assertions to check child specific work is done