我可以用Mockito / Powermock模拟一个超类的构造函数吗?
是否可以使用Mockito和可选的Powermock来模拟超类S
,以便对超类S
任何调用(包括对S()
构造函数的调用)都被模拟? 所以使用下面的例子,如果我使用MockS
用MockS替换S
, MockS
对super()
的调用是否会使用MockS中的构造MockS
?
class S { S() { // Format user's hard drive, call 911, and initiate self-destruct } } class T extends S { T() { super(); } } class Test { @Mock private S mockS; new T(); // T's call to super() should call the mock, not the destructive S. }
我已经看到有关在S
中模拟单个方法或仅模拟对super()
调用的问题,并且认为这是不受支持的,但我不清楚是否可以模拟整个超类。
在我当前的测试中,当我尝试模拟S
, T
对super()
调用调用真实的实现,而不是模拟。
为了解决这个明显的限制,我重构了我的代码, 用委托取代了inheritance ,我认为无论如何我最终得到了更好的设计,因为inheritance并不是真的有必要。
新代码看起来像这样。 请注意,问题的代码已经简化,因此真正的类具有更多function。
class S { S() { // Format user's hard drive, call 911, and initiate self-destruct } } class T { T(S s) {} // Now T "has an S" instead of "is an S" } class Test { @Mock private S mockS; new T(s); // T's call to super() should call the mock, not the destructive S. }
对于那些感兴趣的人,使用Guice和Android,测试看起来更像是这样的:
class T { T(Activity activity, S s) {} } class Test { @Mock Activity activity; @Mock S mockS; injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Activity.class).toInstance(activity); bind(S.class).toInstance(mockS); }} ); T t = injector.getInstance(T.class); }
我认为仅当子方法与超类上的方法不同时才能使用PowerMock(即,如果子方法覆盖该方法,则无法模拟父方法)。 有关更多详细信息,您可以查看相关的错误报告 。
对于PowerMock,请查看“ 抑制不需要的行为”页面 ,看它是否足以满足您的需求。
经过多次挖掘,我最终使用JMockit来处理这些棘手的案例。 在我转到JMockit之前,我尝试使用抑制来删除所有抛出exception的地方。 最后,我需要覆盖一些方法,而不仅仅是压制它们,所以我最终放弃了它。
Android案例的用法示例:
首先,使用@MockClass
注释模拟您的超类:
@MockClass(realClass = Activity.class, instantiation = PerMockedInstance) public class FakeActivity { public Bundle mSavedInstanceState; @Mock public void $init() {} @Mock public void onCreate(Bundle savedInstanceState) { mSavedInstanceState = savedInstanceState; } }
激活后,此类将使用$init()
替换Activity
的默认构造函数,并将onCreate
方法替换为上面的方法。 在android中,被测单元来自Activity(在我的示例代码中,它是HelloTestActivity
)。 测试类看起来像这样:
public class HelloTestActivityTest3 extends AndroidTest { @Tested HelloTestActivity activity; FakeActivity fakeActivity = new FakeActivity(); @Before public void setupMocks() { Mockit.setUpMock(fakeActivity); } @Test public void onCreate_bundle(@Mocked Bundle savedInstanceState) { // Try to access out-of-band information from the fake activity.onCreate(savedInstanceState); assertSame(savedInstanceState, fakeActivity.mSavedInstanceState); } }
调用Mockit.setupMock(fakeActivity)
用我的假实例替换超类。 通过此用法,您还可以访问假类的内部状态。 如果您不需要使用自定义function覆盖任何方法,则可以使用Mockit
类中提供的其他方法。
正如rogerio在下面的评论中指出的那样,模拟Activity
类是最低限度的。 以下代码演示了这一点。
public class HelloTestActivityTest4 { @Tested HelloTestActivity activity; @Mocked Activity base; @Test public void testOnCreate() throws Exception { // Just make sure "Stub!" exception is not thrown. activity.onCreate(null); } }
声明@Mocked Activity base;
导致在HelloActivityTest4
定义的测试中HelloActivityTest4
Activity
类及其超类的所有方法(静态初始化器除外)。
您可以做的是将超类构造函数中的“危险”代码提取到非私有方法中,然后在类T上使用Mockito spy并覆盖该提取方法中的行为。
这当然会违反封装。 Guava为此类案例提供VisibleForTesting注释。