複数のアクティビティにわたってAndroidアプリケーションをどのようにテストしますか?


80

多くのアクティビティにまたがる多くの画面とワークフローで構成される複雑なAndroidアプリケーションを構築しています。私たちのワークフローは、銀行のATMマシンで見られるものと似ています。たとえば、ユーザーの選択に基づいて他のアクティビティに移行できるActivityメインメニューに移行するログインがありますActivity

ワークフローが非常に多いため、ワークフローをエンドツーエンドでテストできるように、複数のアクティビティにまたがる自動テストを作成する必要があります。たとえば、ATMの例を使用して、有効なPINを入力し、メインメニューに移動することを確認し、現金の引き出しを選択し、現金の引き出し画面にいることを確認するなどして、最終的に自分自身を見つけます。メインメニューに戻るか、「ログアウト」します。

Android(eg ActivityInstrumentationTestCase2)とPositronに付属するテストAPIを試してみましたが、どちらも単一の範囲を超えてテストできるようには見えません。Activityこれらのツールには、いくつかの単体テスト用のユーティリティがいくつかありますが、勝ちました。複数のアクティビティにまたがるテストシナリオのニーズを満たしていません。

xUnitフレームワーク、スクリプト、GUIレコーダー/再生などを受け付けており、アドバイスをいただければ幸いです。


2
:アンドロイド4.1の時点では、実際にシステム全体の活動渡ってテストし、有効にアンドロイドから新しいテストフレームワーク今そこにあるdeveloper.android.com/tools/testing/testing_ui.html
クリストファー・オア

1
Robotiumは、このニーズにもわずか数行で適合します。
ドリ

回答:


65

私自身の賞金の質問に答えるのは少し厄介ですが、ここにあります...

私はこれを高低で検索しましたが、どこにも回答が公開されていないなんて信じられません。とても近づきました。今は間違いなくアクティビティにまたがるテストを実行できますが、私の実装には、テストが常に確実に合格するとは限らないタイミングの問題があるようです。これは、複数のアクティビティにわたるテストが成功したことを私が知っている唯一の例です。うまくいけば、それを抽出して匿名化してもエラーが発生しませんでした。これは、ログインアクティビティにユーザー名とパスワードを入力し、別の「ウェルカム」アクティビティに適切なウェルカムメッセージが表示されることを確認する単純なテストです。

package com.mycompany;

import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;

import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;

public class LoginTests extends InstrumentationTestCase {

   @MediumTest
   public void testAValidUserCanLogIn() {

      Instrumentation instrumentation = getInstrumentation();

      // Register we are interested in the authentication activiry...
      Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);

      // Start the authentication activity as the first activity...
      Intent intent = new Intent(Intent.ACTION_MAIN);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
      instrumentation.startActivitySync(intent);

      // Wait for it to start...
      Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Type into the username field...
      View currentView = currentActivity.findViewById(username_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyUsername");

      // Type into the password field...
      currentView = currentActivity.findViewById(password_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyPassword");

      // Register we are interested in the welcome activity...
      // this has to be done before we do something that will send us to that
      // activity...
      instrumentation.removeMonitor(monitor);
      monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);

      // Click the login button...
      currentView = currentActivity.findViewById(login_button;
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(Button.class));
      TouchUtils.clickView(this, currentView);

      // Wait for the welcome page to start...
      currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Make sure we are logged in...
      currentView = currentActivity.findViewById(welcome_message);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(TextView.class));
      assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
   }
}

このコードは明らかにあまり読みにくいです。私は実際にそれを英語のようなAPIを備えた単純なライブラリに抽出したので、次のように言うことができます。

type("myUsername").intoThe(username_field);
click(login_button);

私は約4つのアクティビティの深さまでテストし、アプローチが機能することに満足していますが、私が言ったように、私が完全に理解していない時折のタイミングの問題があるようです。私はまだ、活動全体でテストする他の方法を聞くことに興味があります。


3
タイミングの問題が原因でテストが失敗した場合に、FlakyTestアノテーションを追加して、テストを自動的に繰り返すことができます。実際には解決策ではありませんが、状況によっては実行可能な回避策です。
カールマナスター2010

これを書いてくれてありがとう!テスト用にActivityMonitorsの機能を備えたものを探していました。私はそれらを見つけることができませんでした。
Peter Ajtai 2011

私の知る限り、上記で行ったことは何も使用できませんActivityInstrumentationTestCase2
ericn 2014

任意のアイデア、どのような条件で、 'getInstrumentation()。waitForIdleSync();' 無限ループに入りますか?プロセッサボードを実行しているAndroid4.4.2_r2で、CTSテストの実行中にこの問題に直面しています。
arunJTS 2014

