如果测试跨越多个活动,是否有办法获得当前活动?
getActivtiy()方法只提供一个用于启动测试的活动。
我尝试了类似下面的内容,
public Activity getCurrentActivity() {
Activity activity = null;
ActivityManager am = (ActivityManager) this.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
try {
Class<?> myClass = taskInfo.get(0).topActivity.getClass();
activity = (Activity) myClass.newInstance();
}
catch (Exception e) {
}
return activity;
}
但我得到了空对象。
答案 0 :(得分:27)
如果您只需要对当前Activity
进行检查,请使用可能与原生Espresso单行相关以检查是否已启用预期意图:
intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
Espresso还会向您显示同时触发的意图,如果不匹配您的意图。
您需要的唯一设置是在测试中将ActivityTestRule
替换为IntentsTestRule
,以便跟踪启动的意图。并确保此库位于build.gradle
依赖项中:
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
答案 1 :(得分:26)
在Espresso中,您可以使用ActivityLifecycleMonitorRegistry
,但它不受官方支持,因此在将来的版本中可能无效。
以下是它的工作原理:
Activity getCurrentActivity() throws Throwable {
getInstrumentation().waitForIdleSync();
final Activity[] activity = new Activity[1];
runTestOnUiThread(new Runnable() {
@Override
public void run() {
java.util.Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
activity[0] = Iterables.getOnlyElement(activities);
}});
return activity[0];
}
答案 2 :(得分:10)
我喜欢@Ryan的版本,因为它没有使用未记录的内部版本,但你可以写得更短:
private Activity getCurrentActivity() {
final Activity[] activity = new Activity[1];
onView(isRoot()).check(new ViewAssertion() {
@Override
public void check(View view, NoMatchingViewException noViewFoundException) {
activity[0] = (Activity) view.getContext();
}
});
return activity[0];
}
请注意,尽管在Firebase测试实验室中运行测试时这不起作用。那失败了
java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity
答案 3 :(得分:5)
Android团队已将ActivityTestRule
替换为ActivityScenario。我们可以使用activityTestRule.getActivity()
来ActivityTestRule
,但不能使用ActivityScenario。这是我关于从ActivityScenario获取Activity
的解决方案的工作(受@Ryan和@Fabian解决方案的启发)
@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
...
private fun getActivity(): Activity? {
var activity: Activity? = null
activityRule.scenario.onActivity {
activity = it
}
return activity
}
答案 4 :(得分:4)
如果您的测试用例中只有活动,则可以执行以下操作:
Rule
@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
Activity
:mActivityTestRule.getActivity()
那是一块馅饼!
答案 5 :(得分:3)
try-catch
答案 6 :(得分:3)
我无法使任何其他解决方案起作用,所以我最终不得不这样做:
声明您的ActivityTestRule
:
@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
声明一个final
Activity数组来存储您的活动:
private final Activity[] currentActivity = new Activity[1];
添加帮助方法以注册应用程序上下文以获取生命周期更新:
private void monitorCurrentActivity() {
mainActivityTestRule.getActivity().getApplication()
.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) { }
@Override
public void onActivityStarted(final Activity activity) { }
@Override
public void onActivityResumed(final Activity activity) {
currentActivity[0] = activity;
}
@Override
public void onActivityPaused(final Activity activity) { }
@Override
public void onActivityStopped(final Activity activity) { }
@Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState) { }
@Override
public void onActivityDestroyed(final Activity activity) { }
});
}
添加辅助方法以获取当前活动
private Activity getCurrentActivity() {
return currentActivity[0];
}
因此,一旦您启动了第一项活动,只需致电monitorCurrentActivity()
,然后只要您需要参考当前活动,就可以致电getCurrentActivity()
答案 7 :(得分:1)
基于https://stackoverflow.com/a/50762439/6007104,这是用于访问当前活动的通用util的Kotlin版本:
ANTLR Tool version 4.5.3 used for code generation does not match the
current runtime version 4.7.1ANTLR Runtime version 4.5.3 used for parser
compilation does not match the current runtime version 4.7.1ANTLR Tool
version 4.5.3 used for code generation does not match the current runtime
version 4.7.1ANTLR Runtime version 4.5.3 used for parser compilation does
not match the current runtime version 4.7.1
Cannot find
import com.example.app.databinding.FragmentMyBindingImpl;
然后像这样简单地使用:
class CurrentActivityDelegate(application: Application) {
private var cachedActivity: Activity? = null
init {
monitorCurrentActivity(application)
}
fun getCurrentActivity() = cachedActivity
private fun monitorCurrentActivity(application: Application) {
application.registerActivityLifecycleCallbacks(
object : Application.ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
cachedActivity = activity
Log.i(TAG, "Current activity updated: ${activity::class.simpleName}")
}
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity?) {}
override fun onActivityPaused(activity: Activity?) {}
override fun onActivityStopped(activity: Activity?) {}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
override fun onActivityDestroyed(activity: Activity?) {}
})
}
}
答案 8 :(得分:0)
@lacton提出的解决方案对我不起作用,可能是因为活动未处于ActivityLifecycleMonitorRegistry
报告的状态。
我甚至尝试Stage.PRE_ON_CREATE
仍然没有获得任何活动。
注意:我无法使用ActivityTestRule
或IntentTestRule
,因为我使用activitiy-alias
开始了我的活动并且没有任何意义当我想测试以查看别名是否有效时,在测试中使用实际的类。
我的解决方案是通过ActivityLifecycleMonitorRegistry
订阅生命周期更改并阻止测试线程直到活动启动:
// NOTE: make sure this is a strong reference (move up as a class field) otherwise will be GCed and you will not stably receive updates.
ActivityLifecycleCallback lifeCycleCallback = new ActivityLifecycleCallback() {
@Override
public void onActivityLifecycleChanged(Activity activity, Stage stage) {
classHolder.setValue(((MyActivity) activity).getClass());
// release the test thread
lock.countDown();
}
};
// used to block the test thread until activity is launched
final CountDownLatch lock = new CountDownLatch(1);
final Holder<Class<? extends MyActivity>> classHolder = new Holder<>();
instrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(lifeCycleCallback);
}
});
// start the Activity
intent.setClassName(context, MyApp.class.getPackage().getName() + ".MyActivityAlias");
context.startActivity(intent);
// wait for activity to start
lock.await();
// continue with the tests
assertTrue(classHolder.hasValue());
assertTrue(classHolder.getValue().isAssignableFrom(MyActivity.class));
Holder
基本上是一个包装器对象。您可以使用数组或其他任何东西来捕获匿名类中的值。
答案 9 :(得分:0)
我改进了@Fabian Streitel的答案,因此您可以在没有ClassCastException的情况下使用此方法
1
82
[label_id] => 13
[label_id] => 15
[label_id] => 85
242
[label_id] => 40
[label_id] => 41
0
494
[label_id] => 84
[label_id] => 77
496
[label_id] => 43
答案 10 :(得分:-1)
build.gradle
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',
{
exclude group: 'com.android.support', module: 'support-annotations'
})
并且,在您的仪器化测试课程中
@Rule
public ActivityTestRule<MainActivity> intentsTestRule = new ActivityTestRule<>(MainActivity.class);
MainActivity mainActivity;
@Before
public void setUp() throws Exception {
mainActivity = intentsTestRule.getActivity(); //now Activity's view gets created
//Because Activity's view creation happens in UI Thread, so all the test cases, here, are used by UI Thread
}
答案 11 :(得分:-1)
接受的答案在许多浓咖啡测试中可能不起作用。以下适用于在API 25设备上运行的espresso版本2.2.2和Android编译/目标SDK 27:
<div class="wrap heightwrap wow fadeInDown" data-wow-delay="0.4s">
<svg viewBox="0 0 1000 300" class="svg-defs">
<!-- Symbol with Logo -->
<symbol id="s-text2">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="30%" y="-25%" width="700" height="800" viewBox="0 0 1000 800">
<defs>
<rect id="a" x="50%" y="50%" width="612" height="792"/>
</defs>
<clipPath id="b">
<use href="#a" overflow="visible"/>
</clipPath>
<path clip-path="url(#b)" d="M468.896,118.65l-80.667,254.289H145.563l-24.334-62.667h188 c0,0,26.167,0.167,33.311-18.676l15.356-50.99c20.333-62.333,22.67-67.217-34-67.667c-15.121-0.12-30.689,0.122-39.254,0.072 c-17.878-0.105-22.246,0.139-20.368-10.094l10.712-34.979c0,0,1.285-9.287,17.66-9.287"/>
<path clip-path="url(#b)" d="M275.563,267.9c28.667,0,28.013-1.13,35.833-28.5c8-28,9.457-29.082-12-29.5 c-10.548-0.205-26.166,0-26.166,0c-51.959,0-64.834,1.5-58.222-24.03c2.682-10.354,6.587-25.693,12.221-45.637 c4.024-14.247,8.103-29.353,11.369-38.006C250.481,70.74,271.842,75.708,343.23,75.9c92.334,0.249,139.333,0,139.333,0l20.333-62 h-314l-41.553,149.031c0,0-2.537,10.65-6.408-0.212L87.896,13.9h-69l89.667,254H275.563"/>
</svg>
</symbol>
<!-- Mask with text -->
<mask id="m-text2" maskunits="userSpaceOnUse" maskcontentunits="userSpaceOnUse">
<rect width="100%" height="100%" class="mask__shape">
</rect>
<use href="#s-text2" class="mask__text"></use>
</mask>
</svg>
<div class="box-with-text">
<!-- Container for video -->
<div class="text-fill">
<video class="video" src="/css/fractal3.mp4" autoplay loop></video>
</div>
<!-- Visible SVG -->
<svg viewBox="0 0 1000 300" class="svg-inverted-mask">
<rect width="100%" height="100%" mask="url(#m-text2)" class="shape--fill" />
<use href="#s-text2" class="text--transparent"></use>
</svg>
</div>
</div>