10

Mockito とオプションで Powermock を使用しSて、スーパークラスへのS呼び出し (コンストラクターへの呼び出しを含むS()) がモックされるようにスーパークラスをモックすることは可能ですか? したがって、以下の例を使用して、Mockito の使用に置き換えるSと、呼び出しでコンストラクターを使用するようになりますか?MockSsuper()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

この明らかな制限を回避するために、継承を 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);
}
于 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;
    }
}

アクティブ化されると、このクラスは のデフォルト コンストラクターを に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ます。

于 2012-03-31T15:21:11.113 に答える
1

あなたができることは、スーパークラスコンストラクターの「危険な」コードを非プライベートメソッドに抽出し、クラス T で Mockito スパイを使用して、その抽出されたメソッドの動作をオーバーライドすることです。

もちろん、これはカプセル化に違反します。Guava は、そのような場合にVisibleForTestingアノテーションを提供します。

于 2012-03-31T15:35:06.357 に答える