【问题标题】:How to determine if Android Application is started with JUnit testing instrumentation?如何确定 Android 应用程序是否使用 JUnit 测试工具启动?
【发布时间】:2014-02-17 12:16:42
【问题描述】:

我需要在运行时从代码中确定应用程序是否在 TestInstrumentation 下运行。

我可以使用一些 env/system 变量来初始化测试环境,但是 Eclipse ADK 启动配置不允许我这样做。

默认的 Android 系统属性和环境没有任何关于它的数据。此外,无论应用程序是定期启动还是在测试中,它们都是相同的。

这可能是一个解决方案:Is it possible to find out if an Android application runs as part of an instrumentation test 但由于我不测试活动,因此所有建议的方法都不起作用。 ActivityManager.isRunningInTestHarness() 方法在后台使用了这个:

SystemProperties.getBoolean("ro.test_harness") 

在我的情况下总是返回 false。 (要使用隐藏的 android.os.SystemProperties 类,我使用反射)。

我还能做些什么来尝试从应用程序内部确定它是否正在测试中?

【问题讨论】:

    标签: android junit bootstrapping


    【解决方案1】:

    我找到了一个 hacky 解决方案:在应用程序之外,可以尝试从测试包中加载一个类。如果应用程序类加载器在测试中运行,它可以从测试项目中按名称加载类。在其他情况下,找不到该类。

    private static boolean isTestMode() {
      boolean result;
      try {
        application.getClassLoader().loadClass("foo.bar.test.SomeTest");
        // alternatively (see the comment below):
        // Class.forName("foo.bar.test.SomeTest");
        result = true;
      } catch (final Exception e) {
    result = false;
      }
      return result;
    }
    

    我承认这并不优雅,但它确实有效。将不胜感激适当的解决方案。

    【讨论】:

    • 我有一个想法,我想我会分享:为了不让isTestMode() 依赖于我的一个实际测试类(不太可能被重命名或删除),我添加了一个在我的“androidTest”目录中名为InstrumentationTestIndicator 的空类isTestMode() 然后尝试加载。非常感谢你做的这些!我刚刚在我的项目中采用了这个技巧!
    【解决方案2】:

    我们创建了一个将参数传递给 MainActivity 并在 onCreate 方法中使用它的解决方案,使您能够定义如何创建 Activity。

    MainActivity 类中,我们创建了一些常量,也可以是枚举。我们也创建了一个静态属性。

    public class MainActivity {
        public static final int APPLICATION_MODE = 5;
        public static final int UNIT_TEST_MODE = 10;
        public static final int OTHER_MODE = 15;
    
        public static int activityMode = APPLICATION_MODE;
        (...)
    
        @Override
        protected void onCreate(Bundle icicle) {
            super.onCreate(icicle);
    
            switch (activityMode) {
                case OTHER_MODE:
                    (...)
                    break;
    
                case UNIT_TEST_MODE:
                    Log.d(TAG, "Is in Test Mode!");
                    break;
    
                case APPLICATION_MODE:
                    (...)
                    break;
            }
            (...)
        }
        (...)
    }
    

    在调用super.setUp() 方法之前,我们将MainActivityTest 类抽象,创建了setApplicationMode 并在setUp() 方法中调用此方法。

    public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
    
        protected void setUp() throws Exception {
            setApplicationMode();  // <=====
            super.setUp();
            getActivity();
            (...)
        }
    
        (...)
        public void setApplicationMode() {
            MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
        }
    }
    

    所有其他测试类都继承自MainActivityTest,如果我们希望它具有其他行为,我们可以简单地重写setApplicationMode 方法。

    public class OtherMainActivityTest extends MainActivityTest {
        (...)
        @Override
        public void setApplicationMode() {
            MainActivity.activityMode = MainActivity.OTHER_MODE;
        }
    }
    

    用户nathan-almeida 是此解决方案的共同作者的朋友。

    【讨论】:

    • 太好了,您找到了解决方案,但我的天哪——对于复杂应用程序中的每个活动来说,这是一场噩梦。此外,您将使用所有这些代码将其投入生产..
    • 随着应用程序活动的增加,这将成为维护和可读性的噩梦。生产应用程序不应该了解单元测试或自动化测试。但是,在设计/实现生产代码时可以实践 TDD(测试驱动开发),以使单元和自动化测试更容易实现。
    【解决方案3】:

    isTestMode() 解决方案不适用于 Android Studio 1.2.1.1。我们公司的全能 Krzysztof 使用以下方法调整了您的方法:

    Class.forName("foo.bar.test.SomeTest");
    

    而不是 getClassLoader()。感谢 Krzysztof!

    【讨论】:

    • 是的,Krzysztofs 往往是这样的
    猜你喜欢
    • 2012-03-08
    • 1970-01-01
    • 2015-03-02
    • 2015-12-06
    • 1970-01-01
    • 2013-01-22
    • 2011-11-10
    • 1970-01-01
    • 2019-12-28
    相关资源
    最近更新 更多