使用 kotlin 进行 Espresso webview 测试

Espresso webview test with kotlin

我正在尝试将 Espresso 示例从 Java 移植到 Kotlin,我收到 WebView version

的错误

我按原样使用 MainActivity.kt 这个名字,它很简单而且很好用:

class MainActivity : AppCompatActivity() {
    @VisibleForTesting
    val keyUrl2Load = "KEY_URL_TO_LOAD"

    @VisibleForTesting
    val webFormUrl = "file:///android_asset/web_form.html"

    private var mWebView: WebView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mWebView = findViewById<View>(R.id.web_view) as WebView
        true.also { mWebView!!.settings.javaScriptEnabled = it }
        urlFromIntent(intent)?.let { mWebView!!.loadUrl(it) }
        mWebView!!.requestFocus()
        mWebView!!.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
                return false
            }
        }
    }

    private fun urlFromIntent(intent: Intent): String? {
        val url = intent.getStringExtra(keyUrl2Load)
        return if (!TextUtils.isEmpty(url)) url else webFormUrl
    }
}

然而,MainActivityTest.kt 看起来不太容易移植,因为:

package com.example.espressowebview101

import android.content.Intent
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.web.assertion.WebViewAssertions
import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.webdriver.DriverAtoms
import androidx.test.espresso.web.webdriver.Locator
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.Matchers
import org.junit.After
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

//https://github.com/android/testing-samples/tree/main/ui/espresso/WebBasicSample/app/src
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    private val MACCHIATO = "Macchiato"
    private val DOPPIO = "Doppio"
    lateinit var scenario: ActivityScenario<MainActivity>


    @get:Rule
    var mActivityRule = activityScenarioRule<MainActivity>()

    @After
    fun enableJS() {
        Web.onWebView().forceJavascriptEnabled()
    }

    @Test
    fun typeTextInInput_clickButton_SubmitsForm() {
        val intent = withWebFormIntent()
        scenario = ActivityScenario.launch(intent)

        // Selects the WebView in your layout. If you have multiple WebViews you can also use a
        // matcher to select a given WebView, onWebView(withId(R.id.web_view)).
        Web.onWebView() // Find the input element by ID
            .withElement(DriverAtoms.findElement(Locator.ID, "text_input")) // Clear previous input
            .perform(DriverAtoms.clearElement()) // Enter text into the input element
            .perform(DriverAtoms.webKeys(MACCHIATO)) // Find the submit button
            .withElement(
                DriverAtoms.findElement(
                    Locator.ID,
                    "submitBtn"
                )
            ) // Simulate a click via javascript
            .perform(DriverAtoms.webClick()) // Find the response element by ID
            .withElement(
                DriverAtoms.findElement(
                    Locator.ID,
                    "response"
                )
            ) // Verify that the response page contains the entered text
            .check(
                WebViewAssertions.webMatches(
                    DriverAtoms.getText(),
                    Matchers.containsString(MACCHIATO)
                )
            )
    }

    private fun withWebFormIntent(): Intent {
        return Intent().also {
            val keyUrl2Load = "KEY_URL_TO_LOAD"
            val webFormUrl = "file:///android_asset/web_form.html"
            it.putExtra(keyUrl2Load, webFormUrl)
        }
    }
}

我得到的错误是:

Connected to process 8963 on device 'google-pixel_3_xl-8AJY0LJFQ'.

java.lang.RuntimeException: Unable to resolve activity for: Intent { (has extras) }
    at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:387)
    at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:416)
    at androidx.test.core.app.ActivityScenario.launchInternal(ActivityScenario.java:265)
    at androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:226)
    at com.example.espressowebview101.MainActivityTest.typeTextInInput_clickButton_SubmitsForm(MainActivityTest.kt:36)
    at java.lang.reflect.Method.invoke(Native Method)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
    at org.junit.rules.ExternalResource.evaluate(ExternalResource.java:48)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=12=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:154)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=12=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
    at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
    at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)

您正在尝试启动与 MainActivity 无关的 Intent。它不知道它应该启动这个 activity.

你的 withWebFormIntent 应该 return 一个 Intent 告诉它启动 MainActivity.

例如

private fun withWebFormIntent(context: Context): Intent {
    return Intent(context, MainActivity::class.java).also {
        val keyUrl2Load = "KEY_URL_TO_LOAD"
        val webFormUrl = "file:///android_asset/web_form.html"
        it.putExtra(keyUrl2Load, webFormUrl)
    }
}

有关如何为 activity 构造意图的更多文档,请参阅 this