2

Android TDD シリーズの他の 質問に続いて、 RobolectricMockitoMaven、およびABSを使用して、Android 開発の単体テストを行うことができました。明らかに、私は自分の知識の限界を押し広げているように見えますが、Android CI の夢はあまりにも魅力的です。私の次の問題を手伝っていただけるなら、とても感謝しています。

アプリケーションのデータベースを v1 から先頭に移動する統合テスト ケースを作成したいと考えています。私はDI にDaggerを使用しており、この非標準の JUnit テストを容易にするために、アップグレードを実行するために必要なクラスをテストに挿入しています。

@RunWith(RobolectricTestRunner.class)
public class TestDatabaseHelper {
  private static final String V1_DATABASE_SQL = "res/test-support/v1-database/v1-database.sql";
  @Inject private UpgradeAuditService upgradeAuditService;
  @Rule public StoutLoggingRule loggingRule = new StoutLoggingRule();
  @Rule public DaggerInjector injector = new DaggerInjector();

  /**
   * Tests upgrading the database from version 1 to HEAD.
   *
   * @throws NameNotFoundException When there is no app but it's running the app. A WTF moment.
   * @throws IOException when something goes wrong.
   */
  @Test
  @Config(manifest="../OceanLife/AndroidManifest.xml")
  public void testUpgradingToHead() throws NameNotFoundException, IOException {
    // Given.
    final Context testApplicationContext = Robolectric.getShadowApplication().getApplicationContext();
    final SQLiteDatabase database = SQLiteDatabase.openDatabase("/data/data/com.oceanlife/databases/oceanlife.db", null, SQLiteDatabase.OPEN_READWRITE);
    final int versionCode = testApplicationContext.getPackageManager().getPackageInfo("com.oceanlife", 0).versionCode;

    // When.
    replaceDatabase(database);
    new DatabaseHelper(testApplicationContext, upgradeAuditService).onUpgrade(database, 1, versionCode);

    // Then.
    final int databaseVersion = SQLiteDatabase.openDatabase("/data/data/com.oceanlife/databases/oceanlife.db", null, SQLiteDatabase.OPEN_READWRITE).getVersion();
    assertEquals("Database was not upgraded.", versionCode, databaseVersion);
  }

注意すべき重要な点;

  • テストは、テスト中のプロジェクトとは別に、独自のテスト プロジェクトの下に存在します (構造については、リンクされた質問を参照してください)。
  • 失敗 (以下のスタック トレース) は、実際のアプリケーションのモジュール グラフを作成するときに発生します。
  • インジェクター ルール (以下で説明) は、アプリケーションを実際に実行するときに使用されるモジュールを含むテスト プロジェクト モジュールにリンクします。

DaggerInjectorルールの下で何が起こっているのですか?

テストに必要な依存関係を注入しようとします。

public class DaggerInjector implements MethodRule {

  /**
   * @see org.junit.rules.MethodRule#apply(org.junit.runners.model.Statement, org.junit.runners.model.FrameworkMethod, java.lang.Object)
   */
  @Override
  public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
    return new Statement() {
      @Override
      public void evaluate() throws Throwable {
        final ObjectGraph graph = ObjectGraph.create(new OceanLifeTestingModule());
        graph.inject(target);
        base.evaluate();
      }
    };
  }
}

テストモジュールはどのように見えますか?

テストおよび (予定の) テスト モックの置換用のエントリ ポイント (これはであり、 replacesdagger-0.9.1の最新のものではありません) を追加します。injectentryPoints

/**
 * The injection container providing information on
 * how to construct test support components.
 *
 * @author David C Branton
 */
@Module(includes = OceanLifeModule.class,
        entryPoints = {FixtureBuilder.class,
                       TestSpotService.class,
                       TestDatabaseHelper.class},
        complete = true,
        overrides = true)
public class OceanLifeTestingModule {

  /**
   * Constructs this module.
   */
  public OceanLifeTestingModule() { }

  /**
   * Provide the database.
   *
   * @return the application database.
   */
  @Provides SQLiteDatabase provideDatabase() {
    return SQLiteDatabase.openDatabase("/data/data/com.oceanlife/databases/oceanlife.db", null, SQLiteDatabase.OPEN_READWRITE);
  }
}

実際のアプリケーションのオブジェクト グラフを作成するコード行をコメント アウトすると、テストは緑色で実行されます。これがスタック トレースです。

エラー 1: 私のテストの観点からは、捕らえられて飲み込まれました

dagger.internal.RuntimeAggregatingPlugin#getModuleAdapter

java.lang.RuntimeException: Unexpected failure loading com.oceanlife.OceanLifeModule$ModuleAdapter

エラー 2: Robolectricによってコンソールにダンプされる

java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
    at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:653)
    at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:460)
    at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:286)
    at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:222)
    at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
    at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
    at java.lang.Class.initAnnotationsIfNecessary(Class.java:3079)
    at java.lang.Class.getAnnotation(Class.java:3038)
    at dagger.internal.plugins.reflect.ReflectivePlugin.getModuleAdapter(ReflectivePlugin.java:51)
    at dagger.internal.RuntimeAggregatingPlugin.getModuleAdapter(RuntimeAggregatingPlugin.java:98)
    at dagger.internal.RuntimeAggregatingPlugin.getAllModuleAdapters(RuntimeAggregatingPlugin.java:55)
    at dagger.ObjectGraph.makeGraph(ObjectGraph.java:115)
    at dagger.ObjectGraph.create(ObjectGraph.java:103)
    at com.oceanlife.MainApplication.onCreate(MainApplication.java:36)
    at org.robolectric.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:146)
    at org.robolectric.RobolectricTestRunner.setUpApplicationState(RobolectricTestRunner.java:387)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:227)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

これまでの調査

「Error 1」が発生した理由に焦点を当てています。ユーザー ガイドの「コンパイル時のコード生成」の側面を読むと、RuntimeAggregatingPlugin が探しているアダプター ( OceanLifeModule$ModuleAdapter) がコンパイル時に生成されることがわかります。それから、私は自分の Maven 構成を調べてきました。コミュニティがこれを理解する必要があると感じた場合は、喜んでそれを提供します。「エラー 2」の低レベルの性質は、根本原因よりも派生的なものだと思います。

4

1 に答える 1

0

Ok。したがって、この答えは正しい道を示してくれました。

本質的に、これは私のテスト プロジェクトが古い v1 マップ コンポーネントcom.google.android.maps.MapActivityなどを理解していなかったことが原因でした。問題を解決する方法は、maps.jar をテスト プロジェクトにインポートすることでした。

<dependency>
    <groupId>com.google.android.maps</groupId>
    <artifactId>maps</artifactId>
    <version>17_r3</version>
</dependency>

これはAndroid SDK Deployerを介して取得できます。

于 2013-06-17T21:01:26.817 に答える