Mockito间谍 – 在调用构造函数之前存根

我正在试图窥探一个Object,我想在构造函数调用它之前存根一个由构造函数调用的方法。
我的class级看起来像这样:

public class MyClass { public MyClass() { setup(); } public void setup() { } } 

不得调用安装方法。 那么,我如何监视这个方法(和存根设置,使它什么都不做)?
它可以很好地模拟方法,但我想unit testingMyClass ,所以我需要非常其他的方法。


之所以需要存根设置方法以便它什么都不做:
我正在编写一个Lego机器人(lejos),我在机器人需要工作的设置中放了一些代码。 但是,当我在TinyVM(安装在机器人上的VM)之外调用它时,java崩溃,因为VM尚未正确初始化(因为测试在我的PC上运行)。 对于unit testing,设置并不重要。
我不能存根类/方法设置调用,因为它们中的一些是公共静态最终变量。

要直接回答您的问题, 您不能使用Mockito来存根从构造函数调用的方法 。 在你开始模拟之前,Mockito需要一个类的实例,而你没有给自己创建一个测试实例的方法。

更一般地说,如Effective Java第 17项中所述, 您不应该从构造函数中调用可覆盖的方法 。 例如,如果这样做,您可以在子类中提供覆盖,该子类引用final字段但在final字段设置之前运行。 它可能不会让你遇到麻烦,但它在Java中是一个坏习惯。

幸运的是,您可以重构代码以轻松完成此操作:

 public class MyClass { public MyClass() { this(true); } /** For testing. */ MyClass(boolean runSetup) { if (runSetup) { setup(); } } /* ... */ } 

为了使它更加明显,您可以将单参数MyClass构造函数MyClass私有,并提供public static工厂方法:

 /* ... */ public static MyClass createForTesting() { return new MyClass(false); } private MyClass(boolean runSetup) { /* ... */ 

虽然一些开发人员认为在主要用于测试的方法中编写任何代码是一种不好的做法,但请记住,您负责代码的设计,而测试是您绝对知道需要适应的少数消费者之一。 虽然在“生产”代码中避免显式测试设置仍然是一个好主意,但为了测试而创建额外的方法或重载通常会使您的代码整体更清晰,并且可以大大提高您的测试覆盖率和可读性。

感谢您的建议,但有点过于复杂。
我最后通过扩展类并覆盖我的setup方法来模拟该方法。 这样默认构造函数不会调用它的setup实现,而是调用覆盖的方法。
这是代码:

 // src/author/MyClass.java public class MyClass { public MyClass() { setup(); } protected void setup() { throw new Exception("I hate unit testing !"); } public boolean doesItWork() { return true; } } // test/author/MyClass.java public class MyClassTest { private class MockedMyClass extends MyClass { @Override protected void setup() { } } private MyClass instance; @Before public void setUp() { // Not to be confusing with `MyClass#setup()`! instance = new MockedMyClass(); } @Test public void test_doesItWork() { assertTrue(instance.doesItWork()); } } 

如果您不希望MyTest的安装方法被其他子类调用或覆盖,除了您的测试(因为其他开发人员可能会使用安装方法非常糟糕),只需将可见性更改为默认值,只有您的类才能够呼叫设置。


如果有更简单的方法,请回答这个问题,因为我不是100%满意我的解决方案。

  1. 使用PowerMock。

  2. 导入库后,将其设置为使用不能调用的实例方法操作要模拟的类。

像这样:

 @RunWith(PowerMockRunner.class) @PrepareForTest({, Myclass.class}) 
  1. 在测试开始时取消该方法。

像这样:

 suppress(method(Myclass.class, "setup")); 
  1. 在测试中根据需要自定义setup()方法的行为。

像这样:

 doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { // code here return null; } }).when(Myclass.class, "setup");