Search code examples
androidunit-testingkotlinandroid-webview

Unit testing the onPageStarted and onPageFinished of a WebView's WebViewClient callbacks


Android Studio 3.5.1
Kotlin 1.3

I have the following method that I am trying to unit test. That uses the WebView and WebViewClient

The method I have is the following that needs to be unit tested:

fun setPageStatus(webView: WebView?, pageStatus: (PageStatusResult) -> Unit) {
    webView?.webViewClient = object : WebViewClient() {

        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            pageStatus(PageStatusResult.PageStarted(url ?: "", favicon))
        }

        override fun onPageFinished(view: WebView?, url: String?) {
            pageStatus(PageStatusResult.PageFinished(url ?: ""))
        }
    }
}

I takes a webView that overrides some callback from the WebViewClient. And then calls a lambda function in the onPageStarted or onPageFinished.

Uses a sealed class to set the properties that is passed in the lambda method

sealed class PageStatusResult {
    data class PageFinished(val url: String) : PageStatusResult()
    data class PageStarted(val url: String, val favicon: Bitmap?) : PageStatusResult()
}

In the unit test I have do something like this:

@Test
fun `should set the correct settings of the WebView`() {
    // Arrange the webView
    val webView = WebView(RuntimeEnvironment.application.baseContext)

    // Act by calling the setPageStatus
    webFragment.setPageStatus(webView) { pageStatusResult ->
        when(pageStatusResult) {
            is PageStarted -> {
            // Assert that the url is correct
                assertThat(pageStatusResult.url).isEqualToIgnoringCase("http://google.com")
            }
        }
    }

    // Call the onPageStarted on the webViewClient and and assert in the when statement
    webView.webViewClient.onPageStarted(webView, "http://google.com", null)
}

Solution

  • As the nature of this unit test is async, instead of calling the webView.webViewClient.onPageStarted synchronously yourself, you should use an async testing approach. In this way, we pass a URL to the WebView to show, then wait until the onPageStarted method is called by the WebView itself.

    It seems that the best option to run async unit tests in Android is using the Awaitility.

    build.gradle

    dependencies {
        testImplementation 'org.awaitility:awaitility:4.0.1'
    }
    

    Unit Test Class

    @Test
    fun `should set the correct settings of the WebView`() {
    
        val requestedUrl = "https://www.google.com"
        var resultUrl: String? = null
    
        // Arrange the webView
        val webView = WebView(RuntimeEnvironment.application.baseContext)
    
        // Act by calling the setPageStatus
        webFragment.setPageStatus(webView) { pageStatusResult ->
            when (pageStatusResult) {
                is PageStatusResult.PageStarted -> {
                    resultUrl = pageStatusResult.url
                }
            }
        }
    
        // trying to load the "requestedUrl"
        webView.loadUrl(requestedUrl)
    
        // waiting until the "onPageStarted" is called
        await().until { resultUrl != null }
    
        // now check the equality of URLs
        assertThat(resultUrl).isEqualToIgnoringCase(requestedUrl)
    }