Kotlin和新的ActivityTestRule:@Rule必须是公共的

318

我正在尝试使用Kotlin为我的Android应用创建UI测试。由于新系统使用了ActivityTestRule,我无法使其正常工作:它可以正确编译,但在运行时,我遇到以下错误:

java.lang.Exception: The @Rule 'mActivityRule' must be public.
    at org.junit.internal.runners.rules.RuleFieldValidator.addError(RuleFieldValidator.java:90)
    at org.junit.internal.runners.rules.RuleFieldValidator.validatePublic(RuleFieldValidator.java:67)
    at org.junit.internal.runners.rules.RuleFieldValidator.validateField(RuleFieldValidator.java:55)
    at org.junit.internal.runners.rules.RuleFieldValidator.validate(RuleFieldValidator.java:50)
    at org.junit.runners.BlockJUnit4ClassRunner.validateFields(BlockJUnit4ClassRunner.java:170)
    at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.ParentRunner.validate(ParentRunner.java:344)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:74)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:55)
    at android.support.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:38)
    at android.support.test.runner.AndroidJUnit4.<init>(AndroidJUnit4.java:36)
    at java.lang.reflect.Constructor.constructNative(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at android.support.test.internal.runner.junit4.AndroidAnnotatedBuilder.buildAndroidRunner(AndroidAnnotatedBuilder.java:57)
    at android.support.test.internal.runner.junit4.AndroidAnnotatedBuilder.runnerForClass(AndroidAnnotatedBuilder.java:45)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)
    at org.junit.runner.Computer.getRunner(Computer.java:38)
    at org.junit.runner.Computer$1.runnerForClass(Computer.java:29)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
    at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:98)
    at org.junit.runners.model.RunnerBuilder.runners(RunnerBuilder.java:84)
    at org.junit.runners.Suite.<init>(Suite.java:79)
    at org.junit.runner.Computer.getSuite(Computer.java:26)
    at android.support.test.internal.runner.TestRequestBuilder.classes(TestRequestBuilder.java:691)
    at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:654)
    at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:329)
    at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:226)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)

以下是我如何声明mActivityRule:

RunWith(javaClass<AndroidJUnit4>())
LargeTest
public class RadisTest {

    Rule
    public val mActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule(javaClass<MainActivity>())

   ...
}

已经公开了 :/


4
目前,Kotlin不支持将作为后备属性的字段设置为public,但我们正在努力解决这个问题。 - Andrey Breslav
这个问题有被追踪吗? - Geob-o-matic
3
好的,以下是您需要翻译的内容:链接:https://youtrack.jetbrains.com/issue/KT-3441 - Andrey Breslav
6个回答

393

JUnit允许通过测试类字段或getter方法提供规则。

然而,在Kotlin中,您注释的是一个属性,JUnit不会识别它。

以下是在Kotlin中指定JUnit规则的可能方式:

通过注释的getter方法

从M13开始,注解处理器支持注解目标。 当您编写

@Rule
public val mActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule(javaClass<MainActivity>())

尽管如此,默认情况下注释将使用property目标(对Java不可见)。您可以对属性getter进行注释,这也是公共的,因此满足JUnit对规则getter的要求。
@get:Rule
public val mActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule(javaClass<MainActivity>())

或者,您可以使用函数定义规则,而不是属性(手动实现与@get:Rule相同的结果)。

通过注释的公共字段

Kotlin还允许从候选beta版开始将属性确定性地编译为JVM上的字段,在这种情况下,注释和修饰符适用于生成的字段。这是使用Kotlin的@JvmField属性注释完成的,由@jkschneider回答。


注意:一定要在Rule注释前加上@字符,因为它现在是注释的唯一支持语法,避免使用@publicField,因为它即将被删除


它有用吗?我仍然收到错误,而且它是公共的,使用M14(0.14.449)。 - Geob-o-matic
似乎需要使用@publicField,但它已经过时了,所以我尝试IDE建议的lateinit,但是它向我抛出异常,说lateinit只能应用于可变对象(但博客文章则相反...)。 - Geob-o-matic
1
已更新我的回答 ;) 至于 lateinit val,自 M14 版本以来已被删除,因为它没有完全强制执行字段的不可变性,请参见M14版本发布博客文章 - desseim
什么是@get:注释?它在我的情况下也不起作用。 - aleksandrbel
将 Rule 更改为 @get: Rule 后,问题得到解决。谢谢。 - thegirlincode

270

通过Kotlin 1.0.0+,这是可行的:

@Rule @JvmField 
val mActivityRule = ActivityTestRule(MainActivity::class.java)

1
它解决了 lint 工具抱怨规则上的 public 修饰符是多余的问题。 - Rob
是的,使用 @JvmField 很重要,或者更好地使用 @get:Rule。 - Michał Ziobro
仅适用于最终字段。对于像 var wireMockRule = WireMockRule(wireMockConfig().dynamicPort()) 这样的规则不起作用。 - Abhijit Sarkar

28

使用这个:

@get:Rule
var mActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)

4
需要翻译的内容:Care to add an explanation?可以添加一些解释吗? - Peter Chaula

23

你应该使用@get:Rule而不是@Rule


12

使用 Kotlin 1.0.4 版本:

val mActivityRule: ...
    @Rule get

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反地,请提供不需要询问者澄清的答案。- 来自审阅 - Jignesh Ansodariya
10
@JigneshAnsodariya - 我已经检查过了,这个答案提供了对所问问题的回答。关于这是如何工作的更多信息可以在以下链接中找到 - https://kotlinlang.org/docs/reference/properties.html#getters-and-setters - Praveer Gupta
这种方法存在问题。如果您想访问测试活动(例如:assertTrue(activityRule.activity.isFinishing)),则会调用属性getter,从而创建一个新规则。在这种情况下,activityRule.activity将为null。 - makovkastar

0
我同意@marci的观点,拿这个例子来阐明这个概念:
@get:Rule val tempFolder = TemporaryFolder() //apply @Rule annotation to property getter

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