60 Unit Testing
I’ll create a comprehensive tutorial on writing unit tests in Dart that’s tailored to your programming experience. Since you’re familiar with testing concepts from Python and R, I’ll focus on Dart-specific patterns and best practices.
60.1 Unit Testing in Dart: A Quick Tutorial
Unit testing in Dart is straightforward and follows patterns similar to other testing frameworks you might know from Python (like pytest or unittest).
60.1.1 Setting Up Testing
First, add the test package to your pubspec.yaml
:
dev_dependencies:
test: ^1.24.0
60.1.2 Basic Test Structure
Create test files in the test/
directory with _test.dart
suffix:
// test/calculator_test.dart
import 'package:test/test.dart';
import '../lib/calculator.dart';
void main() {
'Calculator adds two numbers', () {
test(final calculator = Calculator();
.add(2, 3), equals(5));
expect(calculator});
}
60.1.3 Key Testing Components
The main components you’ll use:
test()
- Defines a single test casegroup()
- Groups related tests (like Python’s test classes)expect()
- Makes assertions (similar to assert in Python)setUp()
/tearDown()
- Run before/after each test
60.1.4 Example: Testing a Simple Class
Let’s create a more complete example. First, the class to test:
// lib/user.dart
class User {
final String name;
final int age;
{required this.name, required this.age});
User(
bool get isAdult => age >= 18;
String greet() => 'Hello, my name is $name';
=> User(name: name, age: age + 1);
User birthday() }
Now the comprehensive test:
// test/user_test.dart
import 'package:test/test.dart';
import '../lib/user.dart';
void main() {
'User', () {
group(late User user;
{
setUp(() // Runs before each test
= User(name: 'Alice', age: 25);
user });
'should create user with correct properties', () {
test(.name, equals('Alice'));
expect(user.age, equals(25));
expect(user});
'should identify adult correctly', () {
test(.isAdult, isTrue);
expect(user
final teenager = User(name: 'Bob', age: 16);
.isAdult, isFalse);
expect(teenager});
'should greet properly', () {
test(.greet(), equals('Hello, my name is Alice'));
expect(user});
'birthday should increment age', () {
test(final olderUser = user.birthday();
.age, equals(26));
expect(olderUser.name, equals('Alice'));
expect(olderUser});
});
}
60.1.5 Common Matchers
Dart provides many matchers for assertions:
// Equality
, equals(expected));
expect(actual, expected); // shorthand
expect(actual
// Boolean
, isTrue);
expect(value, isFalse);
expect(value
// Null
, isNull);
expect(value, isNotNull);
expect(value
// Types
, isA<String>());
expect(value, isA<List<int>>());
expect(value
// Numbers
, greaterThan(10));
expect(value, lessThanOrEqualTo(20));
expect(value, closeTo(3.14, 0.01)); // within tolerance
expect(value
// Collections
, contains('item'));
expect(list, hasLength(3));
expect(list, isEmpty);
expect(list, isNotEmpty);
expect(list
// Exceptions
=> dangerousCode(), throwsException);
expect(() => divideByZero(), throwsA(isA<ArgumentError>())); expect(()
60.1.6 Testing Async Code
For async operations (similar to Python’s async testing):
'async operation completes', () async {
test(final future = fetchData();
await expectLater(future, completes);
});
'async function returns correct value', () async {
test(final result = await fetchUserName();
, equals('Alice'));
expect(result});
'stream emits values', () async {
test(final stream = countStream();
, emitsInOrder([1, 2, 3]));
expect(stream});
60.1.7 Running Tests
Run tests from the command line:
# Run all tests
dart test
# Run specific test file
dart test test/user_test.dart
# Run with coverage
dart test --coverage=coverage
# Watch mode (re-runs on file changes)
dart test --reporter compact --watch
60.1.8 Best Practices
Test Organization:
project/ ├── lib/ │ ├── src/ │ │ └── calculator.dart │ └── user.dart └── test/ ├── src/ │ └── calculator_test.dart └── user_test.dart
Descriptive Test Names:
'should throw ArgumentError when dividing by zero', () { test(// Better than: test('division test', () { });
Arrange-Act-Assert Pattern:
'should calculate discount correctly', () { test(// Arrange final product = Product(price: 100); // Act final discountedPrice = product.applyDiscount(0.2); // Assert , equals(80)); expect(discountedPrice});
60.1.9 Flutter-Specific Testing
For Flutter widgets, you’ll use flutter_test
:
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
void main() {
'Counter increments', (WidgetTester tester) async {
testWidgets(// Build widget
await tester.pumpWidget(MyApp());
// Find widgets
.text('0'), findsOneWidget);
expect(find
// Interact
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify
.text('1'), findsOneWidget);
expect(find});
}
60.1.10 Try It Yourself
Create a simple StringUtils
class and write tests for it:
// lib/string_utils.dart
class StringUtils {
static String capitalize(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1).toLowerCase();
}
static int wordCount(String text) {
return text.trim().split(RegExp(r'\s+')).length;
}
}