2010-08-12 53 views
68

我在一些测试中使用Mockito。Mockito如何嘲笑超类的方法的调用

我有以下类别:

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

public Childservice extends BaseService { 
    public void save(){ 
     //some code 
     super.save(); 
    } 
} 

我想嘲笑只有​​第二次调用(super.save)。第一次电话必须要求真正的方法。有没有办法做到这一点?

+0

这可以通过PowerMockito解决吗? – javaPlease42 2016-06-21 17:05:12

回答

49

不,Mockito不支持这一点。

这可能不是你要找的答案,但你看到的是不适用的设计原则的症状:如果你提取的策略

Favor composition over inheritance

而不是延长超级班,问题就没有了。

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

我建议你去重构,但如果这不是一个选项,你在一些严重的黑客乐趣。

+2

我没有看到LSP违规。我的设置与OP基本相同:带有findAll()方法的基本DAO类,以及通过调用super.findAll()来覆盖基本方法然后对结果进行排序的子类DAO。子类可以替代所有接受超类的上下文。我误解你的意思了吗? – 2011-07-08 21:55:42

+1

我将删除LSP注释(它不会增加答案的值)。 – iwein 2011-08-02 08:40:22

+0

是遗传吸收和我坚持与愚蠢的框架设计与继承作为唯一的选择。 – 2015-12-21 22:07:50

66

如果你确实没有重构的选择,你可以在超级方法调用中模拟/存储所有东西,例如

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 spy = Mockito.spy(new ChildService()); 

     // Prevent/stub logic in super.save() 
     Mockito.doNothing().when((BaseService)spy).validate(); 

     // When 
     spy.save(); 

     // Then 
     verify(spy).load(); 
    } 
+1

这段代码实际上并不会阻止super.save()调用,所以如果你在super.save()中做了很多事情,你必须防止所有这些调用... – iwein 2011-08-02 08:47:29

+0

奇妙的解决方案为我工作奇迹时我想从超类方法中返回一个模拟值供孩子使用,非常感谢你。 – Gurnard 2012-05-31 14:18:52

+0

非常感谢。 – 2013-01-28 09:42:26

1

创建保护包(假定在同一个封装测试类)方法中调用父类的方法,然后调用您的重写的子类的方法,该方法的子类。然后您可以通过使用间谍模式在您的测试中设置对此方法的期望。不是很漂亮,但肯定比在测试中处理超级方法的所有期望设置要好

+0

我也可以说继承的构造几乎总是更好,但有时它只是简单地使用继承,直到java包含更好的组合模型,像scala或groovy,这将永远是这种情况,这个问题将继续存在 – Luke 2012-05-22 22:07:30

4

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

例子:

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

public Childservice extends BaseService { 
    public void save(){ 
     newMethod();  
     super.save(); 
    } 
    public void newMethod(){ 
     //some codes 
    } 
} 
0

的原因是你的基类是不公开版,那么的Mockito无法将其拦截,由于知名度,如果你改变基类为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()); 
} 
+6

这不是重点。 ChildService应该覆盖foo(),问题是如何模拟BaseService.foo()而不是ChildService.foo() – 2015-09-30 12:43:13

0

也许最简单的选项,如果继承才有意义是创造一种新的方法(包专用?)来调用超(可以称之为superFindall),间谍真正的实例,然后嘲笑superFindAll()方法就像你想嘲笑父母一样。这不是覆盖率和可视性方面的完美解决方案,但它应该能够完成这项工作,并且很容易实施。

public Childservice extends BaseService { 
    public void save(){ 
     //some code 
     superSave(); 
    } 

    void superSave(){ 
     super.save(); 
    } 
}