【问题标题】:How to convert Android SingleLaunchActivityTestCase to ActivityTestRule? (Instrumentation unit test)如何将 Android SingleLaunchActivityTestCase 转换为 ActivityTestRule? (仪器单元测试)
【发布时间】:2019-02-25 21:29:55
【问题描述】:

SingleLaunchActivityTestCase 的文档说这个类现在已被弃用,应该用ActivityTestRule 替换。但是这是怎么做到的呢?

SingleLaunchActivityTestCase 允许 Activity 启动一次并保持打开状态;然后在这段时间内可以运行多个测试,然后关闭 Activity。 ActivityTestRule 似乎没有这个功能 - 它总是为每个 @Test 方法重新启动 Activity。

那么是否可以让ActivityTestRule 启动一个 Activity 并使其保持打开状态,以及如何确保每个@Test 函数的context(来自activityTestRule.getActivity())不为空?

示例代码here

【问题讨论】:

    标签: android unit-testing android-espresso android-testing android-instrumentation


    【解决方案1】:

    我用乔恩·亚当斯的回答来举例说明:

    @RunWith(AndroidJUnit4.class)
    @LargeTest
    public class KeepActivityOpenDuringTests {
    
        @Rule
        public ActivityTestRule<MyActivity> activityTestRule =
                new ActivityTestRule<>(MyActivity.class, true, false);
    
        /**
         * When running the class as a test suite, setUp() is called repeatedly
         * for each test method. Use setupDone to prevent multiple setups.
         */
        private static boolean setupDone;
        // Static to persist across multiple tests
        private static Context context;
        private static MyActivity activityUnderTest;
    
        @Before
        public void setUp() throws Exception {
            // Launch the Activity manually, once, to get a real Context object.
            if (!setupDone) {
                activityUnderTest = activityTestRule.launchActivity(null);
                context = (Context) activityUnderTest;
                // continue setup of singletons...
                setupDone = true;
            }
        }
    
        @Test
        public void test1() {
            // Use context ...
        }
    
        @Test
        public void test2() {
           // Use activityUnderTest ...
        }
    
        @AfterClass
        public static void cleanUp() throws Exception {
            if (activityUnderTest != null) {
                activityUnderTest.finish();
                // Prevent any potential memory leaks
                activityUnderTest = null;
                context = null;
                setupDone = false;
            }
        }
    }
    

    当测试作为一个类或单独运行时,它的工作方式类似于 SingleLaunchActivityTestCase。

    【讨论】:

    • 请注意,这种测试仅在测试同步运行而不是并行运行时才有效,否则您可能会得到双重设置和/或不一致的活动状态。需要明确的是,这段代码确实完成了操作的要求,但同样,如上所述,这通常是一个不好的测试实践。
    • 使用BeforeClass而不是检查非同步静态初始化切换会更干净吗?
    • @JonAdams 有用的反馈,谢谢。也许我错了,但这真的被认为是不好的做法吗?它对于测试静态方法很有用,例如数据库访问,这需要在真实设备/模拟器上使用真实的context。以这种方式运行测试更高效、更快,而不是每次都启动和停止Activity。无论如何,它只是在复制SingleLaunchActivityTestCase 的行为。
    • 可能不是这个讨论的最佳地点,但是,快速版本是,这不是一个好主意,因为它增加了固定测试顺序和同步运行器等要求,这会减慢和限制事情,并将测试相互联系起来,因为一个测试可能会根据它所做或不做什么等来设置(或破坏)未来的测试。使测试变得脆弱并相互依赖。同样,这不是错误,只是一种不好的做法。不过,总体而言,集成测试是有效的。我指的是跨测试共享活动及其状态的做法是不好的做法。
    • 奇怪的是,我发现activityUnderTesttest1() 之后仍然强制为finish()。 Activity 在屏幕上不再可见,但 context 仍然存在。每次测试后有 2 种方法尝试完成活动:MonitoringInstrumentation.ActivityFinisher.run()ActivityTestRule.finishActivity()
    【解决方案2】:

    使用constructor that does not start the activity by default(将launchActivity 参数设置为false)。然后在您的设置中使用launchActivity(),但不要在每个测试方法中使用。这样您将自己启动一次,但每个测试都将在同一个实例上运行。

    您可能还希望在测试类结束时显式完成活动,以进行清理。

    注意:这通常不是测试的最佳实践,因为测试可能相互依赖(这不是一个好主意),或者根据它们运行的​​顺序等提供不正确的结果,因为活动在这种情况下,状态会从一个测试持续到下一个测试。

    【讨论】:

    猜你喜欢
    • 2014-05-22
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多