Write unit tests for DelegateDatabase

This commit is contained in:
Simon Binder 2019-09-29 16:21:09 +02:00
parent 308167dc12
commit 4137f6cffa
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 209 additions and 7 deletions

View File

@ -168,6 +168,7 @@ class _TransactionExecutor extends TransactionExecutor
Future<void> send() async { Future<void> send() async {
if (_sendOnCommit != null) { if (_sendOnCommit != null) {
await runCustom(_sendOnCommit, const []); await runCustom(_sendOnCommit, const []);
_db.delegate.isInTransaction = false;
} }
_sendCalled.complete(); _sendCalled.complete();

View File

@ -0,0 +1,179 @@
import 'package:mockito/mockito.dart';
import 'package:moor/backends.dart';
import 'package:moor/moor.dart';
import 'package:test/test.dart';
class _MockDelegate extends Mock implements DatabaseDelegate {}
class _MockUserDb extends Mock implements GeneratedDatabase {}
class _MockDynamicVersionDelegate extends Mock
implements DynamicVersionDelegate {}
class _MockTransactionDelegate extends Mock
implements SupportedTransactionDelegate {}
void main() {
_MockDelegate delegate;
setUp(() {
delegate = _MockDelegate();
when(delegate.isOpen).thenAnswer((_) => Future.value(true));
when(delegate.runSelect(any, any))
.thenAnswer((_) => Future.value(QueryResult.fromRows([])));
when(delegate.runUpdate(any, any)).thenAnswer((_) => Future.value(3));
when(delegate.runInsert(any, any)).thenAnswer((_) => Future.value(4));
when(delegate.runCustom(any, any)).thenAnswer((_) => Future.value());
when(delegate.runBatched(any)).thenAnswer((_) => Future.value());
});
group('delegates queries', () {
void _runTests(bool sequential) {
test('when sequential = $sequential', () async {
final db = DelegatedDatabase(delegate, isSequential: sequential);
await db.doWhenOpened((_) async {
expect(await db.runSelect(null, null), isEmpty);
expect(await db.runUpdate(null, null), 3);
expect(await db.runInsert(null, null), 4);
await db.runCustom(null);
await db.runBatched(null);
});
verifyInOrder([
delegate.isOpen,
delegate.runSelect(null, null),
delegate.runUpdate(null, null),
delegate.runInsert(null, null),
delegate.runCustom(null, []),
delegate.runBatched(null),
]);
});
}
_runTests(false);
_runTests(true);
});
group('migrations', () {
DelegatedDatabase db;
_MockUserDb userDb;
setUp(() {
userDb = _MockUserDb();
when(userDb.schemaVersion).thenReturn(3);
when(delegate.isOpen).thenAnswer((_) => Future.value(false));
db = DelegatedDatabase(delegate)..databaseInfo = userDb;
when(userDb.handleDatabaseCreation(executor: anyNamed('executor')))
.thenAnswer((i) async {
final executor = i.namedArguments.values.single as SqlExecutor;
await executor('created', []);
});
when(userDb.handleDatabaseVersionChange(
executor: anyNamed('executor'),
from: anyNamed('from'),
to: anyNamed('to'),
)).thenAnswer((i) async {
final executor = i.namedArguments[#executor] as SqlExecutor;
final from = i.namedArguments[#from] as int;
final to = i.namedArguments[#to] as int;
await executor('upgraded', [from, to]);
});
});
test('when the database does not support versions', () async {
when(delegate.versionDelegate).thenReturn(const NoVersionDelegate());
await db.doWhenOpened((_) async {});
verify(delegate.open(userDb));
verifyNever(delegate.runCustom(any, any));
});
test('when the database supports versions at opening', () async {
when(delegate.versionDelegate)
.thenReturn(OnOpenVersionDelegate(() => Future.value(3)));
await db.doWhenOpened((_) async {});
verify(delegate.open(userDb));
verifyNever(delegate.runCustom(any, any));
});
test('when the database supports dynamic version', () async {
final version = _MockDynamicVersionDelegate();
when(version.schemaVersion).thenAnswer((_) => Future.value(3));
when(delegate.versionDelegate).thenReturn(version);
await db.doWhenOpened((_) async {});
verify(delegate.open(userDb));
verifyNever(delegate.runCustom(any, any));
verify(version.schemaVersion);
verify(version.setSchemaVersion(3));
});
test('handles database creations', () async {
when(delegate.versionDelegate)
.thenReturn(OnOpenVersionDelegate(() => Future.value(0)));
await db.doWhenOpened((_) async {});
verify(delegate.runCustom('created', []));
});
test('handles database upgrades', () async {
when(delegate.versionDelegate)
.thenReturn(OnOpenVersionDelegate(() => Future.value(1)));
await db.doWhenOpened((_) async {});
verify(delegate.runCustom('upgraded', [1, 3]));
});
});
group('transactions', () {
DelegatedDatabase db;
setUp(() {
db = DelegatedDatabase(delegate, isSequential: true);
});
test('when the delegate does not support transactions', () async {
when(delegate.transactionDelegate)
.thenReturn(const NoTransactionDelegate());
await db.doWhenOpened((_) async {
final transaction = db.beginTransaction();
await transaction.doWhenOpened((e) async {
await e.runSelect(null, null);
await transaction.send();
});
});
verifyInOrder([
delegate.runCustom('BEGIN TRANSACTION', []),
delegate.runSelect(null, null),
delegate.runCustom('COMMIT TRANSACTION', []),
]);
});
test('when the database supports transactions', () async {
final transaction = _MockTransactionDelegate();
when(transaction.startTransaction(any)).thenAnswer((i) {
(i.positionalArguments.single as Function(QueryDelegate))(delegate);
});
when(delegate.transactionDelegate).thenReturn(transaction);
await db.doWhenOpened((_) async {
final transaction = db.beginTransaction();
await transaction.doWhenOpened((e) async {
await e.runSelect(null, null);
await transaction.send();
});
});
verify(transaction.startTransaction(any));
});
});
}

View File

@ -18,6 +18,7 @@ void main() {
test('generates insert statements', () async { test('generates insert statements', () async {
await db.into(db.todosTable).insert(const TodosTableCompanion( await db.into(db.todosTable).insert(const TodosTableCompanion(
content: Value('Implement insert statements'), content: Value('Implement insert statements'),
title: Value.absent(),
)); ));
verify(executor.runInsert('INSERT INTO todos (content) VALUES (?)', verify(executor.runInsert('INSERT INTO todos (content) VALUES (?)',
@ -111,16 +112,21 @@ void main() {
verify(streamQueries.handleTableUpdates({db.users})); verify(streamQueries.handleTableUpdates({db.users}));
}); });
test('enforces data integrity', () { test('enforces data integrity', () async {
expect( InvalidDataException exception;
db.into(db.todosTable).insert( try {
await db.into(db.todosTable).insert(
const TodosTableCompanion( const TodosTableCompanion(
// not declared as nullable in table definition // not declared as nullable in table definition
content: Value(null), content: Value(null),
), ),
), );
throwsA(const TypeMatcher<InvalidDataException>()), fail('inserting invalid data did not throw');
); } on InvalidDataException catch (e) {
exception = e;
}
expect(exception.toString(), startsWith('InvalidDataException'));
}); });
test('reports auto-increment id', () async { test('reports auto-increment id', () async {

View File

@ -1,4 +1,5 @@
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:moor/src/runtime/components/join.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'data/tables/todos.dart'; import 'data/tables/todos.dart';
import 'data/utils/mocks.dart'; import 'data/utils/mocks.dart';
@ -113,4 +114,19 @@ void main() {
verify(executor.runSelect( verify(executor.runSelect(
argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3])); argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3]));
}); });
test('injects custom error message when a table is used multiple times',
() async {
when(executor.runSelect(any, any)).thenAnswer((_) => Future.error('nah'));
MoorWrappedException wrappedException;
try {
await db.select(db.todosTable).join([crossJoin(db.todosTable)]).get();
fail('expected this to throw');
} on MoorWrappedException catch (e) {
wrappedException = e;
}
expect(wrappedException.toString(), contains('possible cause'));
});
} }

View File

@ -17,7 +17,7 @@ void main() {
group('Migrations', () { group('Migrations', () {
test('creates all tables', () async { test('creates all tables', () async {
await Migrator(db, mockQueryExecutor).createAllTables(); await db.handleDatabaseCreation(executor: mockQueryExecutor);
// should create todos, categories, users and shared_todos table // should create todos, categories, users and shared_todos table
verify(mockQueryExecutor.call( verify(mockQueryExecutor.call(