55

依存性注入に Dagger 2 を使用する Android アプリがあります。また、単体テスト用のビルド バリアントとインストルメンテーション テスト用のビルド バリアントを許可する最新の gradle ビルド ツールも使用しています。私はjava.util.Random自分のアプリで使用していますが、これをテスト用にモックしたいと考えています。私がテストしているクラスは Android のものを使用していないため、通常の Java クラスです。

私のメイン コードComponentでは、クラスを拡張するクラスでを定義していますApplicationが、単体テストでは . を使用していませんApplicationModuleテストと を定義しようとしましComponentたが、Dagger は . を生成しませんComponent。また、アプリケーションで定義した を使用して、ビルド時に をComponent交換しようとしましたが、アプリケーションにはテスト クラスのメソッドがありません。テスト用のモック実装を提供するにはどうすればよいですか?ModuleComponentinjectRandom

サンプルコードは次のとおりです。

応用:

public class PipeGameApplication extends Application {

    private PipeGame pipeGame;

    @Singleton
    @Component(modules = PipeGameModule.class)
    public interface PipeGame {
        void inject(BoardFragment boardFragment);
        void inject(ConveyorFragment conveyorFragment);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        pipeGame = DaggerPipeGameApplication_PipeGame.create();
    }

    public PipeGame component() {
        return pipeGame;
    }
}

モジュール:

@Module
public class PipeGameModule {

    @Provides
    @Singleton
    Random provideRandom() {
        return new Random();
    }
}

テストの基本クラス:

public class BaseModelTest {

    PipeGameTest pipeGameTest;

    @Singleton
    @Component(modules = PipeGameTestModule.class)
    public interface PipeGameTest {
        void inject(BoardModelTest boardModelTest);
        void inject(ConveyorModelTest conveyorModelTest);
    }

    @Before
    public void setUp() {
        pipeGameTest = DaggerBaseModelTest_PipeGameTest.create(); // Doesn't work
    }

    public PipeGameTest component() {
        return pipeGameTest;
    }
}

また:

public class BaseModelTest {

    PipeGameApplication.PipeGame pipeGameTest;

    // This works if I make the test module extend
    // the prod module, but it can't inject my test classes
    @Before
    public void setUp() {
        pipeGameTest = DaggerPipeGameApplication_PipeGame.builder().pipeGameModule(new PipeGameModuleTest()).build();
    }

    public PipeGameApplication.PipeGame component() {
        return pipeGameTest;
    }
}

テスト モジュール:

@Module
public class PipeGameTestModule {

    @Provides
    @Singleton
    Random provideRandom() {
        return mock(Random.class);
    }
}
4

5 に答える 5

7

あなたは次のように言って頭に釘を打ちました:

アプリケーションのコンポーネントに、テスト クラスの注入メソッドがありません

したがって、この問題を回避するために、Application クラスのテスト バージョンを作成できます。その後、モジュールのテスト バージョンを作成できます。すべてをテストで実行するには、Robolectric を使用できます。

1) アプリケーション クラスのテスト バージョンを作成します。

public class TestPipeGameApp extends PipeGameApp {
    private PipeGameModule pipeGameModule;

    @Override protected PipeGameModule getPipeGameModule() {
        if (pipeGameModule == null) {
            return super.pipeGameModule();
        }
        return pipeGameModule;
    }

    public void setPipeGameModule(PipeGameModule pipeGameModule) {
        this.pipeGameModule = pipeGameModule;
        initComponent();
    }}

2) 元の Application クラスには、initComponent()およびpipeGameModule()メソッドが必要です。

public class PipeGameApp extends Application {
    protected void initComponent() {
        DaggerPipeGameComponent.builder()
            .pipeGameModule(getPipeGameModule())
            .build();
    }

    protected PipeGameModule pipeGameModule() {
        return new PipeGameModule(this);
    }}

3) PipeGameTestModule は、コンストラクターを使用して製品モジュールを拡張する必要があります。

public class PipeGameTestModule extends PipeGameModule {
    public PipeGameTestModule(Application app) {
        super(app);
    }}

4) 次に、junit テストのsetup()メソッドで、このテスト モジュールをテスト アプリに設定します。

@Before
public void setup() {
    TestPipeGameApp app = (TestPipeGameApp) RuntimeEnvironment.application;
    PipeGameTestModule module = new PipeGameTestModule(app);
    app.setPipeGameModule(module);
}

これで、テスト モジュールを当初の希望どおりにカスタマイズできるようになりました。

于 2016-04-04T00:45:30.403 に答える
2

私の意見では、この問題を別の角度から見ることでアプローチできます。モック化された依存関係が注入されたテスト対象の構築クラスの Dagger に依存しないことで、クラスを簡単に単体テストできます。

私が言いたいのは、テスト セットアップでは次のことができるということです。

  • テスト中のクラスの依存関係をモックする
  • モック化された依存関係を使用して、テスト対象のクラスを手動で構築します

Dagger はコンパイル中に依存関係グラフの正確性を検証するため、依存関係が正しく挿入されているかどうかをテストする必要はありません。したがって、そのようなエラーはコンパイルの失敗によって報告されます。そのため、setup メソッドでテスト対象のクラスを手動で作成することが許容される必要があります。

テスト対象のクラスでコンストラクターを使用して依存関係が注入されるコード例:

public class BoardModelTest {

  private BoardModel boardModel;
  private Random random;

  @Before
  public void setUp() {
    random = mock(Random.class);
    boardModel = new BoardModel(random);
  }

  @Test
  ...
}

public class BoardModel {
  private Random random;

  @Inject
  public BoardModel(Random random) {
    this.random = random;
  }

  ...
}

テスト中のクラスのフィールドを使用して依存関係が注入されるコード例 (BoardModelフレームワークによって構築された場合):

public class BoardModelTest {

  private BoardModel boardModel;
  private Random random;

  @Before
  public void setUp() {
    random = mock(Random.class);
    boardModel = new BoardModel();
    boardModel.random = random;
  }

  @Test
  ...
}

public class BoardModel {
  @Inject
  Random random;

  public BoardModel() {}

  ...
}
于 2015-05-05T17:37:47.590 に答える
1

Android で dagger2 を使用している場合は、アプリ フレーバーを使用してモック リソースを提供できます。

模擬テストでのフレーバーのデモ (短剣なし) については、こちらを参照してください: https://www.youtube.com/watch?v=vdasFFfXKOY

このコードベースには例があります: https://github.com/googlecodelabs/android-testing

/src/prod/com/yourcompany/Component.javaで、本番 コンポーネントを提供します。

/src/mock/com/yourcompany/Component.javaで、 モック コンポーネントを提供します。

これにより、モックの有無にかかわらず、アプリのビルドを作成できます。また、並列開発 (あるチームによるバックエンド、別のチームによるフロントエンド アプリ) も可能であり、API メソッドが利用可能になるまでモックできます。

私のgradleコマンドがどのように見えるか(Makefileです):

install_mock:
    ./gradlew installMockDebug

install:
    ./gradlew installProdDebug

test_unit:
    ./gradlew testMockDebugUnitTest

test_integration_mock:
    ./gradlew connectedMockDebugAndroidTest

test_integration_prod:
    ./gradlew connectedProdDebugAndroidTest
于 2016-02-26T14:02:59.090 に答える