Search code examples
android-jetpack-composecoil

Jetpack Compose - UI Test Coil Fetched Image


I have a custom Image composable that uses Coil's rememberAsyncImagePainter.

However, I have another component that uses resources and has logic that is handled separately.

I successfully render the custom resource placeholder, however I'm not sure how I can write a test to check that the actual url image is loaded & visible.

Both the url image and the resource image have different testTags, however in the test, the node with the url image's tag never exists.

Does Coil have any solution to mock the ImageRequest.Builder so that I can guarantee that the URL image successfully loads?

I would prefer to not add any test-related code to the component itself, but if that's the only way, then I would prefer the component to be testable.


Solution

  • According to the official docs https://coil-kt.github.io/coil/image_loaders/#testing, you can create a FakeImageLoader class like this:

    class FakeImageLoader(private val context: Context) : ImageLoader {
    
        override val defaults = DefaultRequestOptions()
        override val components = ComponentRegistry()
        override val memoryCache: MemoryCache? get() = null
        override val diskCache: DiskCache? get() = null
    
        override fun enqueue(request: ImageRequest): Disposable {
            // Always call onStart before onSuccess.
            request.target?.onStart(request.placeholder)
            val result = ColorDrawable(Color.BLACK)
            request.target?.onSuccess(result)
            return object : Disposable {
                override val job = CompletableDeferred(newResult(request, result))
                override val isDisposed get() = true
                override fun dispose() {}
            }
        }
    
        override suspend fun execute(request: ImageRequest): ImageResult {
            return newResult(request, ColorDrawable(Color.BLACK))
        }
    
        private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult {
            return SuccessResult(
                drawable = drawable,
                request = request,
                dataSource = DataSource.MEMORY_CACHE
            )
        }
    
        override fun newBuilder() = throw UnsupportedOperationException()
    
        override fun shutdown() {}
    }
    

    And you UI test, you can write

    @Test
    fun testCustomImageComposable() {
        Coil.setImageLoader(FakeCoilImageLoader())
    
        setContent {
            CutomImageComposable(...)
        }
        
        // ... assert image is displayed, etc.
    }
    

    This should guarantee the image to be shown every-time the test is executed.

    Alternatively, you can mock ImageLoader and mimic the behavior above.

    However, I would not recommend mocking ImageLoader, as we do not know whether rememberAsyncImagePainter uses all of the methods, but if you must mock it, it would be worth a try.