是否可以使用Mockito和可选的Powermock来模拟超类S
,以便对超类S
的任何调用(包括对S()
构造函数的调用)都被模拟?因此,使用下面的示例,如果我使用Mockito将S
替换为MockS
,那么对super()
的调用是否会使用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()
的调用会调用真正的实现,而不是模拟。
答案 0 :(得分:4)
为了解决这个明显的限制,我重构了我的代码,replacing inheritance with delegation,我认为无论如何我最终都得到了更好的设计,因为继承并不是必需的。
新代码看起来像这样。请注意,问题的代码已经简化,因此真正的类具有更多功能。
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);
}
答案 1 :(得分:3)
我认为仅当子节点上的方法与超类上的方法不同时才能使用PowerMock(即,如果子节点覆盖该方法,则无法模拟父方法)。有关详细信息,请查看the relevant bug report。
对于PowerMock,请查看Suppressing Unwanted Behavior page,看看它是否足以满足您的需求。
经过多次挖掘后,我最终使用JMockit来处理这些棘手的案件。在我转到JMockit之前,我试图将使用抑制抛出异常的所有地方删除。最后,我需要覆盖一些方法,而不仅仅是压制它们,所以我最终放弃了它。
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;
}
}
激活后,此类将使用Activity
替换$init()
的默认构造函数,并将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)
用我的假实例替换超类。通过这种用法,您还可以访问假类的内部状态。如果您不需要使用自定义功能覆盖任何方法,则可以使用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;
导致Activity
类及其超类的所有方法(静态初始化器除外)都在HelloActivityTest4
中定义的测试中被模拟。
答案 2 :(得分:1)
您可以做的是将超类构造函数中的“危险”代码提取到非私有方法中,然后在类T上使用Mockito spy并覆盖该提取方法中的行为。
这当然会违反封装。 Guava为这种情况提供了VisibleForTesting注释。