Android
UIテストの作成 - Android
サーチ…
前書き
このドキュメントの目的は、アンドロイドのUIと統合テストを書く方法と目標を表現することです。 エスプレッソとUIAutomatorはGoogleによって提供されるので、これらのツールとそれぞれのラッパー(例:Appium、Spoonなど)に焦点を当てる必要があります。
構文
- アイドリングリソース
- String getName() - アイドル状態のリソースの名前を返します(ロギングおよび登録の冪等に使用されます)。
- boolean isIdleNow() - リソースが現在アイドル状態の場合にtrueを返します。
- void registerIdleTransitionCallback(IdlingResource.ResourceCallback callback) - 指定されたIdlingResource.ResourceCallbackをリソースに登録します。
備考
JUnitのルール:
MockWebServerの例とActivityTestRuleで見ることができるように、これらはすべてJUnitルールのカテゴリに分類されます。これらのルールは、自分自身で作成し、その動作を定義するテストごとに実行する必要があります。@see: https : //github.com/junit-team/junit4/ウィキ/ルール
アピウム
パラメーター
ドキュメントのバグが解決されるまで、パラメータにはいくつか問題があります。
パラメータ | 詳細 |
---|---|
クラスactivityClass | 開始するアクティビティ |
initialTouchMode | 開始時にアクティビティをタッチモードにする必要があります: https : //android-developers.blogspot.de/2008/12/touch-mode.html |
launchActivity | アクティビティをテストメソッドごとに1回起動する必要がある場合はtrue。最初のBeforeメソッドの前に起動され、最後のAfterメソッドの後に終了します。 |
MockWebServerの例
あなたの活動、断片、およびUIにバックグラウンド処理が必要な場合は、MockWebServerをAndroidデバイス上でローカルに実行することで、UIを閉じてテスト可能な環境にすることができます。
https://github.com/square/okhttp/tree/master/mockwebserver
最初のステップは、gradle依存性を含む:
testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'
モックサーバーの実行と使用の手順は次のとおりです。
- 模擬サーバオブジェクトを作成する
- 特定のアドレスとポート(通常はlocalhost:portnumber)で起動します。
- 特定の要求に対する応答をエンキューする
- テストを開始する
これはモックウェブサーバのgithubページでうまく説明されていますが、私たちの場合、すべてのテストでより良いものと再利用可能なものがほしいと思っています.JUnitのルールはうまくいきます:
/**
*JUnit rule that starts and stops a mock web server for test runner
*/
public class MockServerRule extends UiThreadTestRule {
private MockWebServer mServer;
public static final int MOCK_WEBSERVER_PORT = 8000;
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
startServer();
try {
base.evaluate();
} finally {
stopServer();
}
}
};
}
/**
* Returns the started web server instance
*
* @return mock server
*/
public MockWebServer server() {
return mServer;
}
public void startServer() throws IOException, NoSuchAlgorithmException {
mServer = new MockWebServer();
try {
mServer(MOCK_WEBSERVER_PORT);
} catch (IOException e) {
throw new IllegalStateException(e,"mock server start issue");
}
}
public void stopServer() {
try {
mServer.shutdown();
} catch (IOException e) {
Timber.e(e, "mock server shutdown error”);
}
}
}
ここで、前の例のような全く同じアクティビティがあると仮定してみましょう。この場合、ボタンアプリケーションをプッシュすると、ネットワークから何かを取得します: https : //someapi.com/name
これは、あなたが入力したNAME +テキストのように、スナックバーのテキストに連結されるテキスト文字列を返します。
/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
//espresso rule which tells which activity to start
@Rule
public final ActivityTestRule<SnackbarActivity> mActivityRule =
new ActivityTestRule<>(SnackbarActivity.class, true, false);
//start mock web server
@Rule
public final MockServerRule mMockServerRule = new MockServerRule();
@Override
public void tearDown() throws Exception {
//same as previous example
}
@Override
public void setUp() throws Exception {
//same as previous example
**//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
MyAppConfig.setEndpointURL("http://localhost:8000");
}
/**
*Test methods should always start with "testXYZ" and it is a good idea to
*name them after the intent what you want to test
**/
@Test
public void testSnackbarIsShown() {
//setup mockweb server
mMockServerRule.server().setDispatcher(getDispatcher());
mActivityRule.launchActivity(null);
//check is our text entry displayed and enter some text to it
String textToType="new snackbar text";
onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
//we check is our snackbar showing text from mock webserver plus the one we typed
onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
//click the button to show the snackbar
onView(withId(R.id.shownSnackbarBtn)).perform(click());
//assert that a view with snackbar_id with text which we typed and is displayed
onView(allOf(withId(android.support.design.R.id.snackbar_text),
withText(textToType))) .check(matches(isDisplayed()));
}
/**
*creates a mock web server dispatcher with prerecorded requests and responses
**/
private Dispatcher getDispatcher() {
final Dispatcher dispatcher = new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
if (request.getPath().equals("/name")){
return new MockResponse().setResponseCode(200)
.setBody("JazzJackTheRabbit");
}
throw new IllegalStateException("no mock set up for " + request.getPath());
}
};
return dispatcher;
}
ディスパッチャーを何らかのビルダーでラップすることをお勧めします。これにより、画面に新しい応答を簡単に連鎖させて追加することができます。例えば
return newDispatcherBuilder()
.withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
.withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
.withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());
IdlingResource
アイドリングリソースの力は、アプリケーションの処理(ネットワーキング、計算、アニメーションなど)がsleep()
で終了するのを待つ必要がなくなり、フレークさをもたらしたりテストを延期したりすることにあります。公式文書はここにあります 。
実装
IdlingResource
インターフェイスを実装する際には、次の3つのことが必要です。
-
getName()
- アイドル状態のリソースの名前を返します。 -
isIdleNow()
- あなたのxyzオブジェクト、操作などが現在アイドル状態かどうかをチェックします。 -
registerIdleTransitionCallback
(IdlingResource.ResourceCallback
callback) - オブジェクトがアイドル状態に移行したときに呼び出すコールバックを提供します。
今度は、独自のロジックを作成して、アプリがアイドル状態であるかどうかを判断する必要があります。これはアプリに依存しているためです。以下は、単純な例を示しています。オンラインの他の例もありますが、特定のアプリの実装は、特定のアイドリングリソースの実装をもたらします。
ノート
- アプリのコードに
IdlingResources
を置くいくつかのGoogleの例があります。 こんなことしないで。彼らはおそらく彼らが働く方法を示すためにそこにそれを配置しました。 - コードをきれいに保ち、責任の単一の原則を維持することはあなた次第です!
例
奇妙なことをするアクティビティがあり、断片が読み込まれるまでに時間がかかり、フラグメントからリソースを見つけることができないためにエスプレッソテストが失敗する(アクティビティの作成方法を変更する必要があるそれをスピードアップするため)。しかし、どのような場合でもシンプルにするために、次の例は、どのように見えるかを示しています。
例のアイドル状態のリソースには、2つのオブジェクトがあります。
- あなたが見つけて活動に付随するのを待つ必要がある断片のタグ 。
- フラグメントを見つけるために使用されるFragmentManagerオブジェクト。
/**
* FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
*/
public class FragmentIdlingResource implements IdlingResource {
private final FragmentManager mFragmentManager;
private final String mTag;
//resource callback you use when your activity transitions to idle
private volatile ResourceCallback resourceCallback;
public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
mFragmentManager = fragmentManager;
mTag = tag;
}
@Override
public String getName() {
return FragmentIdlingResource.class.getName() + ":" + mTag;
}
@Override
public boolean isIdleNow() {
//simple check, if your fragment is added, then your app has became idle
boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
if (idle) {
//IMPORTANT: make sure you call onTransitionToIdle
resourceCallback.onTransitionToIdle();
}
return idle;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
あなたのIdlingResource
書かれたので、あなたはそれをどこかで使う必要がありますか?
使用法
テストクラスの設定全体をスキップし、テストケースがどのように見えるかを見てみましょう:
@Test
public void testSomeFragmentText() {
mActivityTestRule.launchActivity(null);
//creating the idling resource
IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
//registering the idling resource so espresso waits for it
Espresso.registerIdlingResources(idlingResource1);
onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));
//lets cleanup after ourselves
Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}
JUnitルールとの組み合わせ
これは難しいことではありません。 JUnitテストルールの形式でアイドリングリソースを適用することもできます。たとえば、Volleyが入っているSDKをいくつか持っていて、EspressoがそのSDKを待つようにしたいとしましょう。各テストケースを調べたり、セットアップでそれを適用したりする代わりに、JUnitルールを作成して次のように書くことができます:
@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());
今これが例であるので、それを当然のものとしてはいけません。ここのコードはすべて架空のものであり、デモ目的でのみ使用されます。
public class SDKIdlingRule implements TestRule {
//idling resource you wrote to check is volley idle or not
private VolleyIdlingResource mVolleyIdlingResource;
//request queue that you need from volley to give it to idling resource
private RequestQueue mRequestQueue;
//when using the rule extract the request queue from your SDK
public SDKIdlingRule(SDKClass sdkClass) {
mRequestQueue = getVolleyRequestQueue(sdkClass);
}
private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
return sdkClass.getVolleyRequestQueue();
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
//registering idling resource
mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
Espresso.registerIdlingResources(mVolleyIdlingResource);
try {
base.evaluate();
} finally {
if (mVolleyIdlingResource != null) {
//deregister the resource when test finishes
Espresso.unregisterIdlingResources(mVolleyIdlingResource);
}
}
}
};
}
}