我的班级结构如下:
public class MyParentClass {
void doSomethingParent(){
System.out.println("something in parent");
}
}
public class MyClass extends MyParentClass{
protected String createDummyRequest(Holder myHolder){
//...
super.doSomethingParent();//I want to avoid this
//...
callingDB();
return "processedOutput";
}
private void callingDB(){
System.out.println("Calling to DB");
}
}
然后我的单元测试:
public class UnitTest {
public void testCreateDummyRequest(){
//create my mock holder
Holder mockHolder = new Holder();
MyClass mockObj = Mockito.mock(MyClass.class);
//mock doSomethingParent()
//mock callingDB()
//as mockObj is a fully mock, but I need to run my real method
//Mockito.when(mockObj.createDummyRequest(mockHolder)).thenCallRealMethod();
mockObj.createDummyRequest(mockHolder);
//Problem: doSomethingParent() is getting called though I have mocked it
}
}
如何阻止在我的方法中调用super.doSomethingParent()? (我正在编写测试的方法)
答案 0 :(得分:3)
我遇到了类似的问题,所以我发现使用spy()
可以解决问题。
public class UnitTest {
private MyClass myObj;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
myObj= spy(new MyClass());
}
@Test
public void mockedSuperClassMethod(){
doNothing().when((MyParentClass )myObj).doSomethingParent();
//...
}
}
这种方法对我有用。
答案 1 :(得分:2)
通过这种类结构,模拟和测试真的很难。如果可能的话,我建议改变结构,因为在雾情况下,难以模拟和测试的类结构同样难以扩展和维护。
因此,如果您可以将类结构更改为类似于:
public class MyClass {
private DoSomethingProvider doSomethingProvider;
private DbConnector dbConnector;
public MyClass (DoSomethingProvider p, DbConnector c) {
doSomethingProvicer = p;
dbConnector = c;
}
protected String createDummyRequest(Holder myHolder){
//...
doSomethingProvider.doSomethingParent();
//...
dbConnector.callingDB();
return "processedOutput";
}
}
然后你可以使用DoSomethingProvider和DbConnector的模拟轻松创建你的实例并且瞧.... [/ p>
如果您无法更改类结构,则需要使用Mockito.spy而不是Mockito.mock来存根特定的方法调用,但使用真实对象。
public void testCreateDummyRequest(){
//create my mock holder
Holder mockHolder = new Holder();
MyClass mockObj = Mockito.spy(new MyClass());
Mockito.doNothing().when(mockObj).doSomething();
mockObj.createDummyRequest(mockHolder);
}
注意:使用super
关键字可阻止Mockito存根该方法调用。我不知道是否有办法将超级电话存根。如果可能(如您没有覆盖类中的父方法),只需省略关键字。
答案 2 :(得分:0)
我发现了另一种方法,结果证明对我的情况非常有用。
就我而言,我需要创建一个扩展另一个类的新类,其中包括一个非常复杂的(旧版代码)protected final
方法。由于复杂性,实际上不可能重构以使用合成,所以这就是我的想法。
假设我有以下内容:
abstract class Parent {
public abstract void implementMe();
protected final void doComplexStuff( /* a long parameter list */) {
// very complex legacy logic
}
}
class MyNewClass extends Parent {
@Override
public void implementMe() {
// custom stuff
doComplexStuff(/* a long parameter list */); // calling the parent
// some more custom stuff
}
}
这是我重新排列此代码的方式:
abstract class Parent {
public abstract void implementMe();
protected final void doComplexStuff( /* a long parameter list */) {
// very complex legacy logic
}
}
interface ComplexStuffExecutor {
void executeComplexStuff(/* a long parameter list, matching the one from doComplexStuff */);
}
class MyNewClass extends Parent {
private final ComplexStuffExecutor complexStuffExecutor;
MyNewClass() {
this.complexStuffExecutor = this::doComplexStuff;
}
MyNewClass(ComplexStuffExecutor complexStuffExecutor) {
this.complexStuffExecutor = complexStuffExecutor;
}
@Override
public void implementMe() {
// custom stuff
complexStuffExecutor.doComplexStuff(/* a long parameter list */); // either calling the parent or the injected ComplexStuffExecutor
// some more custom stuff
}
}
在出于生产目的创建MyNewClass
实例时,我可以使用默认构造函数。
但是,在编写单元测试时,我将使用构造函数,在该构造函数中可以注入ComplexStuffExecutor
,在那里提供模拟,并且仅从MyNewClass
测试我的自定义逻辑,即:
class MyNewClassTest {
@Test
void testImplementMe() {
ComplexStuffExecutor complexStuffExecutor = Mockito.mock(ComplexStuffExecutor.class);
doNothing().when(complexStuffExecutor).executeComplexStuff(/* expected parameters */);
MyNewClass systemUnderTest = new MyNewClass(complexStuffExecutor);
// perform tests
}
}
乍看之下,似乎只是添加一些样板代码只是为了使代码可测试。但是,我也可以将其视为代码实际外观的指示器。也许有一天(有人会发现勇气和预算;)可以重构代码,例如使用ComplexStuffExecutor
中doComplexStuff
中的逻辑来实现Parent
,将其注入MyNewClass
中并摆脱继承。