Dart Unit Testing
Unit testing is the fundamental means of ensuring code quality.\\n\\nIt verifies whether the behavior of each smallest functional unit (usually a function or method) meets expectations.\\n\\nThis chapter introduces the usage of Dart's test package, writing test() test cases, expect assertions, and group() grouped testing.\\n\\n* * *\\n\\n## Installation and Configuration of the test Package\\n\\nDart officially provides the test package for writing and running tests.\\n\\nFirst, add the dependency in pubspec.yaml:\\n\\n# File path: pubspec.yaml dev_dependencies: test: ^1.24.0\\nThen run the following command to install:\\n\\n$ dart pub get\\nTest files are usually placed in the project's test/ directory, with filenames ending in _test.dart.\\n\\nA typical project structure:\\n\\nmy_project/βββ lib/β βββ calculator.dart # Code under testβββ test/β βββ calculator_test.dart # Test fileβββ pubspec.yaml\\n\\n* * *\\n\\n## Writing test() Test Cases\\n\\nThe test() function is the basic unit for writing test cases.\\n\\nIt receives two parameters: a test description (string) and a test function body.\\n\\n## Example\\n\\nCode under test (lib/calculator.dart):\\n\\n// File path: lib/calculator.dart\\n\\nclass Calculator {\\n\\nint add(int a,int b)=> a + b;\\n\\nint subtract(int a,int b)=> a - b;\\n\\nint multiply(int a,int b)=> a * b;\\n\\ndouble divide(int a,int b){\\n\\nif(b ==0){\\n\\nthrow ArgumentError('Divisor cannot be 0');\\n\\n}\\n\\nreturn a / b;\\n\\n}\\n\\nbool isEven(int n)=> n %2==0;\\n\\nList filterPositive(List numbers){\\n\\nreturn numbers.where((n)=> n >0).toList();\\n\\n}\\n\\n}\\n\\n## Example\\n\\nTest file (test/calculator_test.dart):\\n\\n// File path: test/calculator_test.dart\\n\\nimport'package:test/test.dart';\\n\\nimport'package:my_project/calculator.dart';\\n\\nvoid main(){\\n\\n// Create a Calculator instance for testing\\n\\nvar calculator = Calculator();\\n\\n// test() function: first parameter is the test description, second is the test function\\n\\n test('add() Adding two positive numbers',(){\\n\\nvar result = calculator.add(3,5);\\n\\n// expect() assertion: verify if the actual result equals the expected value\\n\\n expect(result, equals(8));\\n\\n});\\n\\ntest('add() Addition including negative numbers',(){\\n\\n expect(calculator.add(-3,5), equals(2));\\n\\n expect(calculator.add(-3,-5), equals(-8));\\n\\n});\\n\\ntest('subtract() Subtraction operation',(){\\n\\n expect(calculator.subtract(10,3), equals(7));\\n\\n});\\n\\ntest('multiply() Multiplication operation',(){\\n\\n expect(calculator.multiply(4,5), equals(20));\\n\\n// Multiply by zero\\n\\n expect(calculator.multiply(100,0), equals(0));\\n\\n});\\n\\ntest('divide() Normal Division',(){\\n\\n expect(calculator.divide(10,2), equals(5.0));\\n\\n expect(calculator.divide(7,2), equals(3.5));\\n\\n});\\n\\ntest('isEven() Even Number Check',(){\\n\\n expect(calculator.isEven(4), isTrue);\\n\\n expect(calculator.isEven(5), isFalse);\\n\\n});\\n\\n}\\n\\nRun tests:\\n\\n$ dart test 00:00 +6: All tests passed!\\n\\n* * *\\n\\n## expect Assertions\\n\\nexpect() is the most core function in testing, verifying whether the actual value satisfies a certain condition.\\n\\nBasic syntax: expect(actual, matcher).\\n\\n### Common Matchers\\n\\n| Matcher | Purpose | Example |\\n| --- | --- | --- |\\n| equals(expected) | Verify value equality | expect(result, equals(42)) |\\n| isTrue / isFalse | Verify boolean values | expect(flag, isTrue) |\\n| isNull / isNotNull | Verify null | expect(value, isNull) |\\n| contains(value) | Contains an element (list) or substring (string) | expect(list, contains('a')) |\\n| isA() | Verify type | expect(obj, isA()) |\\n| throws() | Verify exception thrown | expect(() => f(), throwsException) |\\n| isNotEmpty | Non-empty | expect(list, isNotEmpty) |\\n| hasLength(n) | Verify length | expect(list, hasLength(3)) |\\n| greaterThan(n) | Greater than | expect(score, greaterThan(60)) |\\n| closeTo(value, delta) | Floating point approximate equality | expect(3.14, closeTo(3.1, 0.1)) |\\n\\n## Example\\n\\nPractical applications of various Matchers:\\n\\nimport'package:test/test.dart';\\n\\nvoid main(){\\n\\n test('TUTORIAL Various assertion examples',(){\\n\\n// Basic equality\\n\\n expect(2+2, equals(4));\\n\\n// Boolean values\\n\\n expect('hello'.contains('h'), isTrue);\\n\\n expect(''.isEmpty, isTrue);\\n\\n// null check\\n\\n String? name;\\n\\n expect(name, isNull);\\n\\n name ='TUTORIAL';\\n\\n expect(name, isNotNull);\\n\\n// Type check\\n\\n expect('TUTORIAL', isA());\\n\\n expect(42, isA());\\n\\n// List/string contains\\n\\n expect([1,2,3], contains(2));\\n\\n expect('Hello, TUTORIAL!', contains('TUTORIAL'));\\n\\n// Length\\n\\n expect([1,2,3], hasLength(3));\\n\\n expect('Dart', hasLength(4));\\n\\n// Numeric comparison\\n\\n expect(100, greaterThan(50));\\n\\n expect(30, lessThan(60));\\n\\n expect(75, greaterThanOrEqualTo(60));\\n\\n// Floating point comparison (avoid precision issues)\\n\\n expect(0.1+0.2, closeTo(0.3,0.001));\\n\\n// Collection non-empty\\n\\n expect([1,2], isNotEmpty);\\n\\n// List equality\\n\\n expect([1,2,3], equals([1,2,3]));\\n\\n// Map contains a key\\n\\nvar user ={'name':'tutorial','age':10};\\n\\n expect(user, containsPair('name','tutorial'));\\n\\n expect(user.keys, contains('age'));\\n\\n});\\n\\n}\\n\\n00:00 +1: All tests passed!\\n### Testing Exceptions\\n\\n## Example\\n\\nimport'package:test/test.dart';\\n\\nint divide(int a,int b){\\n\\nif(b ==0)throw ArgumentError('Divisor cannot be 0');\\n\\nreturn a ~/ b;\\n\\n}\\n\\nvoid main(){\\n\\n test('divide() Division by zero should throw an exception',(){\\n\\n// Verify throwing any exception\\n\\n expect(()=> divide(10,0), throwsException);\\n\\n// Verify throwing a specific type of exception\\n\\n expect(()=> divide(10,0), throwsArgumentError);\\n\\n// Verify exception message\\n\\n expect(\\n\\n()=> divide(10,0),\\n\\n throwsA(predicate((e)=>\\n\\n e is ArgumentError &&\\n\\n e.message.contains('Divisor'))),\\n\\n);\\n\\n});\\n\\ntest('divide() No exception thrown in normal case',(){\\n\\n expect(()=> divide(10,2), returnsNormally);\\n\\n expect(divide(10,2), equals(5));\\n\\n});\\n\\n}\\n\\n00:00 +2: All tests passed!\\n> When testing exceptions, you need to wrap the code that might throw an exception in an anonymous function (() => code), rather than calling it directly. If you write expect(divide(10, 0), throwsException) directly, divide will throw the exception immediately, and expect won't have a chance to execute.\\n\\n* * *\\n\\n## Grouped Testing with group()\\n\\ngroup() is used to organize related test cases together, making the test structure clearer.\\n\\n## Example\\n\\nimport'package:test/test.dart';\\n\\n// Function under test\\n\\nclass StringUtils {\\n\\nstatic String capitalize(String s){\\n\\nif(s.isEmpty)return s;\\n\\nreturn s.toUpperCase()+ s.substring(1);\\n\\n}\\n\\nstatic String reverse(String s){\\n\\nreturn s.split('').reversed.join('');\\n\\n}\\n\\nstatic bool isPalindrome(String s){\\n\\nvar clean = s.toLowerCase().replaceAll(' ','');\\n\\nreturn clean == reverse(clean);\\n\\n}\\n\\nstatic int countWords(String s){\\n\\nif(s.trim().isEmpty)return 0;\\n\\nreturn s.trim().split(RegExp(r'\\\\s+')).length;\\n\\n}\\n\\n}\\n\\nvoid main(){\\n\\n// group() nested organization of tests\\n\\n group('StringUtils',(){\\n\\n// Sub-group\\n\\n group('capitalize()',(){\\n\\n test('Capitalize first letter of a normal word',(){\\n\\n expect(StringUtils.capitalize('hello'), equals('Hello'));\\n\\n});\\n\\ntest('Already capitalized word remains unchanged',(){\\n\\n expect(StringUtils.capitalize('Hello'), equals('Hello'));\\n\\n});\\n\\ntest('Empty stringReturns empty string',(){\\n\\n expect(StringUtils.capitalize(''), equals(''));\\n\\n});\\n\\ntest('Single character',(){\\n\\n expect(StringUtils.capitalize('a'), equals('A'));\\n\\n});\\n\\n});\\n\\ngroup('reverse()',(){\\n\\n test('Reverse Normal String',(){\\n\\n expect(StringUtils.reverse('TUTORIAL'), equals('BOONUR'));\\n\\n});\\n\\ntest('Reversed palindrome string remains unchanged',(){\\n\\n expect(StringUtils.reverse('aba'), equals('aba'));\\n\\n});\\n\\ntest('Empty string',(){\\n\\n expect(StringUtils.reverse(''), equals(''));\\n\\n});\\n\\n});\\n\\ngroup('isPalindrome()',(){\\n\\n test('Palindrome string returns true',(){\\n\\n expect(StringUtils.isPalindrome('racecar'), isTrue);\\n\\n expect(StringUtils.isPalindrome('A man a plan a canal Panama'), isTrue);\\n\\n});\\n\\ntest('Non-palindrome string returns false',(){\\n\\n expect(StringUtils.isPalindrome('hello'), isFalse);\\n\\n});\\n\\ntest('Empty stringTreat as palindrome',(){\\n\\n expect(StringUtils.isPalindrome(''), isTrue);\\n\\n});\\n\\n});\\n\\ngroup('countWords()',(){\\n\\n test('Normal sentence',(){\\n\\n expect(StringUtils.countWords('Hello World Dart'), equals(3));\\n\\n});\\n\\ntest('Extra Spaces',(){\\n\\n expect(StringUtils.countWords(' Hello World '), equals(2));\\n\\n});\\n\\ntest('Empty string',(){\\n\\n expect(StringUtils.countWords(''), equals(0));\\n\\n expect(StringUtils.countWords(' '), equals(0));\\n\\n});\\n\\n});\\n\\n});\\n\\n}\\n\\n00:00 +12: All tests passed!\\ngroup() can be nested to create a hierarchical test structure.\\n\\nThis makes test reports more readable and makes it easier to quickly locate failed tests.\\n\\n> A good grouping strategy is to organize by "module/class/method under test". This way, when a test fails, you can immediately know which functionality has a problem.\\n\\n### setUp and tearDown\\n\\nsetUp runs before each test, and tearDown runs after each test.\\n\\nThey are used to prepare the test environment and clean up resources.\\n\\n## Example\\n\\nimport'package:test/test.dart';\\n\\n// Simulate a class that needs initialization and cleanup\\n\\nclass Database {\\n\\nbool isConnected =false;\\n\\nvoid connect(){\\n\\n isConnected =true;\\n\\n print(' Database connected');\\n\\n}\\n\\nvoid disconnect(){\\n\\n isConnected =false;\\n\\n print(' Database disconnected');\\n\\n}\\n\\nString query(String sql){\\n\\nif(!isConnected)throw Exception('Database not connected');\\n\\nreturn'Query result: $sql';\\n\\n}\\n\\n}\\n\\nvoid main(){\\n\\n group('Database Test',(){\\n\\n late Database db;// late delayed initialization\\n\\n// Execute before each test\\n\\n setUp((){\\n\\n db = Database();\\n\\n db.connect();\\n\\n print(' Set up test environment');\\n\\n});\\n\\n// Execute after each test\\n\\n tearDown((){\\n\\n db.disconnect();\\n\\n print(' Clean up test environment');\\n\\n});\\n\\ntest('query() Normal query',(){\\n\\nvar result = db.query('SELECT * FROM users');\\n\\n expect(result, contains('TUTORIAL')? result.contains('users'): result.contains('users'));\\n\\n// Simplified assertion\\n\\n expect(result, isNotEmpty);\\n\\n});\\n\\ntest('query() Multiple queries can be executed after connection',(){\\n\\nvar r1 = db.query('SELECT 1');\\n\\nvar r2 = db.query('SELECT 2');\\n\\n expect(r1, isNotEmpty);\\n\\n expect(r2, isNotEmpty);\\n\\n});\\n\\ntest('Query Throws Exception When Not Connected',(){\\n\\n db.disconnect();// Manually disconnect\\n\\n expect(()=> db.query('SELECT 1'), throwsException);\\n\\n});\\n\\n});\\n\\n}\\n\\n Database connected Set up test environment Database disconnected Clean up test environment Database connected Set up test environment Database disconnected Clean up test environment Database connected Set up test environment Database disconnected Clean up test environment00:00 +3: All tests passed!\\nNote the execution order of setUp and tearDown in the outputβthey execute once before and after each test.\\n\\n> setUp and tearDown ensure that each test starts from a clean state, unaffected by other tests. This is the key guarantee of test independence.\\n\\n* * *\\n\\n## Asynchronous Testing\\n\\nThe Dart test package natively supports asynchronous testing.\\n\\nThe test function can return a Future, and the framework will automatically wait for the Future to complete.\\n\\n## Example\\n\\nim
YouTip