Android - Independent Fragment UI 测试工具
Android - Independent Fragment UI testing tool
我一直在寻找一种方法来单独测试我的片段的 UI(即独立于其他片段和活动),但我找不到方法。
特别是,假设我有片段 A、片段 B 和片段 C。转到片段 C 的唯一方法(在应用程序方面)是首先通过片段 A 和片段 B。我正在寻找一种直接测试片段 C 的方法(可能通过模拟其依赖项,如果存在的话),而不必通过片段 A 和 B。
目前我调查过的工具:
monkey:仅用于通过命令行生成伪随机事件。不是我想要的。
monkey运行ner:它可以 运行 Python 程序将事件流发送到我的 Android 应用程序,但它不能针对特定的直接使用这些脚本进行分段。
Espresso:白盒测试工具。这接近我想要的,但它仍然需要在到达片段 C 之前通过片段 A 和 B(即,您需要启动您的应用程序,然后测试将从那里 运行)。
UI Automator:黑盒测试工具。这也很接近,但同样,它需要在测试我想要的片段(片段 C)之前通过之前的片段。
有什么方法可以直接测试碎片人t的UI吗?
您可以使用 Robotium。这是为了 android UI 测试。
我正在使用自定义 FragmentTestRule
和 Espresso 单独测试我的每个 Fragments
。
我有一个专用的 TestActivity
可以在我的应用程序中显示经过测试的 Fragments
。在我的例子中,Activity
仅存在于 debug
变体中,因为我的仪器测试 运行 反对 debug
.
TL;DR 使用真棒 FragmentTestRule library by @brais-gabin。
1.使用内容视图在 src/debug/java/your/package/TestActivity.java
中创建 TestActivity
,其中测试的 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。在 androidTest
变体中创建 FragmentTestRule
src/androidTest/java/your/test/package/FragmentTestRule.java
:
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()));
}
}
我开发了FragmentTestRule an Andorid library using the 。它允许您单独测试 Fragment
。
您只需添加:
@Rule
public FragmentTestRule<?, FragmentWithoutActivityDependency> fragmentTestRule =
FragmentTestRule.create(FragmentWithoutActivityDependency.class);
如果您正在使用导航架构组件并且在您的应用中使用单个 activity 架构,您可以通过 深度链接到目标片段(使用适当的参数)在测试开始时。
例如:
@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
}
这样做会直接启动片段。如果您的测试有效,那么这证明您的应用程序不会在片段被深层链接到时崩溃。如果进程被系统终止并尝试重建堆栈并重新启动片段,它还确保应用程序将保持稳定。
我一直在寻找一种方法来单独测试我的片段的 UI(即独立于其他片段和活动),但我找不到方法。
特别是,假设我有片段 A、片段 B 和片段 C。转到片段 C 的唯一方法(在应用程序方面)是首先通过片段 A 和片段 B。我正在寻找一种直接测试片段 C 的方法(可能通过模拟其依赖项,如果存在的话),而不必通过片段 A 和 B。
目前我调查过的工具:
monkey:仅用于通过命令行生成伪随机事件。不是我想要的。
monkey运行ner:它可以 运行 Python 程序将事件流发送到我的 Android 应用程序,但它不能针对特定的直接使用这些脚本进行分段。
Espresso:白盒测试工具。这接近我想要的,但它仍然需要在到达片段 C 之前通过片段 A 和 B(即,您需要启动您的应用程序,然后测试将从那里 运行)。
UI Automator:黑盒测试工具。这也很接近,但同样,它需要在测试我想要的片段(片段 C)之前通过之前的片段。
有什么方法可以直接测试碎片人t的UI吗?
您可以使用 Robotium。这是为了 android UI 测试。
我正在使用自定义 FragmentTestRule
和 Espresso 单独测试我的每个 Fragments
。
我有一个专用的 TestActivity
可以在我的应用程序中显示经过测试的 Fragments
。在我的例子中,Activity
仅存在于 debug
变体中,因为我的仪器测试 运行 反对 debug
.
TL;DR 使用真棒 FragmentTestRule library by @brais-gabin。
1.使用内容视图在 src/debug/java/your/package/TestActivity.java
中创建 TestActivity
,其中测试的 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。在 androidTest
变体中创建 FragmentTestRule
src/androidTest/java/your/test/package/FragmentTestRule.java
:
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()));
}
}
我开发了FragmentTestRule an Andorid library using the Fragment
。
您只需添加:
@Rule
public FragmentTestRule<?, FragmentWithoutActivityDependency> fragmentTestRule =
FragmentTestRule.create(FragmentWithoutActivityDependency.class);
如果您正在使用导航架构组件并且在您的应用中使用单个 activity 架构,您可以通过 深度链接到目标片段(使用适当的参数)在测试开始时。
例如:
@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
}
这样做会直接启动片段。如果您的测试有效,那么这证明您的应用程序不会在片段被深层链接到时崩溃。如果进程被系统终止并尝试重建堆栈并重新启动片段,它还确保应用程序将保持稳定。