Mockito如何只模拟超类方法的调用

mad*_*ada 86 mockito

我在一些测试中使用Mockito.

我有以下课程:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        //some code  
        super.save();
    }  
}   
Run Code Online (Sandbox Code Playgroud)

我想只模拟第二个调用(super.save)ChildService.第一个调用必须调用真正的方法.有没有办法做到这一点?

jgo*_*ian 82

如果你真的没有重构的选择,你可以在超级方法调用中模拟/存根所有内容,例如

    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();
    }
Run Code Online (Sandbox Code Playgroud)

  • 除非验证隐藏或save方法直接执行工作而不是调用其他方法,否则这很有效.mockito不:Mockito.doNothing().when((BaseService)spy).save(); 这不会'在基本服务上没有任何保存但在childService上保存:( (13认同)
  • 该代码实际上并不会阻止super.save()的调用权,因此,如果您在super.save()中做了很多事情,就必须阻止所有这些调用。 (2认同)

iwe*_*ein 56

不,Mockito不支持这一点.

这可能不是您正在寻找的答案,但您所看到的是不应用设计原则的症状:

赞成合成而不是继承

如果你提取策略而不是扩展超类,问题就会消失.

但是,如果您不允许更改代码,但无论如何您必须对其进行测试,并且以这种尴尬的方式,仍然存在希望.使用一些AOP工具(例如AspectJ),您可以将代码编织到超类方法中并完全避免执行(yuck).如果你使用代理,这不起作用,你必须使用字节码修改(加载时间编织或编译时间编织).还有一些模拟框架支持这种技巧,比如PowerMock和PowerMockito.

我建议你去重构,但如果这不是一个选项,那你就是为了一些严肃的黑客乐趣.

  • 我没有看到LSP违规.我有与OP大致相同的设置:带有findAll()方法的基本DAO类,以及通过调用super.findAll()然后对结果进行排序来覆盖基本方法的子类DAO.子类可以替换为接受超类的所有上下文.我误解了你的意思吗? (3认同)
  • 是的,继承很糟糕,而我所坚持的愚蠢框架的设计是将继承作为唯一的选择。 (2认同)
  • 我在这里看不到继承有什么问题. (2认同)

Sum*_*ane 7

我找到了一种使用 PowerMockito 抑制超类方法的方法。为此需要 3 个简单的步骤

  1. 使用PowerMockito.suppress方法和MemberMatcher.methodsDeclaredIn方法来抑制父类方法

  2. 其次在@PrepareForTest中添加Parent类

  3. 使用 PowerMock 运行您的测试类,即在测试类上方添加@RunWith(PowerMockRunner.class) 。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({BaseService.class})
    public class TestChildService(){
    
        @Spy
        private ChildService testChildServiceObj = Mockito.spy(new ChildService());
    
        @Test
        public void testSave(){
            PowerMockito.suppress(MemberMatcher.methodsDeclaredIn(BaseService.class));
    
            //your further test code
    
            testChildServiceObj.save();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

注意:只有当超类方法不返回任何内容时,这才有效。


小智 6

考虑将 ChildService.save() 方法的代码重构为不同的方法并测试该新方法而不是测试 ChildService.save(),这样您将避免对超级方法的不必要调用。

例子:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        newMethod();    
        super.save();
    }
    public void newMethod(){
       //some codes
    }
} 
Run Code Online (Sandbox Code Playgroud)