10

是否可以使用 Mockito 和可选的 Powermock 来模拟超类,以便模拟对超类S的任何调用S(包括对S()构造函数的调用)?因此,使用下面的示例,如果我替换SMockS使用 Mockito,是否会调用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(),并且读到这是不受支持的,但尚不清楚我是否可以模拟整个超类。

在我目前的测试中,当我尝试模拟时ST调用super()真正的实现,而不是模拟。

4

3 回答 3

5

为了解决这个明显的限制,我重构了我的代码,将继承替换为委托,并且我认为我最终得到了一个更好的设计,因为继承并不是真正必要的。

新代码如下所示。请注意,问题的代码已简化,因此真正的类具有更多功能。

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);
}
于 2012-03-31T18:00:54.483 回答
3

我认为只有当子类的方法与超类的方法不同时,PowerMock 才有可能实现这一点(即,如果子类覆盖该方法,则不能模拟父方法)。有关更多详细信息,您可以查看相关的错误报告

对于 PowerMock,请查看Suppressing Unwanted Behavior 页面,看看它是否足以满足您的需求。


经过大量的挖掘,我最终将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;
    }
}

激活后,该类将替换默认构造函数Activitywith $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.

于 2012-03-31T15:21:11.113 回答
1

您可以做的是将超类构造函数中的“危险”代码提取到非私有方法中,然后在您的类 T 上使用 Mockito spy 并覆盖该提取方法中的行为。

这当然会违反封装。Guava 为这种情况提供了VisibleForTesting注释。

于 2012-03-31T15:35:06.357 回答