Testing Activity Result Contract by Decoupling them from Activity Code

By Published On: March 20, 2023Categories: Development

Hey folks,

I am here with a new article about a better way to write contracts.

In this article, I am going to give you details about how you can test activity results contracts. First thing first, never test contracts directly from the activity, you need to write them in a seprate file and send ActivityResultRegistry as a parameter so you can easily make your own ActivityResultRegistry with the expected result and other details so you can fake this functionality.

If you haven’t read my last article about contracts where I explained the custom contracts to abstract our work and decouple the code. I will highly recommend you to read my last article on custom contracts to get a good understanding of contracts’ thought processes.

Ok so in the last article we wrote a custom contract in a separate file but it was directly being register in activity. In this article, I am going to make an extra layer to abstract my contract logical part to separate my logic from my view.

Your contract file will be like

//Injecting my ActivityResultRegistry here using Hilt
class ImageContractHandler @Inject constructor (registry: ActivityResultRegistry) {

    private val contractUriResult : MutableLiveData  = MutableLiveData(null)

    private val getPermission = registry.register(REGISTRY_KEY, GetContentFileUri()) { uri ->
        contractUriResult.value = uri
    }

    fun getImageFromGallery(): LiveData {
        getPermission.launch("image/*")
        return contractUriResult
    }

    companion object {
        private const val REGISTRY_KEY = "Image Picker"
    }
}

Hilt Module will look like this

@Module
@InstallIn(ActivityComponent::class)
class AppModule {

    @Provides
    fun provideActivityResultRegistry(@ActivityContext activity: Context) =
        (activity as? AppCompatActivity)?.activityResultRegistry
            ?: throw IllegalArgumentException("You must use AppCompatActivity")
}

Contract consumer (MainActivity) will look like this

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var contractHandler : ImageContractHandler

   ..................................................................

   //So we have converted our contract call to single function 

    private fun onGalleryClick() {
        contractHandler.getImageFromGallery().observe(this, {
            it?.let { u ->
                backgroundImageView.setImageURI(u)
                val file = File(u.path)
            }
        })
    }

   ..................................................................
}


Then comes your Android Test where you actually test the contract instead of testing the main activity to call the actual onGalleryClick method we already have a contract in a separate file so we can test it in isolation

@RunWith(AndroidJUnit4::class)
@LargeTest
class MainActivityTest {

    @get:Rule
    var instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    var activityScenarioRule = ActivityScenarioRule(
        MainActivity::class.java
    )

  .....................................................................................

    @Test
    fun activityResultTest() {

        // Create an expected result URI
        val testUrl = "file//dummy_file.test"
        val expectedResult = Uri.parse(testUrl)

        // Create the test ActivityResultRegistry
        val testRegistry = object : ActivityResultRegistry() {
            override fun <I, O> onLaunch(
                requestCode: Int,
                contract: ActivityResultContract<I, O>,
                input: I,
                options: ActivityOptionsCompat?
            ) {
                dispatchResult(requestCode, expectedResult)
            }
        }

        val uri = ImageContractHandler(testRegistry).getImageFromGallery().getOrAwaitValue()
        assert(uri == expectedResult)
    }
    
  .....................................................................................
  
}

I’ve attached complete code for this article where you can find the detailed implementation of this concept for getting images from the gallery and then testing that functionality.

I’ve attached the Github code link so you can see the complete working example. This repository is used for both contracts articles and you can find code for the current article in decouple_test_hilt named branch

GitHub – UmairKhalid786/ImagePickerWithContentProvider at decouple_test_hilt
Contribute to UmairKhalid786/ImagePickerWithContentProvider development by creating an account on GitHub.

github.com

I hope you enjoyed this topic where I’ve explained a better way to write contracts so you can test it and then there is HILT helping in this cause as well.
If you enjoy this article, please subscribe and clap for me 😉 if you don’t then please write some feedback so I can improve in the given area.

Bye Bye

Share this article

Written by : admin

Leave A Comment

Latest Articles