Skip to main content

sqflite-common-test-suite

Use when running the shared sqflite conformance test suite against a DatabaseFactory implementation (ffi, ffi async, web, the native plugin or a custom one) with sqflite_common_test: SqfliteTestContext, SqfliteLocalTestContext, SqfliteTestContextMixin, SqfliteLocalTestContextMixin, all_test.dart run/sqfliteTestGroup, the individual suites (raw_test, batch_test, open_test, transaction_test, type_test, exception_test, doc_test, wal_test walTests, sqflite_protocol_test), the capability flags supportsUri, supportsDeadLock, supportsWithoutRowId, supportsConcurrentRead, supportsMultipleInstances, strict, isPlugin, and databaseFactoryMock.

Source facts

Repository
tekartik/sqflite
Last source activity
September 20, 2026 at 20:45
Detected SKILL.md language
English
Stars
3,020
Forks
556

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
sqflite-common-test-suite
description
Use when running the shared sqflite conformance test suite against a DatabaseFactory implementation (ffi, ffi async, web, the native plugin or a custom one) with sqflite_common_test: SqfliteTestContext, SqfliteLocalTestContext, SqfliteTestContextMixin, SqfliteLocalTestContextMixin, all_test.dart run/sqfliteTestGroup, the individual suites (raw_test, batch_test, open_test, transaction_test, type_test, exception_test, doc_test, wal_test walTests, sqflite_protocol_test), the capability flags supportsUri, supportsDeadLock, supportsWithoutRowId, supportsConcurrentRead, supportsMultipleInstances, strict, isPlugin, and databaseFactoryMock.
# Shared sqflite test suite (sqflite_common_test) `sqflite_common_test` is the conformance suite every sqflite implementation is validated with: give it a `SqfliteTestContext` wrapping a `DatabaseFactory` and it defines hundreds of `test()`s (open/upgrade, raw SQL, batch, transactions, types, exceptions, WAL, protocol) in the calling test file. ## Guidelines * Dependency (not on pub.dev, `publish_to: none`), in `dev_dependencies`: ```yaml dev_dependencies: sqflite_common_test: git: url: https://github.com/tekartik/sqflite path: sqflite_common_test version: '>=0.3.0' ``` It pulls `sqflite_common`, `sqflite_common_ffi`, `test`, `path` and `synchronized`. * Two imports are enough for the common case: `package:sqflite_common_test/sqflite_test.dart` (the context types) and `package:sqflite_common_test/all_test.dart` (the whole suite). Import `all_test.dart` with a prefix (`as all`): every suite library exports a top-level `run`. * Entry points of `all_test.dart`: `run(SqfliteTestContext context)` and `sqfliteTestGroup(SqfliteTestContext context)` are the same thing; call it from `main()`, optionally inside your own `group('ffi', ...)`. * Build the context with `SqfliteLocalTestContext(databaseFactory: ...)` — a file-based context (`dart:io`) that creates/deletes directories under the factory's databases path. Subclass it to flip the capability flags of the implementation under test. * Capability flags (all default to the conservative value in `SqfliteTestContextMixin`, override only what the implementation supports): * `supportsUri` (`false`): `file:` uri paths, true for ffi. * `supportsWithoutRowId` (`false`): `CREATE TABLE ... WITHOUT ROWID`. * `supportsDeadLock` (`false`): enables the multi-instance dead lock tests. * `supportsConcurrentRead` (`false`): only `sqflite_common_ffi_async` sets it. * `supportsMultipleInstances` (`!isWeb`): `singleInstance: false`. * `supportsRecoveredInTransaction` (`false`): native android/ios/macos only. * `strict` (`true`): the implementation rejects loosely typed queries. * `isPlugin` (`false`): true only for the native `sqflite` plugin factory. `isWeb`, `isAndroid`, `isIOS`, `isMacOS`, `isLinux`, `isWindows` come from the mixins; do not override them. * Always call the implementation initializer before `run()` (for ffi: `sqfliteFfiInit()` from `package:sqflite_common_ffi/sqflite_ffi.dart`). * Two test files running the suite in the same package run in parallel and share the databases path. Give each one its own directory with `await factory.setDatabasesPath('${await factory.getDatabasesPath()}_suffix')` in an `async` `main()` before `run()`. * Add `@TestOn('vm')` (before `library;`) to a suite file using a VM-only factory, `@TestOn('browser')` for a web factory, in a package that also runs tests on the other platform. * Individual suites, when the whole suite is too much or one area fails: import `package:sqflite_common_test/<name>.dart` and call its `run(context)` — `raw_test`, `batch_test` (`run(context, noManualTransactionTest: true)`), `open_test`, `open_flutter_test`, `transaction_test`, `type_test`, `exception_test`, `exp_test` (`noMultipleStatement: true`), `doc_test` (`noLoggerTest: true`), `iterate_test`, `slow_test`, `statement_test`, `sql_command_test`, `database_factory_test`, `service_impl_test`, `issue_test`. `wal_test.dart` exports `walTests(context)` (not `run`) and `sqflite_protocol_test.dart` exports `run(SqfliteTestContext?)`, which accepts `null` and then checks the invoke-method protocol against an internal mock factory, no real database needed. * Context helpers usable in your own tests: `await context.initDeleteDb('x.db')` returns a deleted absolute path ready to open, `createDirectory(null)` gives the databases path, `deleteDirectory(path)`, `writeFile(path, bytes)`, `isInMemoryPath(path)`, `pathContext` (a `package:path` `Context`). * `package:sqflite_common_test/database_factory_mock.dart` gives `DatabaseFactoryMock` / `databaseFactoryMock`: every method throws `UnimplementedError`. Use it to satisfy a `DatabaseFactory` parameter that the code under test must not call, never to fake results. * Anti-patterns: calling `run(context)` inside `test()` or `setUp()` (it declares tests, so it must run at `main()` level); sharing one databases path between suite runs; overriding a `supports*` flag to `true` to make a failing test disappear — it hides a real implementation gap. ## Examples ### Whole suite against the ffi factory ```dart @TestOn('vm') library; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_test/all_test.dart' as all; import 'package:sqflite_common_test/sqflite_test.dart'; import 'package:test/test.dart'; class FfiTestContext extends SqfliteLocalTestContext { FfiTestContext() : super(databaseFactory: databaseFactoryFfi); @override bool get supportsUri => true; } void main() { sqfliteFfiInit(); all.run(FfiTestContext()); } ``` ### Isolated databases path, so two suite files can run in parallel ```dart @TestOn('vm') library; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_test/all_test.dart' as all; import 'package:sqflite_common_test/sqflite_test.dart'; import 'package:test/test.dart'; final _factory = createDatabaseFactoryFfi(noIsolate: true); Future<void> main() async { sqfliteFfiInit(); var dbsPath = await _factory.getDatabasesPath(); await _factory.setDatabasesPath('${dbsPath}_no_isolate'); group('ffi_no_isolate', () { all.run(SqfliteLocalTestContext(databaseFactory: _factory)); }); } ``` ### Only a few suites, plus the protocol suite with no factory ```dart @TestOn('vm') library; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_test/batch_test.dart' as batch_test; import 'package:sqflite_common_test/raw_test.dart' as raw_test; import 'package:sqflite_common_test/sqflite_protocol_test.dart' as protocol_test; import 'package:sqflite_common_test/sqflite_test.dart'; import 'package:sqflite_common_test/transaction_test.dart' as transaction_test; import 'package:sqflite_common_test/wal_test.dart'; import 'package:test/test.dart'; void main() { sqfliteFfiInit(); var context = SqfliteLocalTestContext(databaseFactory: databaseFactoryFfi); raw_test.run(context); batch_test.run(context, noManualTransactionTest: true); transaction_test.run(context); walTests(context); protocol_test.run(null); // mock based, no real database } ``` ### Own tests reusing the context helpers ```dart @TestOn('vm') library; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_test/sqflite_test.dart'; import 'package:test/test.dart'; void main() { sqfliteFfiInit(); var context = SqfliteLocalTestContext(databaseFactory: databaseFactoryFfi); test('my schema survives a reopen', () async { var path = await context.initDeleteDb('my_schema.db'); var db = await context.databaseFactory.openDatabase( path, options: OpenDatabaseOptions( version: 1, onCreate: (db, _) => db.execute('CREATE TABLE Item (id INTEGER PRIMARY KEY)'), ), ); await db.close(); db = await context.databaseFactory.openDatabase(path); expect(await db.query('Item'), isEmpty); await db.close(); }); } ``` ### A DatabaseFactory the code under test must not touch ```dart import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common_test/database_factory_mock.dart'; import 'package:test/test.dart'; class Repository { Repository(this.factory); final DatabaseFactory factory; bool get isConfigured => true; // never opens the database } void main() { test('no database access on construction', () { var repository = Repository(databaseFactoryMock); expect(repository.isConfigured, isTrue); }); } ```
View on GitHub