Supabase Test Utilities

2.9.3 · active · verified Tue Apr 21

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

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to use `withTestDatabase` to create an isolated Supabase environment for integration tests, including user authentication, RLS-aware data manipulation, and preventing unauthorized access.

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');
    });
  });
});

view raw JSON →