8

是否有注释或其他方便的方法来忽略特定 Android SDK 版本的 junit 测试?有没有类似于 Lint 注释 TargetApi(x) 的东西?还是我必须手动检查是否使用 Build.VERSION 运行测试?

4

5 回答 5

11

我不认为有什么准备好,但很容易为此创建一个自定义注释。

创建您的自定义注释

@Target( ElementType.METHOD )
@Retention( RetentionPolicy.RUNTIME)
public @interface TargetApi {
    int value();
}

覆盖测试运行器(这将检查值并最终忽略/触发测试)

public class ConditionalTestRunner extends BlockJUnit4ClassRunner {
    public ConditionalTestRunner(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    public void runChild(FrameworkMethod method, RunNotifier notifier) {
        TargetApi condition = method.getAnnotation(TargetApi.class);
        if(condition.value() > 10) {
            notifier.fireTestIgnored(describeChild(method));
        } else {
            super.runChild(method, notifier);
        }
    }
}

并标记你的测试

@RunWith(ConditionalTestRunner.class)
public class TestClass {

    @Test
    @TargetApi(6)
    public void testMethodThatRunsConditionally() {
        System.out.print("Test me!");
    }
}

刚刚测试,它对我有用。:)

归功于:有条件地忽略 JUnit 测试

于 2013-07-15T12:32:01.107 回答
3

另一种方法是使用 JUnit 的assume功能:

@Test
fun shouldWorkOnNewerDevices() {
   assumeTrue(
       "Can only run on API Level 23 or newer because of reasons",
       Build.VERSION.SDK_INT >= 23
   )
}

如果应用,这有效地将测试方法标记为已跳过。

这不像注释解决方案那么好,但您也不需要自定义 JUnit 测试运行器。

于 2018-12-06T09:01:14.347 回答
2

我一直在寻找这个问题的答案,但没有找到比检查版本更好的方法。TestCase通过检查以下 Android方法,我能够有条件地抑制测试逻辑的执行。但是,这实际上并不能阻止单个测试的执行。像这样覆盖该runTest()方法将导致测试“通过”您知道无法正常工作的 API 级别。根据您的测试逻辑,您可能也需要覆盖tearDown()。也许有人会提供更好的解决方案。

@Override
protected void setUp() throws Exception {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
        if (Log.isLoggable(TAG, Log.INFO)) {
            Log.i(TAG, "This feature is only supported on Android 2.3 and above");
        }
    } else {
        super.setUp();
    }
}

@Override
protected void runTest() throws Throwable {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
        assertTrue(true);
    } else {
        super.runTest();
    }
}
于 2014-08-06T14:43:06.600 回答
0

我认为@mark.w 的答案是阻力最小的路径:

您可能想看看SdkSuppress注释。它有两种方法——maxSdkVersion 和 minSdkVersion,您可以根据需要使用它们。

例如:

@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
public void testMethodThatRunsConditionally() {
    System.out.print("Test me!");
}
于 2022-02-11T05:19:50.253 回答
0

我已经为 Kotlin 现代版本升级了@Enrichman 的答案。所以,这是测试运行器类:

class ConditionalSDKTestRunner(klass: Class<*>?) : BlockJUnit4ClassRunner(klass) {
    override fun runChild(method: FrameworkMethod, notifier: RunNotifier) {
        // Annotation class could not have a base class of interface, so we can not group them.
        val testOnlyForTargetSDKCode = method.getAnnotation(TestOnlyForTargetSDK::class.java)?.sdkLevel?.code
        val testForTargetSDKAndBelowCode = method.getAnnotation(TestForTargetSDKAndBelow::class.java)?.sdkLevel?.code
        val testForTargetSDKAndAboveCode = method.getAnnotation(TestForTargetSDKAndAbove::class.java)?.sdkLevel?.code

        when {
            // If annotation exists, but target SDK is not equal of emulator SDK -> skip this test.
            testOnlyForTargetSDKCode != null && testOnlyForTargetSDKCode != Build.VERSION.SDK_INT ->
                notifier.fireTestIgnored(describeChild(method))
            // If annotation exists, but test SDK is lower than emulator SDK -> skip this test.
            testForTargetSDKAndBelowCode != null && testForTargetSDKAndBelowCode < Build.VERSION.SDK_INT ->
                notifier.fireTestIgnored(describeChild(method))
            // If annotation exists, but test SDK is higher than emulator SDK -> skip this test.
            testForTargetSDKAndAboveCode != null && testForTargetSDKAndAboveCode > Build.VERSION.SDK_INT ->
                notifier.fireTestIgnored(describeChild(method))
            // For other cases test should be started.
            else -> super.runChild(method, notifier)
        }
    }
}

枚举项目中现有的 SDK:

enum class SdkLevel(val code: Int) {
    SDK_24(24),
    SDK_25(25),
    SDK_26(26),
    SDK_27(27),
    SDK_28(28),
    SDK_29(29),
    SDK_30(30),
    SDK_31(31),
    SDK_32(32)
}

和下面的注释:

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class TestForTargetSDKAndAbove(val sdkLevel: SdkLevel)

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class TestForTargetSDKAndBelow(val sdkLevel: SdkLevel)

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class TestOnlyForTargetSDK(val sdkLevel: SdkLevel)

要使用它,只需添加ConditionalSDKTestRunner到您的基本 android 单元测试类:

@RunWith(ConditionalSDKTestRunner::class)
abstract class BaseAndroidUnitTest

和必要的测试注释,使其仅适用于特殊的 sdk:

@Test
@TestForTargetSDKAndAbove(SdkLevel.SDK_31)
fun getConnectionInfo_positive_SDK31() {

@Test
@TestForTargetSDKAndBelow(SdkLevel.SDK_30)
fun getConnectionInfo_negative1_SDK30() {

@Test
@TestOnlyForTargetSDK(SdkLevel.SDK_29)
fun getConnectionInfo_negative1_SDK29() {

就这样。谢谢!

于 2022-01-10T07:18:23.500 回答