Dart 49 ๐ฏ Unit Testing with package:test: Grouping, Assertions, and Expectations
A unit test is a small program that exercises one piece of the code and asserts the expected result. The package:test is the standard testing framework for the Dart, and it provides the test function, the group function, the expect function, and the matchers. The framework runs the tests, collects the results, and reports the pass and the fail. The dart test command is the runner, and the flutter test is the Flutter equivalent. The unit tests are the first line of the defense against the regressions, and the package:test is the tool that makes the tests the readable and the maintainable.
The structure is the main function with the test and the group calls. The test function defines the single test case, and the group function groups the related tests under the shared description. The expect function asserts the actual value against the matcher, and the matcher is the equals, the isA<T>, the throwsA, the contains, the hasLength, and the others. The setUp and the tearDown run the code before and after each test, and the setUpAll and the tearDownAll run it once for the group .
The async support is the built-in. The test function accepts the async callback, and the await works inside the test. The expectLater handles the asynchronous matchers, and the completes, the emits, and the throwsA work with the futures and the streams. The FakeAsync and the fakeAsync handle the timers, and the Clock handles the time-dependent code.
This chapter covers three areas. First, why the unit testing exists โ the regression problem and the test-as-documentation. Second, the test structure โ the test, the group, the setUp, the tearDown, and the naming. Third, the assertions and the matchers โ the expect, the equals, the isA, the throwsA, the contains, the hasLength, the predicate, and the async matchers. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the test lifecycle.
Key point: The package:test provides the test, the group, the expect, the setUp, and the tearDown. The test defines the single test case, the group groups the related tests, and the expect asserts the actual value against the matcher. The setUp and the tearDown run the code before and after each test. The async support is the built-in with the await, and the expectLater handles the asynchronous matchers.
Why the unit testing exists
The regression problem. A change to the code can break the existing behavior. The developer adds the feature, and the unrelated function stops working. The manual testing catches the break sometimes, and the unit tests catch it every time. The test asserts the expected behavior, and the regression fails the test. The suite is the safety net, and the dart test is the runner .
The documentation problem. The test is the executable documentation. The test shows how the function is called, what the inputs are, and what the outputs are. The new developer reads the tests to understand the code. The test that is the clear and the focused is the better documentation than the comment .
The design problem. The code that is hard to test is often the code with the hidden dependencies and the tight coupling. The test forces the dependency injection and the separation. The testable code is the modular and the maintainable. The unit tests improve the design as the side effect .
The refactoring problem. The refactoring is safe when the tests pass. The developer changes the implementation, and the tests verify the behavior is unchanged. The refactoring without the tests is the gamble, and the tests make it the routine .
The package:test and the flutter_test. The package:test is the pure Dart testing framework, and the flutter_test builds on it with the widget testing and the golden testing. The unit tests use the package:test, and the Flutter tests use the flutter_test. The two share the test, the group, the expect, and the matchers .
The trade-off. The tests are the code that must be written and maintained. The test suite adds the time to the development, and the maintenance is the ongoing. The tests that are the brittle and the coupled to the implementation are the liability. The trade-off is between the safety of the tests and the cost of the writing, and the discipline is the focused, the behavior-oriented tests.
a. The test structure
The test file is the Dart file with the .dart extension, and the test package is the import. The main function contains the tests.
import 'package:test/test.dart';
void main() {
test('the addition', () {
expect(1 + 1, equals(2));
});
}
The test function takes the description and the callback. The description is the readable string, and the callback is the test body. The expect asserts the actual value against the matcher .
The group function groups the related tests:
void main() {
group('the Calculator', () {
test('adds the numbers', () {
expect(Calculator.add(1, 2), equals(3));
});
test('subtracts the numbers', () {
expect(Calculator.subtract(5, 3), equals(2));
});
});
}
The group takes the description and the callback. The nested groups are the allowed, and the description is the concatenated in the output .
The setUp and the tearDown:
void main() {
group('the UserRepository', () {
late UserRepository repository;
setUp(() {
repository = UserRepository();
});
tearDown(() {
repository.dispose();
});
test('the initial state is the empty', () {
expect(repository.users, isEmpty);
});
test('the add adds the user', () {
repository.add(User(name: 'Alice'));
expect(repository.users, hasLength(1));
});
});
}
The setUp runs before each test, and the tearDown runs after each test. The late variable is declared in the group scope, and the setUp assigns it. The setUpAll and the tearDownAll run once for the group .
The naming convention. The test description is the behavior, not the method. The “the add adds the user” is the better than the “testAdd”. The description completes the sentence “the test verifies that…”, and the failure message is the readable .
The skip and the timeout:
test('the slow operation', () async {
// ...
}, skip: 'The known issue');
test('the slow operation', () async {
// ...
}, timeout: Timeout(Duration(seconds: 30)));
The skip skips the test with the reason, and the timeout overrides the default 30 seconds .
b. The assertions and the matchers
The expect function takes the actual value and the matcher, and it throws the TestFailure when the match fails.
The equality. The equals matches the value equality:
expect(1 + 1, equals(2));
expect('hello', equals('hello'));
expect([1, 2, 3], equals([1, 2, 3]));
The equals uses the == for the primitives and the deep equality for the collections .
The type. The isA<T> matches the type:
expect(value, isA<int>());
expect(value, isA<String>());
expect(value, isA<User>());
The isA<T> passes when the value is the instance of the type. The isNot negates the matcher:
expect(value, isNot(isA<String>()));
The boolean. The isTrue and the isFalse match the boolean:
expect(active, isTrue);
expect(active, isFalse);
expect(value, isNull);
expect(value, isNotNull);
The collections. The contains and the hasLength:
expect([1, 2, 3], contains(2));
expect('hello world', contains('world'));
expect([1, 2, 3], hasLength(3));
expect({'a': 1}, containsPair('a', 1));
The contains matches the element, the hasLength matches the length, and the containsPair matches the key-value pair .
The string. The startsWith, the endsWith, and the matches:
expect('hello world', startsWith('hello'));
expect('hello world', endsWith('world'));
expect('hello world', matches(RegExp(r'^hello')));
The numeric. The greaterThan, the lessThan, and the closeTo:
expect(5, greaterThan(3));
expect(5, lessThan(10));
expect(0.1 + 0.2, closeTo(0.3, 0.0001));
The closeTo handles the floating-point comparison with the tolerance .
The exception. The throwsA matches the exception:
expect(() => int.parse('abc'), throwsA(isA<FormatException>()));
expect(() => throw Exception('boom'), throwsException);
expect(() => throw StateError('bad'), throwsStateError);
expect(() => throw ArgumentError('bad'), throwsArgumentError);
The throwsA takes the matcher for the exception, and the isA<T> matches the type. The throwsException and the throwsStateError are the shorthand .
The predicate. The predicate matches the custom condition:
expect(5, predicate<int>((value) => value.isOdd));
expect(user, predicate<User>((u) => u.name.startsWith('A')));
The predicate takes the function and the optional description .
The anything and the isNot. The anything matches any value, and the isNot negates the matcher:
expect(value, anything);
expect(value, isNot(equals(5)));
The async matchers. The expectLater handles the asynchronous:
test('the async', () async {
await expectLater(fetchData(), completion(equals('data')));
await expectLater(fetchError(), throwsA(isA<Exception>()));
await expectLater(stream, emits(1));
await expectLater(stream, emitsInOrder([1, 2, 3]));
});
The completion matches the future result, the throwsA matches the future error, the emits matches the stream element, and the emitsInOrder matches the sequence .
The returnsNormally and the isNot. The returnsNormally matches the function that does not throw, and the isNot negates:
expect(() => int.parse('42'), returnsNormally);
c. The patterns and the best practices
The arrange-act-assert. The test is the three parts: the arrange sets the state, the act performs the operation, and the assert verifies the result:
test('the add adds the item', () {
// The arrange
final cart = ShoppingCart();
// The act
cart.add(Item(name: 'Apple', price: 1.50));
// The assert
expect(cart.items, hasLength(1));
expect(cart.total, equals(1.50));
});
The three parts are the readable, and the structure is the consistent.
The one assertion per concept. The test asserts one concept, and the multiple assertions are the related:
// โ
The related assertions
test('the create user', () {
final user = User.create(name: 'Alice');
expect(user.name, equals('Alice'));
expect(user.id, isNotNull);
});
// โ The unrelated assertions
test('the user and the order', () {
expect(user.name, equals('Alice'));
expect(order.total, equals(100));
});
The descriptive names. The test description is the behavior:
test('the empty list has the zero length', () { ... });
test('the add throws when the item is null', () { ... });
The test fixtures. The shared fixtures are in the setUp:
group('the UserService', () {
late UserService service;
late MockDatabase db;
setUp(() {
db = MockDatabase();
service = UserService(db);
});
test('the find returns the user', () {
db.seed([User(id: 1, name: 'Alice')]);
expect(service.find(1)?.name, equals('Alice'));
});
});
The async tests. The test callback is the async, and the await works:
test('the fetch returns the data', () async {
final result = await fetchData();
expect(result, equals('data'));
});
The test organization. The test directory mirrors the lib directory, and the test file has the _test.dart suffix:
lib/
calculator.dart
user_service.dart
test/
calculator_test.dart
user_service_test.dart
The dart test command discovers the files with the _test.dart suffix in the test directory .
Complete Example Session
// ============================================
// PART 1: THE BASIC TEST
// ============================================
import 'package:test/test.dart';
void main() {
test('the addition', () {
expect(1 + 1, equals(2));
});
}
// ============================================
// PART 2: THE GROUP
// ============================================
void main() {
group('the Calculator', () {
test('adds the numbers', () {
expect(Calculator.add(1, 2), equals(3));
});
test('subtracts the numbers', () {
expect(Calculator.subtract(5, 3), equals(2));
});
test('divides the numbers', () {
expect(Calculator.divide(10, 2), equals(5));
});
});
}
// ============================================
// PART 3: THE SETUP AND THE TEARDOWN
// ============================================
void main() {
group('the UserRepository', () {
late UserRepository repository;
setUp(() {
repository = UserRepository();
});
tearDown(() {
repository.dispose();
});
test('the initial state is the empty', () {
expect(repository.users, isEmpty);
});
test('the add adds the user', () {
repository.add(User(name: 'Alice'));
expect(repository.users, hasLength(1));
});
test('the remove removes the user', () {
final user = User(name: 'Alice');
repository.add(user);
repository.remove(user);
expect(repository.users, isEmpty);
});
});
}
// ============================================
// PART 4: THE SETUPALL AND THE TEARDOWNAALL
// ============================================
void main() {
group('the DatabaseTest', () {
late Database db;
setUpAll(() {
db = Database.connect();
});
tearDownAll(() async {
await db.close();
});
test('the connection is the open', () {
expect(db.isOpen, isTrue);
});
});
}
// ============================================
// PART 5: THE MATCHERS
// ============================================
void main() {
test('the equality', () {
expect(1 + 1, equals(2));
expect([1, 2, 3], equals([1, 2, 3]));
});
test('the type', () {
expect('hello', isA<String>());
expect(42, isA<int>());
expect(42, isNot(isA<String>()));
});
test('the boolean', () {
expect(true, isTrue);
expect(false, isFalse);
expect(null, isNull);
expect('value', isNotNull);
});
test('the collections', () {
expect([1, 2, 3], contains(2));
expect([1, 2, 3], hasLength(3));
expect({'a': 1}, containsPair('a', 1));
});
test('the string', () {
expect('hello world', startsWith('hello'));
expect('hello world', endsWith('world'));
expect('hello world', contains('world'));
expect('hello world', matches(RegExp(r'^hello')));
});
test('the numeric', () {
expect(5, greaterThan(3));
expect(5, lessThan(10));
expect(0.1 + 0.2, closeTo(0.3, 0.0001));
});
}
// ============================================
// PART 6: THE EXCEPTIONS
// ============================================
void main() {
test('the exception', () {
expect(() => int.parse('abc'), throwsA(isA<FormatException>()));
expect(() => throw Exception('boom'), throwsException);
expect(() => throw StateError('bad'), throwsStateError);
expect(() => throw ArgumentError('bad'), throwsArgumentError);
});
test('the specific error', () {
expect(
() => divide(10, 0),
throwsA(
isA<ArgumentError>().having(
(e) => e.message,
'the message',
'The divisor cannot be the zero',
),
),
);
});
}
// ============================================
// PART 7: THE PREDICATE
// ============================================
void main() {
test('the predicate', () {
expect(5, predicate<int>((value) => value.isOdd));
expect(4, predicate<int>((value) => value.isEven));
expect('Alice', predicate<String>((name) => name.startsWith('A')));
});
}
// ============================================
// PART 8: THE ASYNC MATCHERS
// ============================================
void main() {
test('the future completes', () async {
await expectLater(fetchData(), completion(equals('data')));
});
test('the future throws', () async {
await expectLater(fetchError(), throwsA(isA<Exception>()));
});
test('the stream emits', () async {
await expectLater(countStream(3), emits(1));
});
test('the stream emits in order', () async {
await expectLater(countStream(3), emitsInOrder([1, 2, 3]));
});
}
// ============================================
// PART 9: THE ARRANGE-ACT-ASSERT
// ============================================
void main() {
group('the ShoppingCart', () {
test('the add adds the item', () {
// The arrange
final cart = ShoppingCart();
// The act
cart.add(Item(name: 'Apple', price: 1.50));
// The assert
expect(cart.items, hasLength(1));
expect(cart.total, equals(1.50));
});
test('the total is the sum', () {
final cart = ShoppingCart()
..add(Item(name: 'Apple', price: 1.50))
..add(Item(name: 'Bread', price: 2.50));
expect(cart.total, equals(4.00));
});
});
}
// ============================================
// PART 10: THE COMPLETE EXAMPLE
// ============================================
import 'package:test/test.dart';
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
}
class UserService {
final List<User> _users = [];
int _nextId = 1;
List<User> get users => List.unmodifiable(_users);
User create(String name, String email) {
if (name.isEmpty) {
throw ArgumentError('The name cannot be the empty');
}
if (!email.contains('@')) {
throw ArgumentError('The email is not the valid');
}
final user = User(id: _nextId++, name: name, email: email);
_users.add(user);
return user;
}
User? find(int id) {
return _users.where((u) => u.id == id).firstOrNull;
}
bool remove(int id) {
final before = _users.length;
_users.removeWhere((u) => u.id == id);
return _users.length < before;
}
}
void main() {
group('the UserService', () {
late UserService service;
setUp(() {
service = UserService();
});
group('the create', () {
test('the creates the user', () {
final user = service.create('Alice', 'alice@example.com');
expect(user.id, equals(1));
expect(user.name, equals('Alice'));
expect(user.email, equals('alice@example.com'));
});
test('the increments the id', () {
service.create('Alice', 'alice@example.com');
final user2 = service.create('Bob', 'bob@example.com');
expect(user2.id, equals(2));
});
test('the adds to the list', () {
service.create('Alice', 'alice@example.com');
expect(service.users, hasLength(1));
});
test('the throws when the name is the empty', () {
expect(
() => service.create('', 'alice@example.com'),
throwsA(isA<ArgumentError>()),
);
});
test('the throws when the email is the invalid', () {
expect(
() => service.create('Alice', 'invalid'),
throwsA(
isA<ArgumentError>().having(
(e) => e.message,
'the message',
contains('email'),
),
),
);
});
});
group('the find', () {
test('the returns the user', () {
final created = service.create('Alice', 'alice@example.com');
final found = service.find(created.id);
expect(found, isNotNull);
expect(found!.name, equals('Alice'));
});
test('the returns the null when the missing', () {
expect(service.find(999), isNull);
});
});
group('the remove', () {
test('the removes the user', () {
final user = service.create('Alice', 'alice@example.com');
final removed = service.remove(user.id);
expect(removed, isTrue);
expect(service.users, isEmpty);
});
test('the returns the false when the missing', () {
expect(service.remove(999), isFalse);
});
});
});
}
The ten parts show the basic test, the group, the setUp and the tearDown, the setUpAll and the tearDownAll, the matchers, the exceptions, the predicate, the async matchers, the arrange-act-assert, and the complete example.
Quick Reference
The Test Functions
| The function | The purpose |
|---|---|
test(description, callback) | The single test |
group(description, callback) | The group |
setUp(callback) | Runs before each test |
tearDown(callback) | Runs after each test |
setUpAll(callback) | Runs once before the group |
tearDownAll(callback) | Runs once after the group |
expect(actual, matcher) | The assertion |
expectLater(actual, matcher) | The async assertion |
The Common Matchers
| The matcher | The purpose |
|---|---|
equals(value) | The equality |
isA<T>() | The type |
isTrue / isFalse | The boolean |
isNull / isNotNull | The null |
contains(value) | The collection contains |
hasLength(n) | The length |
containsPair(k, v) | The map pair |
startsWith(s) | The prefix |
endsWith(s) | The suffix |
matches(regexp) | The regex |
greaterThan(n) | The comparison |
lessThan(n) | The comparison |
closeTo(n, delta) | The float |
throwsA(matcher) | The exception |
throwsException | The any exception |
returnsNormally | The no exception |
predicate<T>(f) | The custom |
anything | The any value |
isNot(matcher) | The negation |
The Async Matchers
| The matcher | The purpose |
|---|---|
completion(matcher) | The future result |
throwsA(matcher) | The future error |
emits(value) | The stream element |
emitsInOrder([...]) | The stream sequence |
emitsDone | The stream done |
emitsError(matcher) | The stream error |
The Test Options
| The option | The purpose |
|---|---|
skip: 'reason' | Skips the test |
timeout: Timeout(...) | The custom timeout |
tags: ['tag'] | The test tags |
retry: 3 | The retry |
The Commands
| The command | The purpose |
|---|---|
dart test | Runs all the tests |
dart test path/to/file.dart | Runs the specific file |
dart test -n 'the pattern' | Runs the matching tests |
dart test --coverage=coverage | The coverage |
dart test -r expanded | The detailed reporter |
Best Practices
โ Do This:
// Use the arrange-act-assert
final cart = ShoppingCart();
cart.add(Item(name: 'Apple'));
expect(cart.items, hasLength(1)); // โ
// Use the descriptive names
test('the empty list has the zero length', () { ... }); // โ
// Use the setUp for the shared fixtures
setUp(() { service = UserService(); }); // โ
// Use the throwsA for the exceptions
expect(() => int.parse('abc'), throwsA(isA<FormatException>())); // โ
// Use the expectLater for the async
await expectLater(fetchData(), completion(equals('data'))); // โ
// Use the group for the related tests
group('the UserService', () { ... }); // โ
โ Don’t Do This:
// Don't test the multiple unrelated concepts
test('the user and the order', () { ... }); // the unrelated // โ
// Don't use the vague names
test('test1', () { ... }); // the meaningless // โ
// Don't use the setUp for the test-specific state
setUp(() { theTestSpecific = ...; }); // the shared // โ
// Don't forget the await in the async test
test('the async', () {
expectLater(fetchData(), completion(equals('data'))); // the missing await // โ
});
// Don't use the sleep in the test
await Future.delayed(Duration(seconds: 1)); // the slow and the flaky // โ
// Don't test the implementation detail
expect(service._privateField, equals(5)); // the brittle // โ
// Don't depend on the test order
test('the B', () { expect(state, equals('from A')); }); // the order // โ
Common Pitfalls
| The pitfall | The why | The fix |
|---|---|---|
| The flaky test | The timing or the shared state | Use the fakeAsync or the isolated state |
| The missing await | The async test | Add the await |
| The test order dependency | The shared state | Use the setUp |
| The brittle test | The implementation coupling | Test the behavior |
| The slow test | The real I/O | Use the mocks |
| The unclear failure | The vague description | Use the descriptive names |
The setUp not running | The scope | Check the group |
Real-World Examples
1. The Basic Test
test('the addition', () => expect(1 + 1, equals(2)));
2. The Group
group('the Calculator', () { test('adds', () { ... }); });
3. The setUp
setUp(() { service = UserService(); });
4. The Exception
expect(() => int.parse('abc'), throwsA(isA<FormatException>()));
5. The Collection
expect([1, 2, 3], contains(2));
6. The Async
await expectLater(fetchData(), completion(equals('data')));
7. The Stream
await expectLater(stream, emitsInOrder([1, 2, 3]));
8. The Predicate
expect(5, predicate<int>((v) => v.isOdd));
9. The Specific Error
expect(() => divide(10, 0), throwsA(isA<ArgumentError>()));
10. The Complete
group('the UserService', () {
late UserService service;
setUp(() => service = UserService());
test('the create', () { ... });
});
Visual
The Test Lifecycle
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE TEST LIFECYCLE โ
โ โ
โ The group starts: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ setUpAll() โ runs once โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ The test 1: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ setUp() โ runs before the test โ โ
โ โ the test body โ โ
โ โ tearDown() โ runs after the test โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ The test 2: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ setUp() โ โ
โ โ the test body โ โ
โ โ tearDown() โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ The group ends: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ tearDownAll() โ runs once โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Arrange-Act-Assert
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE ARRANGE-ACT-ASSERT โ
โ โ
โ test('the add adds the item', () { โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ // The ARRANGE โ โ
โ โ final cart = ShoppingCart(); โ โ
โ โ โ โ
โ โ // The ACT โ โ
โ โ cart.add(Item(name: 'Apple', price: 1.50)); โ โ
โ โ โ โ
โ โ // The ASSERT โ โ
โ โ expect(cart.items, hasLength(1)); โ โ
โ โ expect(cart.total, equals(1.50)); โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ }); โ
โ โ
โ The three parts are the readable and the consistent. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Matchers
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE MATCHERS โ
โ โ
โ expect(1 + 1, equals(2)); โ the equality โ
โ expect('hello', isA<String>()); โ the type โ
โ expect(true, isTrue); โ the boolean โ
โ expect(null, isNull); โ the null โ
โ expect([1, 2, 3], contains(2)); โ the collection โ
โ expect([1, 2, 3], hasLength(3)); โ the length โ
โ expect('hello', startsWith('he')); โ the prefix โ
โ expect(5, greaterThan(3)); โ the comparison โ
โ expect(0.1 + 0.2, closeTo(0.3, 0.001)); โ the float โ
โ โ
โ expect(() => int.parse('abc'), โ the exception โ
โ throwsA(isA<FormatException>())); โ
โ โ
โ await expectLater(fetchData(), โ the async โ
โ completion(equals('data'))); โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Test Output
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE TEST OUTPUT โ
โ โ
โ The passing: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 00:01 +3: the UserService โ โ
โ โ the create โ โ
โ โ the creates the user โ โ
โ โ the increments the id โ โ
โ โ the adds to the list โ โ
โ โ โ โ
โ โ All tests passed! โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The failing: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 00:01 +2 -1: the UserService โ โ
โ โ the create โ โ
โ โ the creates the user โ โ
โ โ the increments the id โ โ
โ โ โ the adds to the list โ โ
โ โ Expected: the length of 1 โ โ
โ โ Actual: the length of 0 โ โ
โ โ โ โ
โ โ Some tests failed. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
package:test | The Dart testing framework |
test | The single test |
group | The group of the related tests |
setUp / tearDown | The per-test setup |
setUpAll / tearDownAll | The per-group setup |
expect | The assertion |
expectLater | The async assertion |
equals | The equality matcher |
isA<T> | The type matcher |
throwsA | The exception matcher |
completion | The async matcher |
emitsInOrder | The stream matcher |
Key takeaways:
- The
package:testis the standard Dart testing framework. Thetestdefines the single test, thegroupgroups the related tests, and theexpectasserts the actual value against the matcher. ThesetUpand thetearDownrun the code before and after each test . - The
expecttakes the actual value and the matcher. Theequalsmatches the equality, theisA<T>matches the type, thecontainsmatches the collection, thehasLengthmatches the length, thethrowsAmatches the exception, and thepredicatematches the custom condition . - The async support is the built-in. The test callback is the async, the
awaitworks, and theexpectLaterhandles the asynchronous matchers. Thecompletionmatches the future, thethrowsAmatches the future error, and theemitsInOrdermatches the stream sequence . - The
setUpand thetearDownrun the per-test code. ThesetUpAlland thetearDownAllrun the per-group code. Thelatevariable is declared in the group scope, and thesetUpassigns it . - The arrange-act-assert is the test structure. The arrange sets the state, the act performs the operation, and the assert verifies the result. The three parts are the readable and the consistent .
- The descriptive names are the documentation. The test description is the behavior, and the failure message is the readable. The “the add adds the item” is the better than the “testAdd” .
- The test files mirror the lib structure. The
testdirectory contains the_test.dartfiles, and thedart testcommand discovers them. Thedart test path/to/file.dartruns the specific file, and the-nruns the matching tests . - The tests are the safety net. The test catches the regression, the test documents the behavior, and the test enables the refactoring. The result is the confident development, which is the foundation of the reliable code.
Remember: The package:test is the standard Dart testing framework. The test defines the tests, the group groups them, and the expect asserts the values. The setUp and the tearDown manage the fixtures, and the expectLater handles the async. The matchers โ the equals, the isA, the throwsA, the contains, the hasLength, the predicate, the completion, the emitsInOrder โ are the assertions. The result is the tested, the documented, and the maintainable code, which is the foundation of the professional development.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!