Closed jlengrand closed 1 year ago
BTW, I made a minimally reproduceable example over here : https://github.com/jlengrand/supabase-mock-demo-kotlin.
No need for any db or db credentials to run it
My current method is to wrap your library in a class and mock that class instead, but it looks more like a workaround than a solution
Hey! Sadly, I currently don't have a good solution for that problem. The only two solutions I can think of is your current approach and using a mocked http engine for ktor, which you can provide using
val client = createSupabaseClient(
supabaseUrl,
supabaseKey,
) {
httpEngine = MockEngine { request -> //this will get executed everytime a ktor gets used by any function
respond(Json.encodeToString(listOf<ResultPerson>()))
}
}
But both obviously aren't ideal, and I'm not sure what I could do to make this easier.
My current method is to wrap your library in a class and mock that class instead, but it looks more like a workaround than a solution
Based on the question. Could you please try providing the Posgrest instance instead of the client
to where you want to call the API? It would be easier to mock the dependency when write test
Coming back here just to mentioned that I decided to go another direction, which I'm pretty satisfied with actually. I created a simple Docker Compose setup that I run with test containers and which simulate a simple Supabase instance. No need for mocking any more. It's more "integration tests" than "unit tests" at that point though.
The full blog about it, but in short :
version: '3'
# Thanks https://github.com/mattddowney/compose-postgrest/blob/master/README.md
services:
################
# postgrest-db #
################
postgrest-db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- DB_SCHEMA=${DB_SCHEMA}
volumes:
- "./initdb:/docker-entrypoint-initdb.d"
networks:
- postgrest-backend
restart: always
#############
# postgrest #
#############
postgrest:
image: postgrest/postgrest:latest
ports:
- "3000:3000"
environment:
- PGRST_DB_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgrest-db:5432/${POSTGRES_DB}
- PGRST_DB_SCHEMA=${DB_SCHEMA}
- PGRST_DB_ANON_ROLE=${DB_ANON_ROLE}
- PGRST_JWT_SECRET=${PGRST_JWT_SECRET}
networks:
- postgrest-backend
restart: always
#############
# Nginx #
#############
nginx:
image: nginx:alpine
restart: always
tty: true
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
- "443:443"
networks:
- postgrest-backend
networks:
postgrest-backend:
driver: bridge
and the test file :
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.createSupabaseClient
import io.github.jan.supabase.postgrest.Postgrest
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.testcontainers.containers.ComposeContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import java.io.File
@Testcontainers
class MainKtTestTestContainers {
// The jwt token is calculated manually (https://jwt.io/) based on the private key in the docker-compose.yml file, and a payload of {"role":"postgres"} to match the user in the database
private val jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXMifQ.88jCdmcEuy2McbdwKPmuazNRD-dyD65WYeKIONDXlxg"
private lateinit var supabaseClient: SupabaseClient
@Container
var environment: ComposeContainer =
ComposeContainer(File("src/test/resources/docker-compose.yml"))
.withExposedService("postgrest-db", 5432)
.withExposedService("postgrest", 3000)
.withExposedService("nginx", 80)
@BeforeEach
fun setUp() {
val fakeSupabaseUrl = environment.getServiceHost("nginx", 80) +
":" + environment.getServicePort("nginx", 80)
supabaseClient = createSupabaseClient(
supabaseUrl = "http://$fakeSupabaseUrl",
supabaseKey = jwtToken
) {
install(Postgrest)
}
}
@Test
fun testEmptyPersonTable(){
runBlocking {
val result = getPerson(supabaseClient)
assertEquals(0, result.size)
}
}
@Test
fun testSavePersonAndRetrieve(){
val randomPersons = listOf(Person("Jan", 30), Person("Jane", 42))
runBlocking {
val result = savePerson(randomPersons, supabaseClient)
assertEquals(2, result.size)
assertEquals(randomPersons, result.map { it.toPerson() })
val fetchResult = getPerson(supabaseClient)
assertEquals(2, fetchResult.size)
assertEquals(randomPersons, fetchResult.map { it.toPerson() })
}
}
}
General info
What is your question?
Hey there!
See this question for context.
I'm using the library and I want to unit test my code, reason for which I am trying to mock the SupabaseClient.
Thing is, because functions in PostgrestBuilder.kt are inlined they cannot be mocked by definitions, which makes it hard to unit test.
On top of this, because Supabase does not seem to offer test containers, I cannot turn myself to integration tests either.
Do you have any recommendations as to what to do?
Thanks!