我正在尝试使用 FragmentScenario 测试片段。这个片段有自己的菜单。操作栏上有一个添加图标,单击此菜单项会启动一个子片段,用户可以从中添加新项目。所以我试图测试这种行为。但是,您可能知道,FragmentScenario 在 EmptyFragmentActivity 中启动片段,而不启动真正的宿主活动。由于操作栏不是片段布局的一部分,而是属于宿主活动,因此在测试期间操作栏和菜单甚至不可见。那么如何测试与菜单的交互呢?
我从官方文档中找到了这条信息:
如果您需要调用片段本身的方法,例如响应选项菜单中的选择,您可以通过实现 FragmentAction 安全地执行此操作:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<MyFragment>()
scenario.onFragment(fragment ->
fragment.onOptionsItemSelected(clickedItem) {
//Update fragment's state based on selected item.
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如何将正确的项目传递给 onOptionsItemSelected 回调?我试图将 addMenuItem 定义为成员变量并在 onCreateOptionsMenu 内部对其进行初始化,但它返回 null。在测试期间似乎没有调用 onCreateOptionsMenu。所以我不知道如何测试用户与菜单的交互。
我编写了一些片段场景测试,这些测试将截取屏幕截图和一些点击事件。由于未显示图像视图,很少有测试失败。
测试库( https://developer.android.com/training/basics/fragments/testing )中的测试活动似乎使用 FragmentActivity,它不是 AppCompatActivity 的子类。结果,这些图像视图不可见。我可以添加属性android:src以使其在测试中可见,但这将是一个丑陋的修复。还有其他解决办法吗?
感谢对此的任何评论或建议。
view_item.xml
<ImageView
android:id="@+id/ivRemove"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/spacing_16dp"
android:layout_marginTop="@dimen/spacing_16dp"
android:layout_marginBottom="@dimen/spacing_16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_remove" />
Run Code Online (Sandbox Code Playgroud)
构建.gradle.kts
defaultConfig {
this.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
this.vectorDrawables.useSupportLibrary = true
}
Run Code Online (Sandbox Code Playgroud) android android-testing android-vectordrawable android-fragmentscenario
我正在尝试使用Android Jetpack导航组件和片段测试库来测试片段交互。我的应用程序使用Java + Dagger2作为DI。
为了测试导航,我创建了一个JUnit测试
@Test
public void testNavigationToLoginFragment() {
// Create a mock NavController
NavController mockNavController = mock(NavController.class);
// Create a graphical FragmentScenario for the Intro Fragment
FragmentScenario<IntroFragment> introFragmentScenario = FragmentScenario.launchInContainer(IntroFragment.class);
// Set the NavController property on the fragment
introFragmentScenario.onFragment(fragment ->
Navigation.setViewNavController(fragment.requireView(), mockNavController)
);
// Verify that performing a click prompts the correct Navigation action
onView(withId(R.id.button_login)).perform(click());
verify(mockNavController).navigate(R.id.action_intro_fragment_to_login_fragment);
}
Run Code Online (Sandbox Code Playgroud)
每当我运行测试时,都会出现以下错误:
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: No injector factory bound for Class<XXX>
Run Code Online (Sandbox Code Playgroud)
我如何在这里注入片段?是否可以将DaggerFragments与FragmentScenario一起使用?
我在 Android 中尝试使用 FragmentScenario 时遇到此错误
error: package androidx.fragment.app.testing does not exist
import androidx.fragment.app.testing.FragmentScenario;
^
Run Code Online (Sandbox Code Playgroud)
这是我的简单单元测试示例:
package com.example.myapplication;
import androidx.fragment.app.testing.FragmentScenario;
import android.os.Build;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(AndroidJUnit4.class)
@Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.P)
public class MainActivityTest {
@Test
public void testFragmentScenario() {
FragmentScenario<BlankFragment> scenario = FragmentScenario.launchInContainer(BlankFragment.class);
}
}
Run Code Online (Sandbox Code Playgroud)
构建.gradle(应用程序):
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
def test_version = '1.2.0'
def fragment_version = …Run Code Online (Sandbox Code Playgroud) 对于我的测试,我在空活动根视图容器中启动片段
@Before
fun init() {
scenario = launchFragmentInContainer(null, R.style.Theme_AppCompat) {
MyFragment()
}
}
Run Code Online (Sandbox Code Playgroud)
在我的片段中,我配置工具栏以提供后退按钮
(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
Run Code Online (Sandbox Code Playgroud)
所以当我运行测试时,我得到一个类强制转换异常
java.lang.ClassCastException: androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity cannot be cast to androidx.appcompat.app.AppCompatActivity
Run Code Online (Sandbox Code Playgroud)
所以我必须添加一个守卫
if(requireActivity() !is FragmentScenario.EmptyFragmentActivity)
configureToolBar()
Run Code Online (Sandbox Code Playgroud)
那么是否有另一种方法来配置工具栏,以便我可以使用 espresso 测试后退按钮意图启动?
编辑
显然有一种方法可以创建自定义活动/容器。
我在运行片段测试时遇到此错误,这是一个启动 fragmentInContatiner 的简单测试:
Cannot find a version of 'androidx.test:monitor' that satisfies the version constraints:
Dependency path 'Host Work.features:ui-home:unspecified' --> 'androidx.test:runner:1.2.0' --> 'androidx.test:monitor:1.2.0'
Constraint path 'Host Work.features:ui-home:unspecified' --> 'androidx.test:monitor:{strictly 1.1.1}' because of the following reason: debugRuntimeClasspath uses version 1.1.1
Dependency path 'Host Work.features:ui-home:unspecified' --> 'androidx.fragment:fragment-testing:1.2.0-alpha02' --> 'androidx.test:core:1.1.0' --> 'androidx.test:monitor:1.1.1'
Run Code Online (Sandbox Code Playgroud)
这是我造成此问题的 gradle 依赖项:
implementation 'androidx.fragment:fragment:1.2.0-alpha02'
debugImplementation 'androidx.fragment:fragment-testing:1.2.0-alpha02'
implementation 'androidx.test:core:1.2.0-alpha02'
implementation 'androidx.test:runner:1.2.0-alpha02'
Run Code Online (Sandbox Code Playgroud) 我正在尝试按照以下说明测试片段:https ://developer.android.com/training/basics/fragments/testing
launchFragmentInContainer但是,当我从测试中调用时,我遇到了以下崩溃。
堆栈跟踪:
android.view.InflateException: Binary XML file line #16: Binary XML file line #16: Error inflating class <unknown>
Caused by: android.view.InflateException: Binary XML file line #16: Error inflating class <unknown>
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
...
at android.view.LayoutInflater.$$robo$$android_view_LayoutInflater$createView(LayoutInflater.java:647)
...
at com.myapp.poll.PollHomeFragment.onCreateView(PollHomeFragment.kt:31)
...
Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 13: TypedValue{t=0x2/d=0x7f0301b1 a=-1}
at android.content.res.TypedArray.getDrawableForDensity(TypedArray.java:946)
at android.content.res.TypedArray.getDrawable(TypedArray.java:930)
at android.view.View.__constructor__(View.java:5010)
at android.view.View.<init>(View.java)
at android.widget.TextView.<init>(TextView.java)
...
at com.myapp.poll.PollHomeFragment.onCreateView(PollHomeFragment.kt:31)
Run Code Online (Sandbox Code Playgroud)
这是布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" …Run Code Online (Sandbox Code Playgroud) android android-fragments android-testing android-fragmentscenario
我有一个简单的Fragment像这样:
class SomeFragment : DaggerFragment() {
...
}
Run Code Online (Sandbox Code Playgroud)
现在我想Fragment用它来测试这个FragmentScenario
class LoginFragmentTest {
@Test
fun test() {
launchFragmentInContainer<SomeFragment>()
onView(withId(R.id.someButton))
.check(matches(isDisplayed()))
}
}
Run Code Online (Sandbox Code Playgroud)
但每次我尝试测试时总是:
java.lang.IllegalArgumentException:找不到 <...SomeFragment> 的注入器
我怎样才能正确运行测试?有人可以帮我吗?
我使用的架构与 Google 示例几乎相同:GithubBrowserSample。
该字段被注入到我的 Fragment 类中:
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val viewModel: TrainingListViewModel by viewModels {
viewModelFactory
}
Run Code Online (Sandbox Code Playgroud)
在我的所有片段测试中,我收到此错误,该错误对应于通过注入实例化的字段:
java.lang.RuntimeException: kotlin.UninitializedPropertyAccessException: lateinit property viewModelFactory has not been initialized
at androidx.test.runner.MonitoringInstrumentation.runOnMainSync(MonitoringInstrumentation.java:441)
at androidx.test.core.app.ActivityScenario.onActivity(ActivityScenario.java:564)
at androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:300)
at androidx.fragment.app.testing.FragmentScenario.launchInContainer(FragmentScenario.java:282)
at com.maximesarrato.lafayapp.ui.training.TrainingListFragmentTest.init(TrainingListFragmentTest.kt:213)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:76)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104) …Run Code Online (Sandbox Code Playgroud)