diff --git a/README.md b/README.md index 643c17a..b761d21 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# dart_mock +# Beispiel Programmieren gegen Interfaces + +* `dart run` - Startet Programm mit Sqlite-Backend +* `dart test` - Testet mit Map-Backend diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/bin/mock.dart b/bin/mock.dart new file mode 100644 index 0000000..e745955 --- /dev/null +++ b/bin/mock.dart @@ -0,0 +1,43 @@ +import 'package:mock/person.dart'; +import 'package:mock/business.dart'; +import 'package:mock/person_data_access_map.dart'; +import 'package:mock/person_data_access_sqlite.dart'; + +/* + @startuml +class Business { +- PersonDataAccessMap backend +} +PersonDataAccessMap <-left- Business : uses + +class PersonDataAccessMap { +- Map map + + store(Person p): void + + retrieve(int id): Person + + retrieveAll(): List +} + +class PersonDataAccessSqlite implements PersonDataAccessMap { +- Sqlite db + + store(Person p): void + + retrieve(int id): Person + + retrieveAll(): List +} +@enduml + */ +void main() { + PersonDataAccessMap backend = PersonDataAccessSqlite("sqlite3.db"); + Business b = Business(backend); + + List persons = b.getAllPersons(); + int personCount = persons.length; + + print("We know $personCount persons"); + + int nextPersonId = ++personCount; + b.addPerson(Person(nextPersonId, "A New Person")); + + print("Now we know ${b.getAllPersons().length} persons"); + + backend.close(); +} diff --git a/lib/business.dart b/lib/business.dart new file mode 100644 index 0000000..a24db4e --- /dev/null +++ b/lib/business.dart @@ -0,0 +1,14 @@ +import 'person.dart'; +import 'person_data_access_map.dart'; + +class Business { + final PersonDataAccessMap _backend; + + Business(this._backend); + + void addPerson(Person p) => _backend.store(p); + + bool existsPersonId(int id) => _backend.retrieve(id) != null; + + List getAllPersons() => _backend.retrieveAll(); +} diff --git a/lib/person.dart b/lib/person.dart new file mode 100644 index 0000000..929abbe --- /dev/null +++ b/lib/person.dart @@ -0,0 +1,9 @@ +class Person { + final int id; + final String name; + + const Person(this.id, this.name); + + @override + String toString() => "Person: $name ($id)"; +} diff --git a/lib/person_data_access_map.dart b/lib/person_data_access_map.dart new file mode 100644 index 0000000..7ec863b --- /dev/null +++ b/lib/person_data_access_map.dart @@ -0,0 +1,13 @@ +import 'person.dart'; + +class PersonDataAccessMap { + final Map _persons = {}; + + void store(Person p) => _persons[p.id] = p; + + Person? retrieve(int personId) => _persons[personId]; + + List retrieveAll() => _persons.values.toList(); + + void close() {} +} diff --git a/lib/person_data_access_sqlite.dart b/lib/person_data_access_sqlite.dart new file mode 100644 index 0000000..8f287bc --- /dev/null +++ b/lib/person_data_access_sqlite.dart @@ -0,0 +1,48 @@ +import 'person.dart'; +import 'package:sqlite3/sqlite3.dart'; + +import 'person_data_access_map.dart'; + +class PersonDataAccessSqlite implements PersonDataAccessMap { + final String fileName; + final Database db; + + PersonDataAccessSqlite(this.fileName) : db = sqlite3.open(fileName) { + try { + // when select fails due to missing table ... + db.execute('SELECT count(*) from persons'); + } catch (e) { + // ... create it + db.execute('''CREATE TABLE persons ( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL);'''); + } + } + + @override + void store(Person p) { + final stmt = db.prepare('INSERT INTO persons (id, name) VALUES (?, ?)'); + stmt.execute([p.id, p.name]); + stmt.dispose(); + } + + @override + Person? retrieve(int id) { + final ResultSet resultSet = + db.select('SELECT * FROM persons WHERE id = ?', [id]); + return resultSet.isEmpty + ? null + : Person(resultSet.first['id'], resultSet.first['name']); + } + + @override + List retrieveAll() { + final ResultSet resultSet = db.select('SELECT * FROM persons'); + return resultSet.map((row) => Person(row['id'], row['name'])).toList(); + } + + @override + void close() { + db.dispose(); + } +} diff --git a/mock.iml b/mock.iml new file mode 100644 index 0000000..899e825 --- /dev/null +++ b/mock.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mock.puml b/mock.puml new file mode 100644 index 0000000..1ea36be --- /dev/null +++ b/mock.puml @@ -0,0 +1,21 @@ +@startuml +class Business { +- PersonDataAccess backend +} +PersonDataAccess <-left- Business : uses + +interface PersonDataAccess { + + store(Person p): void + + retrieve(int id): Person + + retrieveAll(): List +} + +class PersonDataAccessMap implements PersonDataAccess { +- Map map +} + +class PersonDataAccessSqlite implements PersonDataAccess { +- Sqlite db +} + +@enduml \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..c9670e5 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,13 @@ +name: mock +description: A simple Mock example +version: 1.0.0 + +environment: + sdk: '>=2.18.0 <3.0.0' + +dev_dependencies: + lints: ^2.0.0 + test: + +dependencies: + sqlite3: ^1.2.0 diff --git a/test/business_test.dart b/test/business_test.dart new file mode 100644 index 0000000..fb76d3e --- /dev/null +++ b/test/business_test.dart @@ -0,0 +1,39 @@ +import 'package:mock/person_data_access_map.dart'; +import 'package:test/test.dart'; +import 'package:mock/person.dart'; +import 'package:mock/business.dart'; + +class TestPersonBackend extends PersonDataAccessMap { + TestPersonBackend() { + store(Person(1, "Mr. X")); + store(Person(2, "Ms. Y")); + } +} + +void main() { + group('Backend', () { + PersonDataAccessMap? backend; + + tearDownAll(() { + backend?.close(); + }); + + group('Empty', () { + final PersonDataAccessMap backend = PersonDataAccessMap(); + final Business business = Business(backend); + + test('Initial size', () { + expect(business.getAllPersons().length, 0); + }); + }); + + group('Filled', () { + final PersonDataAccessMap backend = TestPersonBackend(); + final Business business = Business(backend); + + test('Initial size', () { + expect(business.getAllPersons().length, 2); + }); + }); + }); +} diff --git a/test/person_test.dart b/test/person_test.dart new file mode 100644 index 0000000..fd21777 --- /dev/null +++ b/test/person_test.dart @@ -0,0 +1,36 @@ +import 'package:test/test.dart'; +import 'package:mock/person.dart'; + +void main() { + group('Person properties', () { + final Person p1 = Person(1, "Dummy User"); + + setUp(() { + // runs before each test; + print("Set up"); + }); + + tearDown(() { + // runs after each test; + print("Tear down"); + }); + test('id', () { + expect(p1.id, 1); + }); + + test('name', () { + expect(p1.id, 1); + }); + }); + + group('Comparing persons', () { + final Person p1 = Person(1, "One person"); + final Person p2 = Person(2, "Another person"); + test('Same object', () { + expect(p1 == p1, true); + }); + test('Different objects', () { + expect(p1 != p2, true); + }); + }); +}