添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

忽略安卓单元测试,取决于SDK级别

9 人关注

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

android
junit
Markus K
Markus K
发布于 2013-07-15
5 个回答
Enrichman
Enrichman
发布于 2022-01-10
已采纳
0 人赞同

我不认为有现成的东西,但为这个创建一个自定义注释是很容易的。

创建你的自定义注释

@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!");

Just tested, it works for me. :)

Credits to: 有条件地忽略JUnit测试

看起来是个漂亮的解决方案,但你如何让JUnit 4与安卓系统一起工作?
我不认为没有任何Junit版本会有任何问题,我想你最终必须改变测试运行器。你现在使用的是哪个版本?
我想这是JUnit 3.8。Android测试工具不支持JUnit 4。 developer.android.com/tools/testing/testing_android.html
Now it's supported code.google.com/p/android-test-kit/wiki/... 你可以从支持库中使用它。
当我试图解决一个类似的问题时,这对我来说很有效。不过现在有了@SdkSuppress注解,它也可以工作,而且干净利落,使用起来很简单。请看对我问题的回答 stackoverflow.com/questions/45865658/...
Thomas Keller
Thomas Keller
发布于 2022-01-10
0 人赞同

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

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

如果应用,这将有效地标志着该测试方法被跳过。

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

Roy Clarkson
Roy Clarkson
发布于 2022-01-10
0 人赞同

我一直在寻找这个问题的答案,还没有找到比检查版本更好的方法。我能够通过在以下Android TestCase 方法中设置一个检查来有条件地抑制测试逻辑的执行。然而,这实际上并不能阻止各个测试的执行。像这样覆盖 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();
    
Phileo99
Phileo99
发布于 2022-01-10
0 人赞同

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

你可能想看一下 SdkSuppress 注解。它有两个方法 -- maxSdkVersion和minSdkVersion,你可以根据自己的需要使用。

@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
public void testMethodThatRunsConditionally() {
    System.out.print("Test me!");
    
Georgiy Chebotarev
Georgiy Chebotarev
发布于 2022-01-10
0 人赞同

我升级了@Enrichman的答案,用于Kotlin现代版。所以,这就是测试运行器类。

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)

Enum with exist SDKs in your project:

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

@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() {