フラグメントのUIを個別に(つまり、他のフラグメントやアクティビティから独立して)テストする方法を探していましたが、それを行う方法が見つかりません。
特に、フラグメントA、フラグメントB、フラグメントCがあるとします。フラグメントCに移動する唯一の方法(アプリごと)は、最初にフラグメントAとフラグメントBを通過することです。フラグメントAとBを通過せずに、フラグメントCを直接(依存関係が存在する場合はモックすることで)テストする方法を探しています。
これまでに調査したツール:
サル:コマンドラインから疑似ランダムイベントを生成するためにのみ使用されます。私が欲しいものではありません。
monkeyrunner:Pythonプログラムを実行して、イベントストリームを私のAndroidアプリに送信できますが、これらのスクリプトで特定のフラグメントを直接ターゲットにすることはできません。
Espresso:ホワイトボックステストツール。これは私が望むものに近づきますが、フラグメントCに到達する前にフラグメントAとBを通過する必要があります(つまり、アプリを起動する必要があり、そこからテストが実行されます)。
UI Automator:ブラックボックステストツール。これも近づいていますが、必要なフラグメント(フラグメントC)をテストする前に、前のフラグメントを通過する必要があります。
FragmenのUIをテストする tを直接行う方法はありますか?
カスタムのFragmentTestRule
とEspressoを使用して、それぞれのFragments
を個別にテストしています。
アプリでテスト済みのTestActivity
を表示する専用のFragments
があります。私の場合、インストルメンテーションテストはActivity
に対して実行されるため、debug
はdebug
バリアントにのみ存在します。
TL; DR @ brais-gabin による素晴らしい FragmentTestRuleライブラリを使用します。
1。テストされたTestActivity
が追加されるコンテンツビューを使用してsrc/debug/Java/your/package/TestActivity.Java
にFragment
を作成します。
@VisibleForTesting
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setId(R.id.container);
setContentView(frameLayout);
}
}
2。 debug
バリアントのAndroidManifest.xmlを作成し、TestActivity
を宣言します。これは、テスト時にTestActivity
を開始するために必要です。このマニフェストをsrc/debug/AndroidManifest.xml
のdebug
バリアントに追加します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android">
<application>
<activity Android:name="your.package.TestActivity"/>
</application>
</manifest>
3。 src/androidTest/Java/your/test/package/FragmentTestRule.Java
のFragmentTestRule
バリアントにandroidTest
を作成します。
public class FragmentTestRule<F extends Fragment> extends ActivityTestRule<TestActivity> {
private final Class<F> mFragmentClass;
private F mFragment;
public FragmentTestRule(final Class<F> fragmentClass) {
super(TestActivity.class, true, false);
mFragmentClass = fragmentClass;
}
@Override
protected void afterActivityLaunched() {
super.afterActivityLaunched();
getActivity().runOnUiThread(() -> {
try {
//Instantiate and insert the fragment into the container layout
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
mFragment = mFragmentClass.newInstance();
transaction.replace(R.id.container, mFragment);
transaction.commit();
} catch (InstantiationException | IllegalAccessException e) {
Assert.fail(String.format("%s: Could not insert %s into TestActivity: %s",
getClass().getSimpleName(),
mFragmentClass.getSimpleName(),
e.getMessage()));
}
});
}
public F getFragment(){
return mFragment;
}
}
4。次に、Fragments
を個別にテストできます。
public class MyFragmentTest {
@Rule
public FragmentTestRule<MyFragment> mFragmentTestRule = new FragmentTestRule<>(MyFragment.class);
@Test
public void fragment_can_be_instantiated() {
// Launch the activity to make the fragment visible
mFragmentTestRule.launchActivity(null);
// Then use Espresso to test the Fragment
onView(withId(R.id.an_id_in_the_fragment)).check(matches(isDisplayed()));
}
}
@ thaussmaのアイデア を使用して FragmentTestRule Andoridライブラリを開発しました。 Fragment
sを個別にテストできます。
これを追加する必要があります:
@Rule
public FragmentTestRule<?, FragmentWithoutActivityDependency> fragmentTestRule =
FragmentTestRule.create(FragmentWithoutActivityDependency.class);
詳細はこちら 。
Robotium を使用できます。これはAndroid UIテスト用です。
ナビゲーションアーキテクチャコンポーネントを使用していて、アプリで単一のアクティビティアーキテクチャを使用している場合は、テストの開始時に(適切な引数を使用して)ターゲットフラグメントにディープリンクすることで、各フラグメントをすばやくテストできます。
例えば:
@Rule
@JvmField
var activityRule = ActivityTestRule(MainActivity::class.Java)
protected fun launchFragment(destinationId: Int,
argBundle: Bundle? = null) {
val launchFragmentIntent = buildLaunchFragmentIntent(destinationId, argBundle)
activityRule.launchActivity(launchFragmentIntent)
}
private fun buildLaunchFragmentIntent(destinationId: Int, argBundle: Bundle?): Intent =
NavDeepLinkBuilder(InstrumentationRegistry.getInstrumentation().targetContext)
.setGraph(R.navigation.navigation)
.setComponentName(MainActivity::class.Java)
.setDestination(destinationId)
.setArguments(argBundle)
.createTaskStackBuilder().intents[0]
destinationIdは、ナビゲーショングラフのフラグメント宛先IDです。フラグメントを起動する準備ができたら実行される呼び出しの例を次に示します。
launchFragment(R.id.target_fragment, targetBundle())
private fun targetBundle(): Bundle? {
val bundle = Bundle()
bundle.putString(ARGUMENT_ID, "Argument needed by fragment")
return bundle
}
このようにすると、フラグメントが直接起動します。テストが機能する場合、これは、フラグメントがディープリンクされているときにアプリがクラッシュしないことを証明します。また、プロセスがシステムによって強制終了され、スタックを再構築してフラグメントを再起動しようとした場合でも、アプリが安定することを保証します。