kotlinunit-testingsupabasesupabase-database

Mocking the Kotlin SupabaseClient for unit testing


TLDR : Here is a minimally reproduceable example. You do not need a Supabase account / database to get the error.

I have some code like this, which performs some simple transformation of data before saving it in Supabase

    suspend fun saveClones(owner: String, repository: String, clones: GitHubClones): List<ResultClone> {
        val zeClones = clones.clones.map {
            Clone(
                owner = owner,
                repository = repository,
                count = it.count,
                uniques = it.uniques,
                timestamp = it.timestamp
            )
        }

        return client.postgrest["clone"].insert(zeClones, upsert = true, onConflict = "owner, repository, timestamp").decodeList<ResultClone>()
    }

I'd like to check that the data going in the insert call is what I expect it to be. I've tried to mock the client using mockk as such

val supabaseClient = mockk<SupabaseClient>()
val postgrest = mockk<Postgrest>()
val postgrestBuilder = mockk<PostgrestBuilder>()
val postgrestResult = PostgrestResult(body = null, headers = Headers.Empty)

every { supabaseClient.postgrest } returns postgrest
every { postgrest["path"] } returns postgrestBuilder
coEvery { postgrestBuilder.insert(values = any<List<Path>>()) } returns postgrestResult

but when running my test, I am facing the following error :

java.lang.IllegalStateException: Plugin rest not installed or not of type Postgrest. Consider installing Postgrest within your supabase client builder
    at io.github.jan.supabase.postgrest.PostgrestKt.getPostgrest(Postgrest.kt:172)
    at nl.lengrand.GitHubTrafficStoreTest$setUp$1.invoke(GitHubTrafficStoreTest.kt:53)
    at nl.lengrand.GitHubTrafficStoreTest$setUp$1.invoke(GitHubTrafficStoreTest.kt:53)

What would be the correct way to unit test that function? It looks like I also cannot really use test containers with Supabase, making integration tests equally as difficult.


Solution

  • I think, that extension properties can't be mocked like that.

    My interpretation of extension property is, that it is in byte code present as static getter (basically same principe how extension functions are implemented), which 1st arugment is instance of "extended" type. And in Kotlin is such getter visible as property. That is why mockStatic should do the job.

    When I modify code

    @BeforeTest
    fun setUp() {
      mockkStatic(SupabaseClient::postgrest)
      every { supabaseClient.postgrest } returns postgrest
    }
    
    @AfterTest
    fun tearDown() {
      unmockkStatic(SupabaseClient::postgrest)
    }
    
    

    I got new error

    kotlinx.serialization.SerializationException: Serializer for class 'Person' is not found.
    Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
    
        at kotlinx.serialization.internal.Platform_commonKt.serializerNotRegistered(Platform.common.kt:92)
    

    so I would say, that this approach would work.