Testing apps that use Refine

Copy Markdown

Refine keeps facet data up to date using database triggers. Because database triggers behave differently in the Ecto test sandbox, some adjustments to your test setup are required.

Use the sandbox in :auto mode

Most Ecto test suites run the SQL sandbox in :manual mode, where each test runs inside a transaction that is rolled back when the test finishes. That gives you clean isolation, but it doesn't work for testing Refine's incremental updates.

The reason is that Refine's triggers and Refine.merge_deltas/2 need to see committed data. Inside a rolled-back transaction, the data your test writes is never committed, so the triggers don't behave the way they would in production, and your assertions won't match.

So for tests that exercise facet updates, switch the sandbox to :auto mode:

Ecto.Adapters.SQL.Sandbox.mode(YourApp.Repo, :auto)

This way your writes are committed, and the triggers function similar to production.

Clean up between tests

The trade-off with :auto mode is that nothing is rolled back, so anything a test creates sticks around. For Refine, that includes the triggers it installs on your tables. If one test creates a facets table and the next runs without clearing it, those leftover triggers can fire during the second test and quietly throw off its results - and because it depends on test order, the failures are intermittent and hard to track down.

Clean up artifacts

Running a reset in a setup block guarantees that every test starts clean, regardless of how the previous one ended - including tests that failed partway through.

You can drop every facets table you know your tests create, calling drop_facets_table/2 for each:

setup do
  for config <- my_test_configs() do
    Refine.drop_facets_table(config, repo: Repo)
  end

  :ok
end

If your tests create many facets tables, or you'd rather not maintain a list, you can instead discover and drop Refine's artifacts using an included test helper function Refine.TestHelpers.reset_facets_artifacts/1 that cleans up anything any test created:

setup do
  Refine.TestHelpers.reset_facets_artifacts(repo: Repo) 
  :ok
end

Clean up data

Refine.TestHelpers.reset_facets_artifacts/1 removes the tables and triggers Refine created - but it deliberately leaves your own data tables alone.

Under :auto mode, every change a test makes is committed, so you also need to reset your data between tests, or changes from one test (a deleted row, an inserted record) will carry into the next.

Reset your data with TRUNCATE ... RESTART IDENTITY CASCADE to reset the auto-incrementing identity sequence:

setup do
  Refine.TestHelpers.reset_facets_artifacts(repo: Repo)

  Repo.query!("""
    TRUNCATE articles, categories, article_categories
    RESTART IDENTITY CASCADE
  """)
  
  # ... insert fresh fixtures
  
  :ok
end