2

Android アクティビティをテストするときは、本番環境で実行するときとは異なる実装クラスをインターフェイスに使用する必要があります。私はモック オブジェクトの使用に慣れていますが、問題は、Android のアクティビティが私ではなく OS によってインスタンス化されることです。したがって、アクティビティへの参照を取得するまでに、それは既にインスタンス化されており、既に実装クラス。私の場合、話しているクラスは Login クラスです。私の自動テストでは、ログイン結果を制御できるようにしたいと考えています。これが私の例です:

class MainActivity extends Activity {

    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        setContentView(R.layout.main);
        LoginMgr aLoginMgr = new LoginMgrImpl();
        if (aLoginMgr.authenticated()) {
            //do something
        } else {
            //do something else
        }
    }
}


public interface LoginMgr {
    public boolean authenticated();
}

ライブラリの他の場所には、実装クラスがあります。

public class LoginMgrImpl implements LoginMgr {
    //authenticate
}

LoginMgrImpl自動テスト中に、本番環境を、テストに必要なブール値を返すテスト バージョンに置き換えられるようにしたいと考えています。MainActivityで別の を使用する方法がわからないため、ここで助けが必要LoginMgrImplです。それが重要な場合、私はEclipseを使用しています。getActivity () を呼び出すとActivityInstrumentationTestCase2<MainActivity>、テスト クラスによって が作成されます。MainActivity引数なしのコンストラクターを呼び出すため、そこで変更する機会はありませんLoginMgrImplMainActivityEclipse がビルドを制御するため、別の実装ライブラリで ビルドする方法がわかりません。

経験豊富な方で、私のテストをうまく自動化するための方向性を教えてもらえますか? Activity オブジェクトで使用されるいくつかのテスト クラスをモックしようとしているのは私だけではありませんが、フォーラムで解決策を見つけるのに何時間も費やしましたが、成功しませんでした。ヘルプ!


皆さんからのフィードバックに基づいて、さまざまな解決策を試しましたが、問題なく使用できる 2 つの解決策を見つけました。

解決策1:1つはマークされた回答で説明されており、元のsrc、res、assetsフォルダーとAndroidManifest.xml. その後、プロジェクトのリンクされたバージョンのプロジェクト プロパティを変更して、テストをサポートする別のライブラリ実装を参照できます。これは機能しますが、ぎこちなく感じます。


解決策 2 : の実装のシングルトン インスタンスを保持するプロジェクトの Application サブクラスを定義しますLoginMgr。次に、 は Application インスタンスからMainActivityを取得します。LoginMgrテスト中にActivityUnitTestCase<MainActivity>、モック LoginMgr を参照するモック アプリケーションを挿入するために使用できます。コード スニペットは次のとおりです。

主な用途: (必ずAndroidManifest.xml含む<Application android:name="MainApplication" ...>)

class MainApplication extends Application {

    private static LoginMgr sLoginMgr = new LoginMgrImpl();

    public LoginMgr getLoginMgr() {
        return sLoginMgr;
    }

}

MainActivity は次のように変更する必要があります。

class MainActivity extends Activity {

    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        setContentView(R.layout.main);

        // 2 lines added
        MainApplication aApplication = (MainApplication)getApplication();
        LoginMgr aLoginMgr = aApplication.getLoginMgr();

        if (aLoginMgr.authenticated()) {
            //do something
        } else {
            //do something else
        }
    }
}

単体テストは次のようにする必要があります。

public class MainActivityTest extends ActivityUnitTestCase<MainActivity> {

  public MainActivityTest() {
    super(MainActivity.class);
  }

  public void testUseAlternateLoginMgr() {
    MainApplication aMockApplication = new MainApplication()
    {
      @Override
      public LoginMgr getLoginMgr() {
        return new LoginMgr() {
          public boolean authenticated() {              {
            return true;
          }
        };
      }
    };

    setApplication(aMockApplication);

    Intent aDependencyIntent = new Intent("android.intent.action.MAIN");
    Activity aMainActivity = startActivity(aDependencyIntent, null, null);
    // verify results
  }
}

ハンド コードのモックではなく、モック フレームワークを使用することになると思いますが、これは単なる例です。

解決策2がより良い方法だと思います。ありがとう@yorkw!

4

5 に答える 5

1

アプリケーションをテストする 1 つの方法は、Linux でシンボリック リンクを使用して、本番環境で使用されているものとは異なるライブラリを使用して、アプリケーションの「テスト可能な」ビルドを作成することです。これを行うために、Eclipse で Application と ApplicationTestable の 2 つのプロジェクトを作成しました。アプリケーションは実際のプロジェクトです。ApplicationTestable は、Application と同じ src、res、および assets ディレクトリへのシンボリック リンクを持つダミー プロジェクトです。AndroidManifest.xml ファイルへのシンボリック リンクも追加しました。次に、プロジェクトのプロパティに移動し、製品版ではなくテスト版のライブラリを選択して、ApplicationTestable をテスト版のライブラリにリンクしました。次に、jUnit テストがある ApplicationTest プロジェクトのプロジェクト プロパティに移動し、ApplicationTestable プロジェクトを参照します。この場合、ライブラリのテスト バージョンは常にログインを認証しますが、ライブラリの製品バージョンは有効な ID とパスワードを必要とし、実際のサーバーに対して認証を試みます。私はまだより洗練されたソリューションを受け入れていますが、誰かがより良いアイデアを提供するまで、これを進めます.

于 2012-05-10T14:53:31.237 に答える
1

Android アプリで特定のオブジェクトをモックまたはスタブ化する必要がある場合、スタブ化が必要なオブジェクトの作成を Application クラス内の単純な 1 つの場所に分離しました。次に、テストかどうかに応じて、必要なバージョンを手動でコメントまたはコメント解除します。それは間違いなく自動化されておらず、理想的でもありませんが、うまくいきました.

たとえば、Application クラスに LoginMgr の (静的) インスタンスを所有させ、静的メソッドを介してアクセスできるようにします。ある種の構成データを使用して、使用する実装を Application クラスに伝えることができます。構成データをアプリケーションに渡す 1 つの方法は、こちらで<meta-data>説明されているように、マニフェスト内のノードを使用することです。ノードを含む特別な「テスト」アクティビティを宣言し(ノードが の子であるのとは対照的に)、 を使用してアクティビティから取得することで、これを自動化することもできます。次に、Eclipse 起動構成で、通常の「デフォルト」アクティビティの代わりに、この「テスト」アクティビティを起動するように指定します。<meta-data><application>getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA)

于 2012-05-09T16:57:09.963 に答える
0

初歩的なものが欠けているかもしれませんが、としてサブクラス化ActivityしてMainActivityから、2つのコンストラクターを実装します。

public MainActivity() {
  this(new LoginMgrImpl());
}

public MainActivity(LoginMgr loginMgr) {
  this.loginMgr = loginMgr;
}

次に、名前をに変更LoginMgrAuthenticationServiceます。次に、名前をに変更LoginMgrImplBlahBlahBlahAuthenticationServiceます。ここで、「BlahBlahBlah」は認証の実装方法を説明しています。(InMemoryAuthenticationService?? LdapAuthenticationService

于 2012-05-16T17:28:30.720 に答える
0

この問題を解決するには、依存性注入を使用して調査することをお勧めします。私が知っている Android 用の 2 つの DI フレームワークは、RoboGuiceGoogle Guiceです。それらは非常に似ているように見えます。しかし、私は違いや関係を学ぶのに十分な調査を行っていません.

于 2012-05-09T21:23:39.210 に答える