私の息子@ pajato1があなたのタイミングの問題を見つけて修正したと思います。彼の修正は私の問題を解決しました。彼の発言は次のとおりです。「javadocで、Instrumentation.startActivitySync()が新しいアクティビティの準備ができるまでブロックされてから返されることに気付いたので、モニターは必要なかったようです。削除すると、これが正しいことが証明されました。理論的には、モニターによってstartActivitySync()によって作成されたアクティビティが競合状態のために再起動される場合がありました。Androidのソースコードを読むのに時間を費やしましたが、競合状態の原因として何も飛び出しませんでした。 「」
pajato0 2015年

22

Robotiumの 「Androidアプリケーションの自動ブラックボックステストを、そのままのAndroidインストルメンテーションテストで可能なものよりも大幅に高速かつ簡単にするために作成されたオープンソーステストフレームワーク」ご覧
ください。

ホームページ: http
//www.robotium.org/出典:http//github.com/jayway/robotium

Robotiumプロジェクトは私が働いている会社によって維持されていることに注意してください


こんにちは、このためのレコーダーツールはありますか?私は多くのウェブサイトをチェックし、スクリプトを記録して実行するtestdroidを見つけました。残念ながらそれはフリーウェアではありません、あなたは記録プロセスを行うフリーウェアを知っていますか?
thndrkiss 2011

@thndrkiss:そのようなツールは知りません。Robotiumフォーラムに質問をすると、より良い回答が得られる可能性があります。
ジョナスSöderström

2
Robotiumは命の恩人です。それはあなたのテストを書くのを非常に簡単にします(あなたは基本的に平易な英語でそれと話している:これをクリックし、戻るボタンを押すなど)あなたは何でもテストすることができますがあなたは小さな詳細を知る必要はありません。少なくとも2つの大きな利点があります。ソースコードがないアプリをテストできることと、UIに依存しているため、非常に堅牢です(コントローラー/モデルをビューよりもはるかに多く変更します...)
tiktak 2013年

8

Robotiumはいつでも使用できます。Seleniumと同じように、Android用のブラックボックステストをサポートします。Robotium.orgで見つけることができます


1
前回、Robotiumがアクティビティ間で使用できないことを確認しました。これは修正されましたか?stackoverflow.com/questions/3840034/...
user77115

3
同じプロセスに属している限り、アクティビティ間で常に機能します。
レナス2012

4

主要な自動機能テストツールのいくつかについて誰も言及していないことに驚いています。Robotiumと比較すると、これらはJavaコードを記述する必要がありません。

MonkeyTalk GorillaLogic社が支援するオープンソースツール。長所:技術者以外のユーザーにとって、録音と高レベルのスクリプト言語を簡単に提供し、クロスプラットフォーム(iOSを含む)です。これらの利点を要件として考えると、これが最良のソリューションであることがわかりました。また、Javascriptを使用してスクリプト言語で実行できる以上のカスタマイズも可能です。

Calabash-Android:キュウリスタイルの機能のためのオープンソースツール。長所:ビジネスで読み取り可能なドメイン固有言語であるGherkin言語で機能を記述します。これにより、ソフトウェアの動作を詳細に説明することなく、その動作を説明できます。同様ですが、正確ではありませんが、iOSのcucumber-iosで利用できます。記録機能は、バイナリ出力を生成するため、それほど優れていません。

他のいくつかの参考文献:

  • ここではいくつかある追加の比較Robotium、Monkeytalkとカラバッシュの間では。別の可能性としてTestDroidについて言及しています。
  • このブログでは、上記に加えて、NativeDriverとボットボットについて言及しています。

3

Android用の記録と再生のツールを作成し、GitHubで利用できるようにしました。設定と使用は簡単で、プログラミングは不要で、実際のデバイス(ルート化する必要はありません)に対して実行され、テストの再生時にスクリーンショットを自動的に保存します。


これは有望に見えます。要点がわからない人のために:これはジェスチャー(タップ、ドラッグなど)をテストするためのかなり良い解決策のようです
tiktak 2013年

3

まず、基本クラスとして「InstrumentationTestCase」ではなく「ActivityInstrumentationTestCase2」を使用します。Robotiumを使用して、複数のアクティビティにわたって定期的にテストしています。ログインアクティビティをジェネリック型(およびコンストラクターへのクラス引数)として指定する必要があることがわかりました。

'ActivityInstrumentationTestCase2'コンストラクターはパッケージ引数を無視し、それを必要としません。パッケージを受け取るコンストラクターは非推奨です。

Javadocから: "ActivityInstrumentationTestCase2(String pkg、Class activityClass)このコンストラクターは非推奨です。代わりにActivityInstrumentationTestCase2(Class)を使用してください"

