有没有办法判断一个Android应用程序是否作为测试框架的一部分运行?

17

是否有一种运行时检查方式,可以让应用程序判断它是否作为插桩测试的一部分运行?

背景:我们的应用程序在启动时执行数据库同步。但只有正常启动时才需要进行此操作。这会干扰数据库同步测试的插桩测试,这并不令人意外。

对于所有其他测试,这都是浪费CPU周期。


你找到任何解决方案了吗? - Piotr
8个回答

12

一种更简单的解决方案是检查一个只会出现在测试类路径中的类,它可以与JUnit 4配合使用(不像使用ActivityUnitTestCase的解决方案),并且不需要发送自定义意图到您的活动/服务(在某些情况下甚至可能不可能)。

private boolean isTesting() {
    try {
        Class.forName("com.company.SomeTestClass");
        return true;
    } catch (ClassNotFoundException e) {
        return false;
    }
}

7
如果您正在使用Robolectric,可以按照以下方式进行操作:
public boolean isUnitTest() {
        String device = Build.DEVICE;
        String product = Build.PRODUCT;
        if (device == null) {
            device = "";
        }

        if (product == null) {
            product = "";
        }
        return device.equals("robolectric") && product.equals("robolectric");
    }

6

9
无论我运行测试还是应用程序配置,它都会返回 false。 - Piotr
这种方法基本上只被系统应用程序使用,当它们需要确定是否在某个平台测试工具中运行时。请参见我的错误报告:http://b.android.com/191171 - Thomas Keller
根据最新的文档,应该使用ActivityManager.isRunningInUserTestHarness()(但值得注意的是,它仅在API 29中可用,甚至不在support/androidx库中)。 - Top-Master

2

1
有趣。我已经拥有了一个自定义应用程序,在那里我可以轻松地设置一个仪器测试标志。我明天会试试看。 - Martin
还有一件事需要考虑 - 您可以使用RoboGuice http://code.google.com/p/roboguice/根据需要注入仅用于测试的db同步代码版本(这实际上是我们在测试代码中所做的)。 这里有我们设置的描述:http://www.swiftkey.net/blog/?p=496 - Paul Butcher
似乎只有Activity和Service测试用例有setApplication方法,而简单的AndroidTestCase没有。但是它们似乎仍然可以正常运行。总之,这似乎是唯一的问题。谢谢。 - Martin
如果你需要更灵活的东西,RoboGuice绝对也可以使用。虽然设置起来有点麻烦 :-) - Paul Butcher

2

d = (◕‿↼) 很好的回答, 但是如果一些库开发者(比如我)想知道主机(或使用该库的应用程序)是否正在进行测试,则可以尝试:

import android.content.pm.ApplicationInfo;

// ...

private static int wasTestRun = 0xDEAD;

/**
 * Should only be used to speed up testing (no behavior change).
 * @return true in tests, if Gradle has the right dependencies.
 */
public static boolean isTestRun(@NonNull Context context) {
    if (wasTestRun != 0xDEAD) {
        return wasTestRun != 0;
    }
    // Ignore release builds (as App may be using JUnit by mistake).
    if (isDebuggable(context)) {
        try {
            Class.forName("org.junit.runner.Runner");
            wasTestRun = 1;
            return true;
        } catch (ClassNotFoundException ignored) {
        }
    }
    wasTestRun = 0;
    return false;
}

public static boolean isDebuggable(@Nullable Context context) {
    return context != null && (context.getApplicationContext()
            .getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}


请注意,我没有使用任何AtomicBoolean或其他辅助工具,因为它已经非常快了(而锁定可能会降低速度)。

1
你可以向你的Activity传递一个意图额外参数,指示它正在被测试。
1)在你的测试中,向你的Activity传递“testMode”额外参数:
public void setUp() throws Exception {
    super.setUp();

    Intent activityIntent = new Intent();
    activityIntent.putExtra("testMode", true);
    setActivityIntent(activityIntent);
}

2) 在您的活动中,检查 testMode:

Bundle extras = getIntent().getExtras();
if (extras != null && extras.getBoolean("testMode")) {
    // disable your database sync
}

我使用这种方法来进行一些旧代码的测试。但是我要指出,您的生产代码通常不应在任何方面处理测试。 - Pär Nils Amsen

0
你可以试试这个。
if (isRunningTest == null) {
        isRunningTest = false;
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        List<StackTraceElement> list = Arrays.asList(stackTrace);
        for (StackTraceElement element : list) {
            if (element.getClassName().startsWith("androidx.test.runner.MonitoringInstrumentation")) {
                isRunningTest = true;
                break;
            }
        }
    }

-1

这对我有效,因为没有实际设备在运行

public static boolean isUnitTest() {
    return Build.BRAND.startsWith(Build.UNKNOWN) && Build.DEVICE.startsWith(Build.UNKNOWN) && Build.DEVICE.startsWith(Build.UNKNOWN) && Build.PRODUCT.startsWith(Build.UNKNOWN);
}

不确定您是否需要对DEVICE进行两次测试。在我的测试机器上,使用AS 2.3.3和嵌入式JDK,BRAND、DEVICE和PRODUCT都为null,测试变成了:return (Build.BRAND == null || Build.BRAND.startsWith(Build.UNKNOWN)) && (Build.DEVICE == null || Build.DEVICE.startsWith(Build.UNKNOWN)) && (Build.PRODUCT == null || Build.PRODUCT.startsWith(Build.UNKNOWN)); - Timores

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接