Agent Skills
› tekartik/sqflite
› sqflite-common-test-suite
sqflite-common-test-suite
GitHubsqflite通用测试套件,用于验证各种DatabaseFactory实现的兼容性。提供标准化测试用例覆盖SQL操作、事务及特性标志,确保不同平台实现的一致性。
Trigger Scenarios
运行sqflite数据库实现兼容性测试
验证自定义或官方sqflite工厂的合规性
Install
npx skills add tekartik/sqflite --skill sqflite-common-test-suite -g -y
SKILL.md
Frontmatter
{
"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), indev_dependencies:
It pullsdev_dependencies: sqflite_common_test: git: url: https://github.com/tekartik/sqflite path: sqflite_common_test version: '>=0.3.0'sqflite_common,sqflite_common_ffi,test,pathandsynchronized. - Two imports are enough for the common case:
package:sqflite_common_test/sqflite_test.dart(the context types) andpackage:sqflite_common_test/all_test.dart(the whole suite). Importall_test.dartwith a prefix (as all): every suite library exports a top-levelrun. - Entry points of
all_test.dart:run(SqfliteTestContext context)andsqfliteTestGroup(SqfliteTestContext context)are the same thing; call it frommain(), optionally inside your owngroup('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): onlysqflite_common_ffi_asyncsets 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 nativesqfliteplugin factory.isWeb,isAndroid,isIOS,isMacOS,isLinux,isWindowscome from the mixins; do not override them.
- Always call the implementation initializer before
run()(for ffi:sqfliteFfiInit()frompackage: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 anasyncmain()beforerun(). - Add
@TestOn('vm')(beforelibrary;) 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>.dartand call itsrun(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.dartexportswalTests(context)(notrun) andsqflite_protocol_test.dartexportsrun(SqfliteTestContext?), which acceptsnulland 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(apackage:pathContext). package:sqflite_common_test/database_factory_mock.dartgivesDatabaseFactoryMock/databaseFactoryMock: every method throwsUnimplementedError. Use it to satisfy aDatabaseFactoryparameter that the code under test must not call, never to fake results.- Anti-patterns: calling
run(context)insidetest()orsetUp()(it declares tests, so it must run atmain()level); sharing one databases path between suite runs; overriding asupports*flag totrueto make a failing test disappear — it hides a real implementation gap.
Examples
Whole suite against the ffi factory
@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
@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
@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
@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
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);
});
}
Version History
- aaabf90 Current 2026-09-22 03:38