推奨される基本クラスを使用すると、フレームワークは、アクティビティの開始など、特定の定型文を処理できます。これは、必要に応じて「getActivity()」を呼び出すことで実行されます。


3

これは、いくつかの変更を加えることで便利であることがわかりました。まずgetInstrumentation().waitForIdleSync()、SingleShotが話す薄片を治し、スタートアクティビティラインを置き換えることができる機能もInstrumentationTestCaseありlauchActivityます。


2

フレークの待機時間が同期しないようにするには、次のようにします。

final Button btnLogin = (Button) getActivity().findViewById(R.id.button);
Instrumentation instrumentation = getInstrumentation();

// Register we are interested in the authentication activity...
Instrumentation.ActivityMonitor aMonitor = 
        instrumentation.addMonitor(mynextActivity.class.getName(), null, false);

getInstrumentation().runOnMainSync(new Runnable() {
         public void run() {
             btnLogin.performClick();
         }
     });

getInstrumentation().waitForIdleSync();

//check if we got at least one hit on the new activity
assertTrue(getInstrumentation().checkMonitorHit(aMonitor, 1)); 


0

私は個人的には使用していませんが、ApplicationTestCaseはあなたが探しているもののようです。


残念ながら、それが事実であることを示す例はありません。
シングルショット2009年

ええ、あなたが正しいように見えます...名前にだまされました。私はこれを理解することはできません。私がこれまでに得た最善のアプローチは、positronのActivityUnitTestCaseを使用して、次のアクティビティが開始されたことを確認することですが、それは一貫したストーリーを構築するのに役立ちません。あるいは、InstrumentationTestCase.launchActivityを使用すると、任意の数のアクティビティを開始できる場合がありますが、私はまだInstrumentationの内容を理解しようとしています。
エリック

0

受け入れられたアプローチは、さまざまな証明書によって署名された、さまざまなアプリケーションのさまざまなアクティビティで機能しますか?そうでない場合は、Robotiumが同じアプリケーション内のアクティビティをテストするための最良の方法です。


0

ActivityInstrumentationクラスを使用して複数のアクティビティを実行する別の方法があります。通常の自動化シナリオ...最初に必要なオブジェクトのフォーカスを取得してから、サンプルコードとしてSimpleキーを送信します。

button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);

唯一のことは、すべてのAPI呼び出しが私たちを助けることを理解することです。


0

この回答は、受け入れられた回答に基づいていますが、タイミングの問題を解決するために変更されました。タイミングの問題は、約5ダースのテストを追加した後に一貫性がありました。@ pajato1は、承認された回答コメントに引用されているように、タイミングの問題を解決したことで評価を得ています。

/**
 * Creates a test Activity for a given fully qualified test class name.
 *
 * @param fullyQualifiedClassName The fully qualified name of test activity class.
 *
 * @return The test activity object or null if it could not be located.
 */
protected AbstractTestActivity getTestActivity(final String fullyQualifiedClassName) {
    AbstractTestActivity result = null;

    // Register our interest in the given activity and start it.
    Log.d(TAG, String.format("Running test (%s) with main class: %s.", getName(), fullyQualifiedClassName));
    instrumentation = getInstrumentation();

    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(instrumentation.getTargetContext(), fullyQualifiedClassName);
    // Wait for the activity to finish starting
    Activity activity = instrumentation.startActivitySync(intent);

    // Perform basic sanity checks.
    assertTrue("The activity is null!  Aborting.", activity != null);
    String format = "The test activity is of the wrong type (%s).";
    assertTrue(String.format(format, activity.getClass().getName()), activity.getClass().getName().equals(fullyQualifiedClassName));
    result = (AbstractTestActivity) activity;

    return result;
}

0

モンキーツールのテストをお試しください

ステップ1:

android studioターミナルを開きます([ツール]-> [ターミナルを開く])

ステップ2:

モンキーを使用するには、コマンドプロンプトを開き、次のディレクトリに移動します。

 export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools

ステップ3:

このサルコマンドをターミナルに追加し、Enterキーを押します。

エミュレーターの魔法を見てください。

adb shell monkey -p com.example.yourpackage -v 500

500-テストのために送信されるイベントの頻度または数です。

このカウントを変更できます。

より多くの参照、

http://www.tutorialspoint.com/android/android_testing.htm

http://androidtesting.blogspot.in/2012/04/android-testing-with-monkey-tool.html


反対票を投じる人は、反対票を投じる理由を伝えなければなりません...これは機能するコードです..そして公式のテスト方法でもあります。間違いがあれば、答えを訂正する準備ができています
。– Ranjith Kumar 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.