Supabase Test Utilities
supabase-test is a specialized library designed to facilitate robust integration testing for Supabase projects by providing isolated, ephemeral PostgreSQL environments. It leverages Docker to spin up fresh, role-aware database instances for each test or test suite, ensuring complete data isolation and preventing test interference. As of its current stable version, 2.9.3, the library is actively maintained, receiving iterative updates for compatibility with the latest Supabase and PostgreSQL versions, and addressing new features or performance enhancements. Its key differentiators include built-in support for Row-Level Security (RLS) testing, allowing developers to rigorously validate their security policies, and a rollback-friendly architecture that simplifies test teardown, making it an essential tool for comprehensive Supabase backend testing.
Common errors
-
Error: connect ECONNREFUSED 127.0.0.1:5432
cause The PostgreSQL container failed to start or is not accessible, often due to Docker not running, port conflicts, or network issues.fixVerify that Docker Desktop (or the Docker daemon) is running. Check if port 5432 is already in use by another application. Review Docker logs for errors related to the PostgreSQL container startup. -
Failed to apply migrations: [SQLSTATE 42P01] relation "my_table" does not exist
cause The specified `migrationsPath` either does not contain valid migration files, or the migration files themselves have errors (e.g., trying to reference a table before it's created, or syntax errors).fixDouble-check that the `migrationsPath` option in `withTestDatabase` points to the correct directory containing your Supabase SQL migration files. Manually verify the SQL syntax and order of your migration scripts. -
Postgrest: RLS policy prohibits insertion on table "profiles"
cause The Row-Level Security (RLS) policy for the `profiles` table does not permit the current authenticated role (or lack thereof) to perform an INSERT operation.fixExamine the RLS policies defined in your migrations for the `profiles` table. Ensure there is an `INSERT` policy that grants the necessary permissions to the roles you are testing with, especially for authenticated users. Make sure the policy's `WITH CHECK` clause allows the insert.
Warnings
- breaking Major versions may introduce breaking changes to the `withTestDatabase` signature or options. Always consult the migration guide when upgrading to a new major version to ensure compatibility with your test setup.
- gotcha This library relies on Docker to spin up PostgreSQL containers. If Docker Desktop or the Docker daemon is not running, tests will fail with connection errors or container startup failures.
- gotcha Running tests with `withTestDatabase` can be significantly slower than unit tests because each test (or suite, depending on configuration) may involve spinning up a new PostgreSQL instance, applying migrations, and seeding data. This overhead adds up quickly.
- gotcha Incorrect or incomplete Row-Level Security (RLS) policies in your migrations can lead to unexpected test failures, where authenticated users are denied access or gain unauthorized access. `supabase-test` accurately reflects these policies.
Install
-
npm install supabase-test -
yarn add supabase-test -
pnpm add supabase-test
Imports
- withTestDatabase
const { withTestDatabase } = require('supabase-test');import { withTestDatabase } from 'supabase-test'; - setupDatabase
const { setupDatabase } = require('supabase-test');import { setupDatabase } from 'supabase-test'; - TestConnection
import type { TestConnection } from 'supabase-test';
Quickstart
import { withTestDatabase } from 'supabase-test';
import { createClient, SupabaseClient } from '@supabase/supabase-js';
// Ensure you have a 'supabase/migrations' directory with your schema migrations
// and a 'test_items' table for this example to fully run.
describe('Supabase integration tests with RLS', () => {
let supabase: SupabaseClient;
// Use withTestDatabase to get an isolated client for each test or suite
withTestDatabase({
migrationsPath: './supabase/migrations', // Path to your Supabase schema migrations
seedScript: './scripts/seed.ts', // Optional: run a seed script after migrations
connect: true, // Automatically provides a Supabase client configured for the test DB
})(({ client, connectionString }) => {
supabase = client; // Assign the test-specific Supabase client
beforeAll(() => {
console.log(`Using test database connection: ${connectionString.split('@')[1]}`);
});
test('should allow an authenticated user to insert and retrieve their own data', async () => {
const userEmail = `testuser-${Date.now()}@example.com`;
const userPassword = 'securepassword';
// 1. Sign up a new user
const { data: signUpData, error: signUpError } = await supabase.auth.signUp({
email: userEmail,
password: userPassword,
});
expect(signUpError).toBeNull();
expect(signUpData.user).toBeDefined();
const userId = signUpData.user?.id;
// 2. Insert data belonging to this user
const { data: insertData, error: insertError } = await supabase
.from('test_items')
.insert({ name: 'User Specific Item', user_id: userId })
.select();
expect(insertError).toBeNull();
expect(insertData).toHaveLength(1);
expect(insertData![0].user_id).toEqual(userId);
// 3. Retrieve data, should only get their own item due to RLS
const { data: fetchedData, error: fetchError } = await supabase
.from('test_items')
.select('*');
expect(fetchError).toBeNull();
expect(fetchedData).toHaveLength(1);
expect(fetchedData![0].name).toBe('User Specific Item');
expect(fetchedData![0].user_id).toEqual(userId);
});
test('should prevent unauthenticated access to protected data (RLS)', async () => {
// Attempt to access data without authentication
const unauthenticatedClient = createClient(connectionString, 'anon_key_for_test_db');
const { data, error } = await unauthenticatedClient.from('test_items').select('*');
expect(data).toHaveLength(0); // Should return no data due to RLS
expect(error).toBeDefined();
expect(error?.message).toContain('Row-level security policy');
});
});
});