[{"data":1,"prerenderedAt":4561},["ShallowReactive",2],{"page-\u002Fdart\u002F14-testing":3},{"id":4,"title":5,"body":6,"description":4554,"extension":4555,"meta":4556,"navigation":63,"path":4557,"seo":4558,"stem":4559,"__hash__":4560},"content\u002Fdart\u002F14-testing.md","Dart — Testing, Mocking & Async Test Patterns",{"type":7,"value":8,"toc":4541},"minimark",[9,13,29,644,648,1761,1765,2221,2229,2927,2931,3378,3382,4002,4006,4116,4120,4312,4316,4320,4371,4374,4537],[10,11,5],"h1",{"id":12},"dart-testing-mocking-async-test-patterns",[14,15,17,18,22,23,22,26],"h2",{"id":16},"test-structure-group-setup-teardown","Test Structure — ",[19,20,21],"code",{},"group",", ",[19,24,25],{},"setUp",[19,27,28],{},"tearDown",[30,31,33],"code-wrapper",{"language":32},"dart",[34,35,39],"pre",{"className":36,"code":37,"language":32,"meta":38,"style":38},"language-dart shiki shiki-themes github-light github-dark","import 'package:test\u002Ftest.dart';\n\nvoid main() {\n  \u002F\u002F Group organizes related tests — output shows the hierarchy.\n  group('Stack', () {\n    \u002F\u002F setUp runs BEFORE EACH test (fresh state — no inter-test coupling).\n    late Stack\u003Cint> stack;\n\n    setUp(() {\n      stack = Stack\u003Cint>();\n      stack.push(1);\n      stack.push(2);\n    });\n\n    tearDown(() {\n      \u002F\u002F cleanup (close connections, reset state) — runs after each test\n      \u002F\u002F even on failure. Can be async: tearDown(() async { ... });\n    });\n\n    test('pop returns the top item', () {\n      expect(stack.pop(), equals(2));\n      expect(stack.pop(), equals(1));\n    });\n\n    test('pop on empty throws StateError', () {\n      stack.pop();\n      stack.pop();  \u002F\u002F now empty\n      expect(() => stack.pop(), throwsStateError);\n    });\n\n    test('peek does not remove the item', () {\n      expect(stack.peek(), equals(2));\n      expect(stack.length, equals(2));  \u002F\u002F unchanged\n    });\n\n    test('isEmpty reflects state', () {\n      expect(stack.isEmpty, isFalse);\n      stack.pop();\n      stack.pop();\n      expect(stack.isEmpty, isTrue);\n    });\n  });\n\n  \u002F\u002F setUpAll runs ONCE before all tests (not per test) — for expensive setup.\n  \u002F\u002F But state isn't reset between tests — use setUp for fresh state.\n  group('Database', () {\n    late Database db;\n\n    setUpAll(() async {\n      db = await Database.connect(testDbPath);  \u002F\u002F once\n    });\n\n    tearDownAll(() async {\n      await db.close();\n    });\n\n    test('insert and query', () async { \u002F* ... *\u002F });\n  });\n}\n","",[19,40,41,58,65,78,85,100,106,125,130,139,157,174,188,194,199,207,213,219,224,229,242,267,286,291,296,308,318,331,350,355,360,372,392,412,417,422,434,442,451,460,468,473,479,484,490,496,508,519,524,538,563,568,573,585,599,604,609,633,638],{"__ignoreMap":38},[42,43,46,50,54],"span",{"class":44,"line":45},"line",1,[42,47,49],{"class":48},"svdQ7","import",[42,51,53],{"class":52},"sJ6F3"," 'package:test\u002Ftest.dart'",[42,55,57],{"class":56},"ssxIu",";\n",[42,59,61],{"class":44,"line":60},2,[42,62,64],{"emptyLinePlaceholder":63},true,"\n",[42,66,68,71,75],{"class":44,"line":67},3,[42,69,70],{"class":48},"void",[42,72,74],{"class":73},"sIsaT"," main",[42,76,77],{"class":56},"() {\n",[42,79,81],{"class":44,"line":80},4,[42,82,84],{"class":83},"sdCPZ","  \u002F\u002F Group organizes related tests — output shows the hierarchy.\n",[42,86,88,91,94,97],{"class":44,"line":87},5,[42,89,90],{"class":73},"  group",[42,92,93],{"class":56},"(",[42,95,96],{"class":52},"'Stack'",[42,98,99],{"class":56},", () {\n",[42,101,103],{"class":44,"line":102},6,[42,104,105],{"class":83},"    \u002F\u002F setUp runs BEFORE EACH test (fresh state — no inter-test coupling).\n",[42,107,109,112,116,119,122],{"class":44,"line":108},7,[42,110,111],{"class":48},"    late",[42,113,115],{"class":114},"snvgF"," Stack",[42,117,118],{"class":56},"\u003C",[42,120,121],{"class":114},"int",[42,123,124],{"class":56},"> stack;\n",[42,126,128],{"class":44,"line":127},8,[42,129,64],{"emptyLinePlaceholder":63},[42,131,133,136],{"class":44,"line":132},9,[42,134,135],{"class":73},"    setUp",[42,137,138],{"class":56},"(() {\n",[42,140,142,145,148,150,152,154],{"class":44,"line":141},10,[42,143,144],{"class":56},"      stack ",[42,146,147],{"class":48},"=",[42,149,115],{"class":114},[42,151,118],{"class":56},[42,153,121],{"class":114},[42,155,156],{"class":56},">();\n",[42,158,160,163,166,168,171],{"class":44,"line":159},11,[42,161,162],{"class":56},"      stack.",[42,164,165],{"class":73},"push",[42,167,93],{"class":56},[42,169,170],{"class":114},"1",[42,172,173],{"class":56},");\n",[42,175,177,179,181,183,186],{"class":44,"line":176},12,[42,178,162],{"class":56},[42,180,165],{"class":73},[42,182,93],{"class":56},[42,184,185],{"class":114},"2",[42,187,173],{"class":56},[42,189,191],{"class":44,"line":190},13,[42,192,193],{"class":56},"    });\n",[42,195,197],{"class":44,"line":196},14,[42,198,64],{"emptyLinePlaceholder":63},[42,200,202,205],{"class":44,"line":201},15,[42,203,204],{"class":73},"    tearDown",[42,206,138],{"class":56},[42,208,210],{"class":44,"line":209},16,[42,211,212],{"class":83},"      \u002F\u002F cleanup (close connections, reset state) — runs after each test\n",[42,214,216],{"class":44,"line":215},17,[42,217,218],{"class":83},"      \u002F\u002F even on failure. Can be async: tearDown(() async { ... });\n",[42,220,222],{"class":44,"line":221},18,[42,223,193],{"class":56},[42,225,227],{"class":44,"line":226},19,[42,228,64],{"emptyLinePlaceholder":63},[42,230,232,235,237,240],{"class":44,"line":231},20,[42,233,234],{"class":73},"    test",[42,236,93],{"class":56},[42,238,239],{"class":52},"'pop returns the top item'",[42,241,99],{"class":56},[42,243,245,248,251,254,257,260,262,264],{"class":44,"line":244},21,[42,246,247],{"class":73},"      expect",[42,249,250],{"class":56},"(stack.",[42,252,253],{"class":73},"pop",[42,255,256],{"class":56},"(), ",[42,258,259],{"class":73},"equals",[42,261,93],{"class":56},[42,263,185],{"class":114},[42,265,266],{"class":56},"));\n",[42,268,270,272,274,276,278,280,282,284],{"class":44,"line":269},22,[42,271,247],{"class":73},[42,273,250],{"class":56},[42,275,253],{"class":73},[42,277,256],{"class":56},[42,279,259],{"class":73},[42,281,93],{"class":56},[42,283,170],{"class":114},[42,285,266],{"class":56},[42,287,289],{"class":44,"line":288},23,[42,290,193],{"class":56},[42,292,294],{"class":44,"line":293},24,[42,295,64],{"emptyLinePlaceholder":63},[42,297,299,301,303,306],{"class":44,"line":298},25,[42,300,234],{"class":73},[42,302,93],{"class":56},[42,304,305],{"class":52},"'pop on empty throws StateError'",[42,307,99],{"class":56},[42,309,311,313,315],{"class":44,"line":310},26,[42,312,162],{"class":56},[42,314,253],{"class":73},[42,316,317],{"class":56},"();\n",[42,319,321,323,325,328],{"class":44,"line":320},27,[42,322,162],{"class":56},[42,324,253],{"class":73},[42,326,327],{"class":56},"();  ",[42,329,330],{"class":83},"\u002F\u002F now empty\n",[42,332,334,336,339,342,345,347],{"class":44,"line":333},28,[42,335,247],{"class":73},[42,337,338],{"class":56},"(() ",[42,340,341],{"class":48},"=>",[42,343,344],{"class":56}," stack.",[42,346,253],{"class":73},[42,348,349],{"class":56},"(), throwsStateError);\n",[42,351,353],{"class":44,"line":352},29,[42,354,193],{"class":56},[42,356,358],{"class":44,"line":357},30,[42,359,64],{"emptyLinePlaceholder":63},[42,361,363,365,367,370],{"class":44,"line":362},31,[42,364,234],{"class":73},[42,366,93],{"class":56},[42,368,369],{"class":52},"'peek does not remove the item'",[42,371,99],{"class":56},[42,373,375,377,379,382,384,386,388,390],{"class":44,"line":374},32,[42,376,247],{"class":73},[42,378,250],{"class":56},[42,380,381],{"class":73},"peek",[42,383,256],{"class":56},[42,385,259],{"class":73},[42,387,93],{"class":56},[42,389,185],{"class":114},[42,391,266],{"class":56},[42,393,395,397,400,402,404,406,409],{"class":44,"line":394},33,[42,396,247],{"class":73},[42,398,399],{"class":56},"(stack.length, ",[42,401,259],{"class":73},[42,403,93],{"class":56},[42,405,185],{"class":114},[42,407,408],{"class":56},"));  ",[42,410,411],{"class":83},"\u002F\u002F unchanged\n",[42,413,415],{"class":44,"line":414},34,[42,416,193],{"class":56},[42,418,420],{"class":44,"line":419},35,[42,421,64],{"emptyLinePlaceholder":63},[42,423,425,427,429,432],{"class":44,"line":424},36,[42,426,234],{"class":73},[42,428,93],{"class":56},[42,430,431],{"class":52},"'isEmpty reflects state'",[42,433,99],{"class":56},[42,435,437,439],{"class":44,"line":436},37,[42,438,247],{"class":73},[42,440,441],{"class":56},"(stack.isEmpty, isFalse);\n",[42,443,445,447,449],{"class":44,"line":444},38,[42,446,162],{"class":56},[42,448,253],{"class":73},[42,450,317],{"class":56},[42,452,454,456,458],{"class":44,"line":453},39,[42,455,162],{"class":56},[42,457,253],{"class":73},[42,459,317],{"class":56},[42,461,463,465],{"class":44,"line":462},40,[42,464,247],{"class":73},[42,466,467],{"class":56},"(stack.isEmpty, isTrue);\n",[42,469,471],{"class":44,"line":470},41,[42,472,193],{"class":56},[42,474,476],{"class":44,"line":475},42,[42,477,478],{"class":56},"  });\n",[42,480,482],{"class":44,"line":481},43,[42,483,64],{"emptyLinePlaceholder":63},[42,485,487],{"class":44,"line":486},44,[42,488,489],{"class":83},"  \u002F\u002F setUpAll runs ONCE before all tests (not per test) — for expensive setup.\n",[42,491,493],{"class":44,"line":492},45,[42,494,495],{"class":83},"  \u002F\u002F But state isn't reset between tests — use setUp for fresh state.\n",[42,497,499,501,503,506],{"class":44,"line":498},46,[42,500,90],{"class":73},[42,502,93],{"class":56},[42,504,505],{"class":52},"'Database'",[42,507,99],{"class":56},[42,509,511,513,516],{"class":44,"line":510},47,[42,512,111],{"class":48},[42,514,515],{"class":114}," Database",[42,517,518],{"class":56}," db;\n",[42,520,522],{"class":44,"line":521},48,[42,523,64],{"emptyLinePlaceholder":63},[42,525,527,530,532,535],{"class":44,"line":526},49,[42,528,529],{"class":73},"    setUpAll",[42,531,338],{"class":56},[42,533,534],{"class":48},"async",[42,536,537],{"class":56}," {\n",[42,539,541,544,546,549,551,554,557,560],{"class":44,"line":540},50,[42,542,543],{"class":56},"      db ",[42,545,147],{"class":48},[42,547,548],{"class":48}," await",[42,550,515],{"class":114},[42,552,553],{"class":56},".",[42,555,556],{"class":73},"connect",[42,558,559],{"class":56},"(testDbPath);  ",[42,561,562],{"class":83},"\u002F\u002F once\n",[42,564,566],{"class":44,"line":565},51,[42,567,193],{"class":56},[42,569,571],{"class":44,"line":570},52,[42,572,64],{"emptyLinePlaceholder":63},[42,574,576,579,581,583],{"class":44,"line":575},53,[42,577,578],{"class":73},"    tearDownAll",[42,580,338],{"class":56},[42,582,534],{"class":48},[42,584,537],{"class":56},[42,586,588,591,594,597],{"class":44,"line":587},54,[42,589,590],{"class":48},"      await",[42,592,593],{"class":56}," db.",[42,595,596],{"class":73},"close",[42,598,317],{"class":56},[42,600,602],{"class":44,"line":601},55,[42,603,193],{"class":56},[42,605,607],{"class":44,"line":606},56,[42,608,64],{"emptyLinePlaceholder":63},[42,610,612,614,616,619,622,624,627,630],{"class":44,"line":611},57,[42,613,234],{"class":73},[42,615,93],{"class":56},[42,617,618],{"class":52},"'insert and query'",[42,620,621],{"class":56},", () ",[42,623,534],{"class":48},[42,625,626],{"class":56}," { ",[42,628,629],{"class":83},"\u002F* ... *\u002F",[42,631,632],{"class":56}," });\n",[42,634,636],{"class":44,"line":635},58,[42,637,478],{"class":56},[42,639,641],{"class":44,"line":640},59,[42,642,643],{"class":56},"}\n",[14,645,647],{"id":646},"matchers-composition-custom","Matchers — Composition & Custom",[30,649,650],{"language":32},[34,651,653],{"className":36,"code":652,"language":32,"meta":38,"style":38},"import 'package:test\u002Ftest.dart';\n\nvoid main() {\n  test('equality matchers', () {\n    expect(42, equals(42));\n    expect([1, 2, 3], equals([1, 2, 3]));\n    expect({'a': 1}, equals({'a': 1}));\n    expect(true, isTrue);\n    expect(null, isNull);\n    expect(42, isNotNull);\n    expect([1, 2], isNotEmpty);\n    expect('', isEmpty);\n  });\n\n  test('type matchers', () {\n    expect(42, isA\u003Cint>());\n    expect('hello', isA\u003CString>());\n    expect([1, 2], isA\u003CList\u003Cint>>());\n  });\n\n  test('collection matchers', () {\n    expect([1, 2, 3], contains(2));\n    expect([1, 2, 3], containsAll([2, 3]));  \u002F\u002F order-independent\n    expect([1, 2, 3], hasLength(3));\n    expect([1, 2, 3], everyElement(isA\u003Cint>()));\n    expect([1, 2, 3], anyElement(greaterThan(2)));\n  });\n\n  test('string matchers', () {\n    expect('hello world', startsWith('hello'));\n    expect('hello world', endsWith('world'));\n    expect('hello world', contains('lo wo'));\n    expect('hello', matches(RegExp(r'^[a-z]+$')));\n  });\n\n  test('numeric matchers', () {\n    expect(3.14159, closeTo(3.14, 0.01));  \u002F\u002F |actual - expected| \u003C= tolerance\n    expect(42, greaterThan(10));\n    expect(42, lessThan(100));\n    expect(42, inInclusiveRange(1, 100));\n  });\n\n  test('composing matchers with allOf \u002F anyOf', () {\n    expect(42, allOf([\n      greaterThan(10),\n      lessThan(100),\n      isA\u003Cint>(),\n    ]));\n\n    expect(-1, anyOf([\n      greaterThan(0),\n      lessThan(0),  \u002F\u002F this one matches\n    ]));\n  });\n\n  test('custom predicate matcher', () {\n    bool isPrime(int n) {\n      if (n \u003C 2) return false;\n      for (var i = 2; i * i \u003C= n; i++) {\n        if (n % i == 0) return false;\n      }\n      return true;\n    }\n\n    expect(17, predicate(isPrime, 'is a prime number'));\n    expect(18, isNot(predicate(isPrime, 'is a prime number')));\n  });\n\n  test('exception matchers', () {\n    expect(\n      () => throw ArgumentError('bad'),\n      throwsA(allOf([\n        isA\u003CArgumentError>(),\n        predicate((ArgumentError e) => e.message == 'bad'),\n      ])),\n    );\n  });\n}\n",[19,654,655,663,667,675,687,707,745,777,789,801,812,827,839,843,847,858,878,898,926,930,934,945,972,1007,1034,1066,1099,1103,1107,1118,1138,1158,1177,1202,1206,1210,1221,1250,1269,1289,1312,1316,1320,1331,1347,1359,1370,1382,1387,1391,1409,1420,1434,1438,1442,1446,1457,1472,1496,1534,1561,1567,1578,1584,1589,1612,1637,1642,1647,1659,1667,1688,1700,1713,1739,1745,1751,1756],{"__ignoreMap":38},[42,656,657,659,661],{"class":44,"line":45},[42,658,49],{"class":48},[42,660,53],{"class":52},[42,662,57],{"class":56},[42,664,665],{"class":44,"line":60},[42,666,64],{"emptyLinePlaceholder":63},[42,668,669,671,673],{"class":44,"line":67},[42,670,70],{"class":48},[42,672,74],{"class":73},[42,674,77],{"class":56},[42,676,677,680,682,685],{"class":44,"line":80},[42,678,679],{"class":73},"  test",[42,681,93],{"class":56},[42,683,684],{"class":52},"'equality matchers'",[42,686,99],{"class":56},[42,688,689,692,694,697,699,701,703,705],{"class":44,"line":87},[42,690,691],{"class":73},"    expect",[42,693,93],{"class":56},[42,695,696],{"class":114},"42",[42,698,22],{"class":56},[42,700,259],{"class":73},[42,702,93],{"class":56},[42,704,696],{"class":114},[42,706,266],{"class":56},[42,708,709,711,714,716,718,720,722,725,728,730,732,734,736,738,740,742],{"class":44,"line":102},[42,710,691],{"class":73},[42,712,713],{"class":56},"([",[42,715,170],{"class":114},[42,717,22],{"class":56},[42,719,185],{"class":114},[42,721,22],{"class":56},[42,723,724],{"class":114},"3",[42,726,727],{"class":56},"], ",[42,729,259],{"class":73},[42,731,713],{"class":56},[42,733,170],{"class":114},[42,735,22],{"class":56},[42,737,185],{"class":114},[42,739,22],{"class":56},[42,741,724],{"class":114},[42,743,744],{"class":56},"]));\n",[42,746,747,749,752,755,758,761,764,766,768,770,772,774],{"class":44,"line":108},[42,748,691],{"class":73},[42,750,751],{"class":56},"({",[42,753,754],{"class":52},"'a'",[42,756,757],{"class":48},":",[42,759,760],{"class":114}," 1",[42,762,763],{"class":56},"}, ",[42,765,259],{"class":73},[42,767,751],{"class":56},[42,769,754],{"class":52},[42,771,757],{"class":48},[42,773,760],{"class":114},[42,775,776],{"class":56},"}));\n",[42,778,779,781,783,786],{"class":44,"line":127},[42,780,691],{"class":73},[42,782,93],{"class":56},[42,784,785],{"class":114},"true",[42,787,788],{"class":56},", isTrue);\n",[42,790,791,793,795,798],{"class":44,"line":132},[42,792,691],{"class":73},[42,794,93],{"class":56},[42,796,797],{"class":114},"null",[42,799,800],{"class":56},", isNull);\n",[42,802,803,805,807,809],{"class":44,"line":141},[42,804,691],{"class":73},[42,806,93],{"class":56},[42,808,696],{"class":114},[42,810,811],{"class":56},", isNotNull);\n",[42,813,814,816,818,820,822,824],{"class":44,"line":159},[42,815,691],{"class":73},[42,817,713],{"class":56},[42,819,170],{"class":114},[42,821,22],{"class":56},[42,823,185],{"class":114},[42,825,826],{"class":56},"], isNotEmpty);\n",[42,828,829,831,833,836],{"class":44,"line":176},[42,830,691],{"class":73},[42,832,93],{"class":56},[42,834,835],{"class":52},"''",[42,837,838],{"class":56},", isEmpty);\n",[42,840,841],{"class":44,"line":190},[42,842,478],{"class":56},[42,844,845],{"class":44,"line":196},[42,846,64],{"emptyLinePlaceholder":63},[42,848,849,851,853,856],{"class":44,"line":201},[42,850,679],{"class":73},[42,852,93],{"class":56},[42,854,855],{"class":52},"'type matchers'",[42,857,99],{"class":56},[42,859,860,862,864,866,868,871,873,875],{"class":44,"line":209},[42,861,691],{"class":73},[42,863,93],{"class":56},[42,865,696],{"class":114},[42,867,22],{"class":56},[42,869,870],{"class":73},"isA",[42,872,118],{"class":56},[42,874,121],{"class":114},[42,876,877],{"class":56},">());\n",[42,879,880,882,884,887,889,891,893,896],{"class":44,"line":215},[42,881,691],{"class":73},[42,883,93],{"class":56},[42,885,886],{"class":52},"'hello'",[42,888,22],{"class":56},[42,890,870],{"class":73},[42,892,118],{"class":56},[42,894,895],{"class":114},"String",[42,897,877],{"class":56},[42,899,900,902,904,906,908,910,912,914,916,919,921,923],{"class":44,"line":221},[42,901,691],{"class":73},[42,903,713],{"class":56},[42,905,170],{"class":114},[42,907,22],{"class":56},[42,909,185],{"class":114},[42,911,727],{"class":56},[42,913,870],{"class":73},[42,915,118],{"class":56},[42,917,918],{"class":114},"List",[42,920,118],{"class":56},[42,922,121],{"class":114},[42,924,925],{"class":56},">>());\n",[42,927,928],{"class":44,"line":226},[42,929,478],{"class":56},[42,931,932],{"class":44,"line":231},[42,933,64],{"emptyLinePlaceholder":63},[42,935,936,938,940,943],{"class":44,"line":244},[42,937,679],{"class":73},[42,939,93],{"class":56},[42,941,942],{"class":52},"'collection matchers'",[42,944,99],{"class":56},[42,946,947,949,951,953,955,957,959,961,963,966,968,970],{"class":44,"line":269},[42,948,691],{"class":73},[42,950,713],{"class":56},[42,952,170],{"class":114},[42,954,22],{"class":56},[42,956,185],{"class":114},[42,958,22],{"class":56},[42,960,724],{"class":114},[42,962,727],{"class":56},[42,964,965],{"class":73},"contains",[42,967,93],{"class":56},[42,969,185],{"class":114},[42,971,266],{"class":56},[42,973,974,976,978,980,982,984,986,988,990,993,995,997,999,1001,1004],{"class":44,"line":288},[42,975,691],{"class":73},[42,977,713],{"class":56},[42,979,170],{"class":114},[42,981,22],{"class":56},[42,983,185],{"class":114},[42,985,22],{"class":56},[42,987,724],{"class":114},[42,989,727],{"class":56},[42,991,992],{"class":73},"containsAll",[42,994,713],{"class":56},[42,996,185],{"class":114},[42,998,22],{"class":56},[42,1000,724],{"class":114},[42,1002,1003],{"class":56},"]));  ",[42,1005,1006],{"class":83},"\u002F\u002F order-independent\n",[42,1008,1009,1011,1013,1015,1017,1019,1021,1023,1025,1028,1030,1032],{"class":44,"line":293},[42,1010,691],{"class":73},[42,1012,713],{"class":56},[42,1014,170],{"class":114},[42,1016,22],{"class":56},[42,1018,185],{"class":114},[42,1020,22],{"class":56},[42,1022,724],{"class":114},[42,1024,727],{"class":56},[42,1026,1027],{"class":73},"hasLength",[42,1029,93],{"class":56},[42,1031,724],{"class":114},[42,1033,266],{"class":56},[42,1035,1036,1038,1040,1042,1044,1046,1048,1050,1052,1055,1057,1059,1061,1063],{"class":44,"line":298},[42,1037,691],{"class":73},[42,1039,713],{"class":56},[42,1041,170],{"class":114},[42,1043,22],{"class":56},[42,1045,185],{"class":114},[42,1047,22],{"class":56},[42,1049,724],{"class":114},[42,1051,727],{"class":56},[42,1053,1054],{"class":73},"everyElement",[42,1056,93],{"class":56},[42,1058,870],{"class":73},[42,1060,118],{"class":56},[42,1062,121],{"class":114},[42,1064,1065],{"class":56},">()));\n",[42,1067,1068,1070,1072,1074,1076,1078,1080,1082,1084,1087,1089,1092,1094,1096],{"class":44,"line":310},[42,1069,691],{"class":73},[42,1071,713],{"class":56},[42,1073,170],{"class":114},[42,1075,22],{"class":56},[42,1077,185],{"class":114},[42,1079,22],{"class":56},[42,1081,724],{"class":114},[42,1083,727],{"class":56},[42,1085,1086],{"class":73},"anyElement",[42,1088,93],{"class":56},[42,1090,1091],{"class":73},"greaterThan",[42,1093,93],{"class":56},[42,1095,185],{"class":114},[42,1097,1098],{"class":56},")));\n",[42,1100,1101],{"class":44,"line":320},[42,1102,478],{"class":56},[42,1104,1105],{"class":44,"line":333},[42,1106,64],{"emptyLinePlaceholder":63},[42,1108,1109,1111,1113,1116],{"class":44,"line":352},[42,1110,679],{"class":73},[42,1112,93],{"class":56},[42,1114,1115],{"class":52},"'string matchers'",[42,1117,99],{"class":56},[42,1119,1120,1122,1124,1127,1129,1132,1134,1136],{"class":44,"line":357},[42,1121,691],{"class":73},[42,1123,93],{"class":56},[42,1125,1126],{"class":52},"'hello world'",[42,1128,22],{"class":56},[42,1130,1131],{"class":73},"startsWith",[42,1133,93],{"class":56},[42,1135,886],{"class":52},[42,1137,266],{"class":56},[42,1139,1140,1142,1144,1146,1148,1151,1153,1156],{"class":44,"line":362},[42,1141,691],{"class":73},[42,1143,93],{"class":56},[42,1145,1126],{"class":52},[42,1147,22],{"class":56},[42,1149,1150],{"class":73},"endsWith",[42,1152,93],{"class":56},[42,1154,1155],{"class":52},"'world'",[42,1157,266],{"class":56},[42,1159,1160,1162,1164,1166,1168,1170,1172,1175],{"class":44,"line":374},[42,1161,691],{"class":73},[42,1163,93],{"class":56},[42,1165,1126],{"class":52},[42,1167,22],{"class":56},[42,1169,965],{"class":73},[42,1171,93],{"class":56},[42,1173,1174],{"class":52},"'lo wo'",[42,1176,266],{"class":56},[42,1178,1179,1181,1183,1185,1187,1190,1192,1195,1197,1200],{"class":44,"line":394},[42,1180,691],{"class":73},[42,1182,93],{"class":56},[42,1184,886],{"class":52},[42,1186,22],{"class":56},[42,1188,1189],{"class":73},"matches",[42,1191,93],{"class":56},[42,1193,1194],{"class":114},"RegExp",[42,1196,93],{"class":56},[42,1198,1199],{"class":52},"r'^[a-z]+$'",[42,1201,1098],{"class":56},[42,1203,1204],{"class":44,"line":414},[42,1205,478],{"class":56},[42,1207,1208],{"class":44,"line":419},[42,1209,64],{"emptyLinePlaceholder":63},[42,1211,1212,1214,1216,1219],{"class":44,"line":424},[42,1213,679],{"class":73},[42,1215,93],{"class":56},[42,1217,1218],{"class":52},"'numeric matchers'",[42,1220,99],{"class":56},[42,1222,1223,1225,1227,1230,1232,1235,1237,1240,1242,1245,1247],{"class":44,"line":436},[42,1224,691],{"class":73},[42,1226,93],{"class":56},[42,1228,1229],{"class":114},"3.14159",[42,1231,22],{"class":56},[42,1233,1234],{"class":73},"closeTo",[42,1236,93],{"class":56},[42,1238,1239],{"class":114},"3.14",[42,1241,22],{"class":56},[42,1243,1244],{"class":114},"0.01",[42,1246,408],{"class":56},[42,1248,1249],{"class":83},"\u002F\u002F |actual - expected| \u003C= tolerance\n",[42,1251,1252,1254,1256,1258,1260,1262,1264,1267],{"class":44,"line":444},[42,1253,691],{"class":73},[42,1255,93],{"class":56},[42,1257,696],{"class":114},[42,1259,22],{"class":56},[42,1261,1091],{"class":73},[42,1263,93],{"class":56},[42,1265,1266],{"class":114},"10",[42,1268,266],{"class":56},[42,1270,1271,1273,1275,1277,1279,1282,1284,1287],{"class":44,"line":453},[42,1272,691],{"class":73},[42,1274,93],{"class":56},[42,1276,696],{"class":114},[42,1278,22],{"class":56},[42,1280,1281],{"class":73},"lessThan",[42,1283,93],{"class":56},[42,1285,1286],{"class":114},"100",[42,1288,266],{"class":56},[42,1290,1291,1293,1295,1297,1299,1302,1304,1306,1308,1310],{"class":44,"line":462},[42,1292,691],{"class":73},[42,1294,93],{"class":56},[42,1296,696],{"class":114},[42,1298,22],{"class":56},[42,1300,1301],{"class":73},"inInclusiveRange",[42,1303,93],{"class":56},[42,1305,170],{"class":114},[42,1307,22],{"class":56},[42,1309,1286],{"class":114},[42,1311,266],{"class":56},[42,1313,1314],{"class":44,"line":470},[42,1315,478],{"class":56},[42,1317,1318],{"class":44,"line":475},[42,1319,64],{"emptyLinePlaceholder":63},[42,1321,1322,1324,1326,1329],{"class":44,"line":481},[42,1323,679],{"class":73},[42,1325,93],{"class":56},[42,1327,1328],{"class":52},"'composing matchers with allOf \u002F anyOf'",[42,1330,99],{"class":56},[42,1332,1333,1335,1337,1339,1341,1344],{"class":44,"line":486},[42,1334,691],{"class":73},[42,1336,93],{"class":56},[42,1338,696],{"class":114},[42,1340,22],{"class":56},[42,1342,1343],{"class":73},"allOf",[42,1345,1346],{"class":56},"([\n",[42,1348,1349,1352,1354,1356],{"class":44,"line":492},[42,1350,1351],{"class":73},"      greaterThan",[42,1353,93],{"class":56},[42,1355,1266],{"class":114},[42,1357,1358],{"class":56},"),\n",[42,1360,1361,1364,1366,1368],{"class":44,"line":498},[42,1362,1363],{"class":73},"      lessThan",[42,1365,93],{"class":56},[42,1367,1286],{"class":114},[42,1369,1358],{"class":56},[42,1371,1372,1375,1377,1379],{"class":44,"line":510},[42,1373,1374],{"class":73},"      isA",[42,1376,118],{"class":56},[42,1378,121],{"class":114},[42,1380,1381],{"class":56},">(),\n",[42,1383,1384],{"class":44,"line":521},[42,1385,1386],{"class":56},"    ]));\n",[42,1388,1389],{"class":44,"line":526},[42,1390,64],{"emptyLinePlaceholder":63},[42,1392,1393,1395,1397,1400,1402,1404,1407],{"class":44,"line":540},[42,1394,691],{"class":73},[42,1396,93],{"class":56},[42,1398,1399],{"class":48},"-",[42,1401,170],{"class":114},[42,1403,22],{"class":56},[42,1405,1406],{"class":73},"anyOf",[42,1408,1346],{"class":56},[42,1410,1411,1413,1415,1418],{"class":44,"line":565},[42,1412,1351],{"class":73},[42,1414,93],{"class":56},[42,1416,1417],{"class":114},"0",[42,1419,1358],{"class":56},[42,1421,1422,1424,1426,1428,1431],{"class":44,"line":570},[42,1423,1363],{"class":73},[42,1425,93],{"class":56},[42,1427,1417],{"class":114},[42,1429,1430],{"class":56},"),  ",[42,1432,1433],{"class":83},"\u002F\u002F this one matches\n",[42,1435,1436],{"class":44,"line":575},[42,1437,1386],{"class":56},[42,1439,1440],{"class":44,"line":587},[42,1441,478],{"class":56},[42,1443,1444],{"class":44,"line":601},[42,1445,64],{"emptyLinePlaceholder":63},[42,1447,1448,1450,1452,1455],{"class":44,"line":606},[42,1449,679],{"class":73},[42,1451,93],{"class":56},[42,1453,1454],{"class":52},"'custom predicate matcher'",[42,1456,99],{"class":56},[42,1458,1459,1462,1465,1467,1469],{"class":44,"line":611},[42,1460,1461],{"class":114},"    bool",[42,1463,1464],{"class":73}," isPrime",[42,1466,93],{"class":56},[42,1468,121],{"class":114},[42,1470,1471],{"class":56}," n) {\n",[42,1473,1474,1477,1480,1482,1485,1488,1491,1494],{"class":44,"line":635},[42,1475,1476],{"class":48},"      if",[42,1478,1479],{"class":56}," (n ",[42,1481,118],{"class":48},[42,1483,1484],{"class":114}," 2",[42,1486,1487],{"class":56},") ",[42,1489,1490],{"class":48},"return",[42,1492,1493],{"class":114}," false",[42,1495,57],{"class":56},[42,1497,1498,1501,1504,1507,1510,1512,1514,1517,1520,1522,1525,1528,1531],{"class":44,"line":640},[42,1499,1500],{"class":48},"      for",[42,1502,1503],{"class":56}," (",[42,1505,1506],{"class":48},"var",[42,1508,1509],{"class":56}," i ",[42,1511,147],{"class":48},[42,1513,1484],{"class":114},[42,1515,1516],{"class":56},"; i ",[42,1518,1519],{"class":48},"*",[42,1521,1509],{"class":56},[42,1523,1524],{"class":48},"\u003C=",[42,1526,1527],{"class":56}," n; i",[42,1529,1530],{"class":48},"++",[42,1532,1533],{"class":56},") {\n",[42,1535,1537,1540,1542,1545,1547,1550,1553,1555,1557,1559],{"class":44,"line":1536},60,[42,1538,1539],{"class":48},"        if",[42,1541,1479],{"class":56},[42,1543,1544],{"class":48},"%",[42,1546,1509],{"class":56},[42,1548,1549],{"class":48},"==",[42,1551,1552],{"class":114}," 0",[42,1554,1487],{"class":56},[42,1556,1490],{"class":48},[42,1558,1493],{"class":114},[42,1560,57],{"class":56},[42,1562,1564],{"class":44,"line":1563},61,[42,1565,1566],{"class":56},"      }\n",[42,1568,1570,1573,1576],{"class":44,"line":1569},62,[42,1571,1572],{"class":48},"      return",[42,1574,1575],{"class":114}," true",[42,1577,57],{"class":56},[42,1579,1581],{"class":44,"line":1580},63,[42,1582,1583],{"class":56},"    }\n",[42,1585,1587],{"class":44,"line":1586},64,[42,1588,64],{"emptyLinePlaceholder":63},[42,1590,1592,1594,1596,1599,1601,1604,1607,1610],{"class":44,"line":1591},65,[42,1593,691],{"class":73},[42,1595,93],{"class":56},[42,1597,1598],{"class":114},"17",[42,1600,22],{"class":56},[42,1602,1603],{"class":73},"predicate",[42,1605,1606],{"class":56},"(isPrime, ",[42,1608,1609],{"class":52},"'is a prime number'",[42,1611,266],{"class":56},[42,1613,1615,1617,1619,1622,1624,1627,1629,1631,1633,1635],{"class":44,"line":1614},66,[42,1616,691],{"class":73},[42,1618,93],{"class":56},[42,1620,1621],{"class":114},"18",[42,1623,22],{"class":56},[42,1625,1626],{"class":73},"isNot",[42,1628,93],{"class":56},[42,1630,1603],{"class":73},[42,1632,1606],{"class":56},[42,1634,1609],{"class":52},[42,1636,1098],{"class":56},[42,1638,1640],{"class":44,"line":1639},67,[42,1641,478],{"class":56},[42,1643,1645],{"class":44,"line":1644},68,[42,1646,64],{"emptyLinePlaceholder":63},[42,1648,1650,1652,1654,1657],{"class":44,"line":1649},69,[42,1651,679],{"class":73},[42,1653,93],{"class":56},[42,1655,1656],{"class":52},"'exception matchers'",[42,1658,99],{"class":56},[42,1660,1662,1664],{"class":44,"line":1661},70,[42,1663,691],{"class":73},[42,1665,1666],{"class":56},"(\n",[42,1668,1670,1673,1675,1678,1681,1683,1686],{"class":44,"line":1669},71,[42,1671,1672],{"class":56},"      () ",[42,1674,341],{"class":48},[42,1676,1677],{"class":48}," throw",[42,1679,1680],{"class":114}," ArgumentError",[42,1682,93],{"class":56},[42,1684,1685],{"class":52},"'bad'",[42,1687,1358],{"class":56},[42,1689,1691,1694,1696,1698],{"class":44,"line":1690},72,[42,1692,1693],{"class":73},"      throwsA",[42,1695,93],{"class":56},[42,1697,1343],{"class":73},[42,1699,1346],{"class":56},[42,1701,1703,1706,1708,1711],{"class":44,"line":1702},73,[42,1704,1705],{"class":73},"        isA",[42,1707,118],{"class":56},[42,1709,1710],{"class":114},"ArgumentError",[42,1712,1381],{"class":56},[42,1714,1716,1719,1722,1724,1727,1729,1732,1734,1737],{"class":44,"line":1715},74,[42,1717,1718],{"class":73},"        predicate",[42,1720,1721],{"class":56},"((",[42,1723,1710],{"class":114},[42,1725,1726],{"class":56}," e) ",[42,1728,341],{"class":48},[42,1730,1731],{"class":56}," e.message ",[42,1733,1549],{"class":48},[42,1735,1736],{"class":52}," 'bad'",[42,1738,1358],{"class":56},[42,1740,1742],{"class":44,"line":1741},75,[42,1743,1744],{"class":56},"      ])),\n",[42,1746,1748],{"class":44,"line":1747},76,[42,1749,1750],{"class":56},"    );\n",[42,1752,1754],{"class":44,"line":1753},77,[42,1755,478],{"class":56},[42,1757,1759],{"class":44,"line":1758},78,[42,1760,643],{"class":56},[14,1762,1764],{"id":1763},"async-testing-the-await-trap","Async Testing — The Await Trap",[30,1766,1767],{"language":32},[34,1768,1770],{"className":36,"code":1769,"language":32,"meta":38,"style":38},"import 'package:test\u002Ftest.dart';\n\n\u002F\u002F ❌ Anti-pattern: async test without `async` keyword.\ntest('fetch returns data', () {\n  var future = fetchData();\n  expect(future, completion(equals('hello')));  \u002F\u002F async expectation\n  \u002F\u002F The test function returns SYNC — the framework thinks the test is done\n  \u002F\u002F before the future completes. The async assertion may not run.\n  \u002F\u002F → false positive (passes even when broken).\n});\n\n\u002F\u002F ✓ Correct: make the test `async` and `await`.\ntest('fetch returns data (fixed)', () async {\n  var data = await fetchData();\n  expect(data, equals('hello'));\n});\n\n\u002F\u002F ── Testing that a future throws ──\ntest('fetch throws on 404', () async {\n  await expectLater(\n    fetchUser('nonexistent'),\n    throwsA(isA\u003CUserNotFoundException>()),\n  );\n});\n\n\u002F\u002F ── expectLater for async matchers (completion, throwsA) ──\ntest('future completes with value', () async {\n  await expectLater(\n    Future.value(42),\n    completion(equals(42)),\n  );\n});\n\n\u002F\u002F ── Testing streams ──\ntest('stream emits expected values', () async {\n  final stream = Stream.fromIterable([1, 2, 3]);\n  await expectLater(stream, emitsInOrder([1, 2, 3]));\n});\n\ntest('stream with errors', () async {\n  final stream = Stream.error(Exception('boom'));\n  await expectLater(stream, emitsError(isA\u003CException>()));\n});\n",[19,1771,1772,1780,1784,1789,1801,1816,1841,1846,1851,1856,1861,1865,1870,1885,1900,1915,1919,1923,1928,1943,1953,1965,1982,1987,1991,1995,2000,2015,2023,2039,2055,2059,2063,2067,2072,2087,2120,2146,2150,2154,2169,2196,2217],{"__ignoreMap":38},[42,1773,1774,1776,1778],{"class":44,"line":45},[42,1775,49],{"class":48},[42,1777,53],{"class":52},[42,1779,57],{"class":56},[42,1781,1782],{"class":44,"line":60},[42,1783,64],{"emptyLinePlaceholder":63},[42,1785,1786],{"class":44,"line":67},[42,1787,1788],{"class":83},"\u002F\u002F ❌ Anti-pattern: async test without `async` keyword.\n",[42,1790,1791,1794,1796,1799],{"class":44,"line":80},[42,1792,1793],{"class":73},"test",[42,1795,93],{"class":56},[42,1797,1798],{"class":52},"'fetch returns data'",[42,1800,99],{"class":56},[42,1802,1803,1806,1809,1811,1814],{"class":44,"line":87},[42,1804,1805],{"class":48},"  var",[42,1807,1808],{"class":56}," future ",[42,1810,147],{"class":48},[42,1812,1813],{"class":73}," fetchData",[42,1815,317],{"class":56},[42,1817,1818,1821,1824,1827,1829,1831,1833,1835,1838],{"class":44,"line":102},[42,1819,1820],{"class":73},"  expect",[42,1822,1823],{"class":56},"(future, ",[42,1825,1826],{"class":73},"completion",[42,1828,93],{"class":56},[42,1830,259],{"class":73},[42,1832,93],{"class":56},[42,1834,886],{"class":52},[42,1836,1837],{"class":56},")));  ",[42,1839,1840],{"class":83},"\u002F\u002F async expectation\n",[42,1842,1843],{"class":44,"line":108},[42,1844,1845],{"class":83},"  \u002F\u002F The test function returns SYNC — the framework thinks the test is done\n",[42,1847,1848],{"class":44,"line":127},[42,1849,1850],{"class":83},"  \u002F\u002F before the future completes. The async assertion may not run.\n",[42,1852,1853],{"class":44,"line":132},[42,1854,1855],{"class":83},"  \u002F\u002F → false positive (passes even when broken).\n",[42,1857,1858],{"class":44,"line":141},[42,1859,1860],{"class":56},"});\n",[42,1862,1863],{"class":44,"line":159},[42,1864,64],{"emptyLinePlaceholder":63},[42,1866,1867],{"class":44,"line":176},[42,1868,1869],{"class":83},"\u002F\u002F ✓ Correct: make the test `async` and `await`.\n",[42,1871,1872,1874,1876,1879,1881,1883],{"class":44,"line":190},[42,1873,1793],{"class":73},[42,1875,93],{"class":56},[42,1877,1878],{"class":52},"'fetch returns data (fixed)'",[42,1880,621],{"class":56},[42,1882,534],{"class":48},[42,1884,537],{"class":56},[42,1886,1887,1889,1892,1894,1896,1898],{"class":44,"line":196},[42,1888,1805],{"class":48},[42,1890,1891],{"class":56}," data ",[42,1893,147],{"class":48},[42,1895,548],{"class":48},[42,1897,1813],{"class":73},[42,1899,317],{"class":56},[42,1901,1902,1904,1907,1909,1911,1913],{"class":44,"line":201},[42,1903,1820],{"class":73},[42,1905,1906],{"class":56},"(data, ",[42,1908,259],{"class":73},[42,1910,93],{"class":56},[42,1912,886],{"class":52},[42,1914,266],{"class":56},[42,1916,1917],{"class":44,"line":209},[42,1918,1860],{"class":56},[42,1920,1921],{"class":44,"line":215},[42,1922,64],{"emptyLinePlaceholder":63},[42,1924,1925],{"class":44,"line":221},[42,1926,1927],{"class":83},"\u002F\u002F ── Testing that a future throws ──\n",[42,1929,1930,1932,1934,1937,1939,1941],{"class":44,"line":226},[42,1931,1793],{"class":73},[42,1933,93],{"class":56},[42,1935,1936],{"class":52},"'fetch throws on 404'",[42,1938,621],{"class":56},[42,1940,534],{"class":48},[42,1942,537],{"class":56},[42,1944,1945,1948,1951],{"class":44,"line":231},[42,1946,1947],{"class":48},"  await",[42,1949,1950],{"class":73}," expectLater",[42,1952,1666],{"class":56},[42,1954,1955,1958,1960,1963],{"class":44,"line":244},[42,1956,1957],{"class":73},"    fetchUser",[42,1959,93],{"class":56},[42,1961,1962],{"class":52},"'nonexistent'",[42,1964,1358],{"class":56},[42,1966,1967,1970,1972,1974,1976,1979],{"class":44,"line":269},[42,1968,1969],{"class":73},"    throwsA",[42,1971,93],{"class":56},[42,1973,870],{"class":73},[42,1975,118],{"class":56},[42,1977,1978],{"class":114},"UserNotFoundException",[42,1980,1981],{"class":56},">()),\n",[42,1983,1984],{"class":44,"line":288},[42,1985,1986],{"class":56},"  );\n",[42,1988,1989],{"class":44,"line":293},[42,1990,1860],{"class":56},[42,1992,1993],{"class":44,"line":298},[42,1994,64],{"emptyLinePlaceholder":63},[42,1996,1997],{"class":44,"line":310},[42,1998,1999],{"class":83},"\u002F\u002F ── expectLater for async matchers (completion, throwsA) ──\n",[42,2001,2002,2004,2006,2009,2011,2013],{"class":44,"line":320},[42,2003,1793],{"class":73},[42,2005,93],{"class":56},[42,2007,2008],{"class":52},"'future completes with value'",[42,2010,621],{"class":56},[42,2012,534],{"class":48},[42,2014,537],{"class":56},[42,2016,2017,2019,2021],{"class":44,"line":333},[42,2018,1947],{"class":48},[42,2020,1950],{"class":73},[42,2022,1666],{"class":56},[42,2024,2025,2028,2030,2033,2035,2037],{"class":44,"line":352},[42,2026,2027],{"class":114},"    Future",[42,2029,553],{"class":56},[42,2031,2032],{"class":73},"value",[42,2034,93],{"class":56},[42,2036,696],{"class":114},[42,2038,1358],{"class":56},[42,2040,2041,2044,2046,2048,2050,2052],{"class":44,"line":357},[42,2042,2043],{"class":73},"    completion",[42,2045,93],{"class":56},[42,2047,259],{"class":73},[42,2049,93],{"class":56},[42,2051,696],{"class":114},[42,2053,2054],{"class":56},")),\n",[42,2056,2057],{"class":44,"line":362},[42,2058,1986],{"class":56},[42,2060,2061],{"class":44,"line":374},[42,2062,1860],{"class":56},[42,2064,2065],{"class":44,"line":394},[42,2066,64],{"emptyLinePlaceholder":63},[42,2068,2069],{"class":44,"line":414},[42,2070,2071],{"class":83},"\u002F\u002F ── Testing streams ──\n",[42,2073,2074,2076,2078,2081,2083,2085],{"class":44,"line":419},[42,2075,1793],{"class":73},[42,2077,93],{"class":56},[42,2079,2080],{"class":52},"'stream emits expected values'",[42,2082,621],{"class":56},[42,2084,534],{"class":48},[42,2086,537],{"class":56},[42,2088,2089,2092,2095,2097,2100,2102,2105,2107,2109,2111,2113,2115,2117],{"class":44,"line":424},[42,2090,2091],{"class":48},"  final",[42,2093,2094],{"class":56}," stream ",[42,2096,147],{"class":48},[42,2098,2099],{"class":114}," Stream",[42,2101,553],{"class":56},[42,2103,2104],{"class":73},"fromIterable",[42,2106,713],{"class":56},[42,2108,170],{"class":114},[42,2110,22],{"class":56},[42,2112,185],{"class":114},[42,2114,22],{"class":56},[42,2116,724],{"class":114},[42,2118,2119],{"class":56},"]);\n",[42,2121,2122,2124,2126,2129,2132,2134,2136,2138,2140,2142,2144],{"class":44,"line":436},[42,2123,1947],{"class":48},[42,2125,1950],{"class":73},[42,2127,2128],{"class":56},"(stream, ",[42,2130,2131],{"class":73},"emitsInOrder",[42,2133,713],{"class":56},[42,2135,170],{"class":114},[42,2137,22],{"class":56},[42,2139,185],{"class":114},[42,2141,22],{"class":56},[42,2143,724],{"class":114},[42,2145,744],{"class":56},[42,2147,2148],{"class":44,"line":444},[42,2149,1860],{"class":56},[42,2151,2152],{"class":44,"line":453},[42,2153,64],{"emptyLinePlaceholder":63},[42,2155,2156,2158,2160,2163,2165,2167],{"class":44,"line":462},[42,2157,1793],{"class":73},[42,2159,93],{"class":56},[42,2161,2162],{"class":52},"'stream with errors'",[42,2164,621],{"class":56},[42,2166,534],{"class":48},[42,2168,537],{"class":56},[42,2170,2171,2173,2175,2177,2179,2181,2184,2186,2189,2191,2194],{"class":44,"line":470},[42,2172,2091],{"class":48},[42,2174,2094],{"class":56},[42,2176,147],{"class":48},[42,2178,2099],{"class":114},[42,2180,553],{"class":56},[42,2182,2183],{"class":73},"error",[42,2185,93],{"class":56},[42,2187,2188],{"class":114},"Exception",[42,2190,93],{"class":56},[42,2192,2193],{"class":52},"'boom'",[42,2195,266],{"class":56},[42,2197,2198,2200,2202,2204,2207,2209,2211,2213,2215],{"class":44,"line":475},[42,2199,1947],{"class":48},[42,2201,1950],{"class":73},[42,2203,2128],{"class":56},[42,2205,2206],{"class":73},"emitsError",[42,2208,93],{"class":56},[42,2210,870],{"class":73},[42,2212,118],{"class":56},[42,2214,2188],{"class":114},[42,2216,1065],{"class":56},[42,2218,2219],{"class":44,"line":481},[42,2220,1860],{"class":56},[14,2222,2224,2225,2228],{"id":2223},"mocking-mocktail-no-code-generation","Mocking — ",[19,2226,2227],{},"mocktail"," (No Code Generation)",[30,2230,2231],{"language":32},[34,2232,2234],{"className":36,"code":2233,"language":32,"meta":38,"style":38},"import 'package:mocktail\u002Fmocktail.dart';\nimport 'package:test\u002Ftest.dart';\n\n\u002F\u002F ── mocktail: no codegen, simpler than mockito ──\nclass MockHttpClient extends Mock implements HttpClient {}\n\nclass MockDatabase extends Mock implements Database {}\n\nvoid main() {\n  late MockHttpClient mockHttp;\n  late UserService service;\n\n  setUp(() {\n    mockHttp = MockHttpClient();\n    service = UserService(mockHttp);\n  });\n\n  test('getUser returns parsed user', () async {\n    \u002F\u002F Stub: when(() => ...) with a lambda (not the actual call).\n    when(() => mockHttp.get('\u002Fusers\u002F1')).thenAnswer(\n      (_) async => '{\"id\": 1, \"name\": \"Alice\"}',\n    );\n\n    final user = await service.getUser(1);\n\n    expect(user.name, equals('Alice'));\n    \u002F\u002F Verify: check the call was made with the right arguments.\n    verify(() => mockHttp.get('\u002Fusers\u002F1')).called(1);\n  });\n\n  test('getUser throws on 404', () async {\n    when(() => mockHttp.get('\u002Fusers\u002F999')).thenThrow(\n      HttpException(404),\n    );\n\n    expect(\n      () => service.getUser(999),\n      throwsA(isA\u003CUserNotFoundException>()),\n    );\n    verify(() => mockHttp.get('\u002Fusers\u002F999')).called(1);\n  });\n\n  test('getUser uses cache on second call', () async {\n    when(() => mockHttp.get('\u002Fusers\u002F1')).thenAnswer(\n      (_) async => '{\"id\": 1, \"name\": \"Alice\"}',\n    );\n\n    await service.getUser(1);\n    await service.getUser(1);  \u002F\u002F should use cache\n\n    \u002F\u002F Verify the HTTP call happened only once (second was cached).\n    verify(() => mockHttp.get('\u002Fusers\u002F1')).called(1);\n    verifyNoMoreInteractions(mockHttp);\n  });\n\n  \u002F\u002F ── Argument matchers ──\n  test('any argument', () async {\n    when(() => mockHttp.get(any())).thenAnswer((_) async => '{}');\n    \u002F\u002F `any()` matches any argument. Use `that()` for specific matching:\n    when(() => mockHttp.get(any(that: startsWith('\u002Fusers\u002F'))))\n        .thenAnswer((_) async => '{}');\n  });\n}\n",[19,2235,2236,2245,2253,2257,2262,2285,2289,2306,2310,2318,2328,2338,2342,2349,2360,2372,2376,2380,2395,2400,2428,2444,2448,2452,2476,2480,2496,2501,2529,2533,2537,2552,2576,2588,2592,2596,2602,2619,2633,2637,2663,2667,2671,2686,2708,2720,2724,2728,2743,2761,2765,2770,2796,2803,2807,2811,2816,2831,2865,2870,2902,2919,2923],{"__ignoreMap":38},[42,2237,2238,2240,2243],{"class":44,"line":45},[42,2239,49],{"class":48},[42,2241,2242],{"class":52}," 'package:mocktail\u002Fmocktail.dart'",[42,2244,57],{"class":56},[42,2246,2247,2249,2251],{"class":44,"line":60},[42,2248,49],{"class":48},[42,2250,53],{"class":52},[42,2252,57],{"class":56},[42,2254,2255],{"class":44,"line":67},[42,2256,64],{"emptyLinePlaceholder":63},[42,2258,2259],{"class":44,"line":80},[42,2260,2261],{"class":83},"\u002F\u002F ── mocktail: no codegen, simpler than mockito ──\n",[42,2263,2264,2267,2270,2273,2276,2279,2282],{"class":44,"line":87},[42,2265,2266],{"class":48},"class",[42,2268,2269],{"class":114}," MockHttpClient",[42,2271,2272],{"class":48}," extends",[42,2274,2275],{"class":114}," Mock",[42,2277,2278],{"class":48}," implements",[42,2280,2281],{"class":114}," HttpClient",[42,2283,2284],{"class":56}," {}\n",[42,2286,2287],{"class":44,"line":102},[42,2288,64],{"emptyLinePlaceholder":63},[42,2290,2291,2293,2296,2298,2300,2302,2304],{"class":44,"line":108},[42,2292,2266],{"class":48},[42,2294,2295],{"class":114}," MockDatabase",[42,2297,2272],{"class":48},[42,2299,2275],{"class":114},[42,2301,2278],{"class":48},[42,2303,515],{"class":114},[42,2305,2284],{"class":56},[42,2307,2308],{"class":44,"line":127},[42,2309,64],{"emptyLinePlaceholder":63},[42,2311,2312,2314,2316],{"class":44,"line":132},[42,2313,70],{"class":48},[42,2315,74],{"class":73},[42,2317,77],{"class":56},[42,2319,2320,2323,2325],{"class":44,"line":141},[42,2321,2322],{"class":48},"  late",[42,2324,2269],{"class":114},[42,2326,2327],{"class":56}," mockHttp;\n",[42,2329,2330,2332,2335],{"class":44,"line":159},[42,2331,2322],{"class":48},[42,2333,2334],{"class":114}," UserService",[42,2336,2337],{"class":56}," service;\n",[42,2339,2340],{"class":44,"line":176},[42,2341,64],{"emptyLinePlaceholder":63},[42,2343,2344,2347],{"class":44,"line":190},[42,2345,2346],{"class":73},"  setUp",[42,2348,138],{"class":56},[42,2350,2351,2354,2356,2358],{"class":44,"line":196},[42,2352,2353],{"class":56},"    mockHttp ",[42,2355,147],{"class":48},[42,2357,2269],{"class":114},[42,2359,317],{"class":56},[42,2361,2362,2365,2367,2369],{"class":44,"line":201},[42,2363,2364],{"class":56},"    service ",[42,2366,147],{"class":48},[42,2368,2334],{"class":114},[42,2370,2371],{"class":56},"(mockHttp);\n",[42,2373,2374],{"class":44,"line":209},[42,2375,478],{"class":56},[42,2377,2378],{"class":44,"line":215},[42,2379,64],{"emptyLinePlaceholder":63},[42,2381,2382,2384,2386,2389,2391,2393],{"class":44,"line":221},[42,2383,679],{"class":73},[42,2385,93],{"class":56},[42,2387,2388],{"class":52},"'getUser returns parsed user'",[42,2390,621],{"class":56},[42,2392,534],{"class":48},[42,2394,537],{"class":56},[42,2396,2397],{"class":44,"line":226},[42,2398,2399],{"class":83},"    \u002F\u002F Stub: when(() => ...) with a lambda (not the actual call).\n",[42,2401,2402,2405,2407,2409,2412,2415,2417,2420,2423,2426],{"class":44,"line":231},[42,2403,2404],{"class":48},"    when",[42,2406,338],{"class":56},[42,2408,341],{"class":48},[42,2410,2411],{"class":56}," mockHttp.",[42,2413,2414],{"class":73},"get",[42,2416,93],{"class":56},[42,2418,2419],{"class":52},"'\u002Fusers\u002F1'",[42,2421,2422],{"class":56},")).",[42,2424,2425],{"class":73},"thenAnswer",[42,2427,1666],{"class":56},[42,2429,2430,2433,2435,2438,2441],{"class":44,"line":244},[42,2431,2432],{"class":56},"      (_) ",[42,2434,534],{"class":48},[42,2436,2437],{"class":48}," =>",[42,2439,2440],{"class":52}," '{\"id\": 1, \"name\": \"Alice\"}'",[42,2442,2443],{"class":56},",\n",[42,2445,2446],{"class":44,"line":269},[42,2447,1750],{"class":56},[42,2449,2450],{"class":44,"line":288},[42,2451,64],{"emptyLinePlaceholder":63},[42,2453,2454,2457,2460,2462,2464,2467,2470,2472,2474],{"class":44,"line":293},[42,2455,2456],{"class":48},"    final",[42,2458,2459],{"class":56}," user ",[42,2461,147],{"class":48},[42,2463,548],{"class":48},[42,2465,2466],{"class":56}," service.",[42,2468,2469],{"class":73},"getUser",[42,2471,93],{"class":56},[42,2473,170],{"class":114},[42,2475,173],{"class":56},[42,2477,2478],{"class":44,"line":298},[42,2479,64],{"emptyLinePlaceholder":63},[42,2481,2482,2484,2487,2489,2491,2494],{"class":44,"line":310},[42,2483,691],{"class":73},[42,2485,2486],{"class":56},"(user.name, ",[42,2488,259],{"class":73},[42,2490,93],{"class":56},[42,2492,2493],{"class":52},"'Alice'",[42,2495,266],{"class":56},[42,2497,2498],{"class":44,"line":320},[42,2499,2500],{"class":83},"    \u002F\u002F Verify: check the call was made with the right arguments.\n",[42,2502,2503,2506,2508,2510,2512,2514,2516,2518,2520,2523,2525,2527],{"class":44,"line":333},[42,2504,2505],{"class":73},"    verify",[42,2507,338],{"class":56},[42,2509,341],{"class":48},[42,2511,2411],{"class":56},[42,2513,2414],{"class":73},[42,2515,93],{"class":56},[42,2517,2419],{"class":52},[42,2519,2422],{"class":56},[42,2521,2522],{"class":73},"called",[42,2524,93],{"class":56},[42,2526,170],{"class":114},[42,2528,173],{"class":56},[42,2530,2531],{"class":44,"line":352},[42,2532,478],{"class":56},[42,2534,2535],{"class":44,"line":357},[42,2536,64],{"emptyLinePlaceholder":63},[42,2538,2539,2541,2543,2546,2548,2550],{"class":44,"line":362},[42,2540,679],{"class":73},[42,2542,93],{"class":56},[42,2544,2545],{"class":52},"'getUser throws on 404'",[42,2547,621],{"class":56},[42,2549,534],{"class":48},[42,2551,537],{"class":56},[42,2553,2554,2556,2558,2560,2562,2564,2566,2569,2571,2574],{"class":44,"line":374},[42,2555,2404],{"class":48},[42,2557,338],{"class":56},[42,2559,341],{"class":48},[42,2561,2411],{"class":56},[42,2563,2414],{"class":73},[42,2565,93],{"class":56},[42,2567,2568],{"class":52},"'\u002Fusers\u002F999'",[42,2570,2422],{"class":56},[42,2572,2573],{"class":73},"thenThrow",[42,2575,1666],{"class":56},[42,2577,2578,2581,2583,2586],{"class":44,"line":394},[42,2579,2580],{"class":114},"      HttpException",[42,2582,93],{"class":56},[42,2584,2585],{"class":114},"404",[42,2587,1358],{"class":56},[42,2589,2590],{"class":44,"line":414},[42,2591,1750],{"class":56},[42,2593,2594],{"class":44,"line":419},[42,2595,64],{"emptyLinePlaceholder":63},[42,2597,2598,2600],{"class":44,"line":424},[42,2599,691],{"class":73},[42,2601,1666],{"class":56},[42,2603,2604,2606,2608,2610,2612,2614,2617],{"class":44,"line":436},[42,2605,1672],{"class":56},[42,2607,341],{"class":48},[42,2609,2466],{"class":56},[42,2611,2469],{"class":73},[42,2613,93],{"class":56},[42,2615,2616],{"class":114},"999",[42,2618,1358],{"class":56},[42,2620,2621,2623,2625,2627,2629,2631],{"class":44,"line":444},[42,2622,1693],{"class":73},[42,2624,93],{"class":56},[42,2626,870],{"class":73},[42,2628,118],{"class":56},[42,2630,1978],{"class":114},[42,2632,1981],{"class":56},[42,2634,2635],{"class":44,"line":453},[42,2636,1750],{"class":56},[42,2638,2639,2641,2643,2645,2647,2649,2651,2653,2655,2657,2659,2661],{"class":44,"line":462},[42,2640,2505],{"class":73},[42,2642,338],{"class":56},[42,2644,341],{"class":48},[42,2646,2411],{"class":56},[42,2648,2414],{"class":73},[42,2650,93],{"class":56},[42,2652,2568],{"class":52},[42,2654,2422],{"class":56},[42,2656,2522],{"class":73},[42,2658,93],{"class":56},[42,2660,170],{"class":114},[42,2662,173],{"class":56},[42,2664,2665],{"class":44,"line":470},[42,2666,478],{"class":56},[42,2668,2669],{"class":44,"line":475},[42,2670,64],{"emptyLinePlaceholder":63},[42,2672,2673,2675,2677,2680,2682,2684],{"class":44,"line":481},[42,2674,679],{"class":73},[42,2676,93],{"class":56},[42,2678,2679],{"class":52},"'getUser uses cache on second call'",[42,2681,621],{"class":56},[42,2683,534],{"class":48},[42,2685,537],{"class":56},[42,2687,2688,2690,2692,2694,2696,2698,2700,2702,2704,2706],{"class":44,"line":486},[42,2689,2404],{"class":48},[42,2691,338],{"class":56},[42,2693,341],{"class":48},[42,2695,2411],{"class":56},[42,2697,2414],{"class":73},[42,2699,93],{"class":56},[42,2701,2419],{"class":52},[42,2703,2422],{"class":56},[42,2705,2425],{"class":73},[42,2707,1666],{"class":56},[42,2709,2710,2712,2714,2716,2718],{"class":44,"line":492},[42,2711,2432],{"class":56},[42,2713,534],{"class":48},[42,2715,2437],{"class":48},[42,2717,2440],{"class":52},[42,2719,2443],{"class":56},[42,2721,2722],{"class":44,"line":498},[42,2723,1750],{"class":56},[42,2725,2726],{"class":44,"line":510},[42,2727,64],{"emptyLinePlaceholder":63},[42,2729,2730,2733,2735,2737,2739,2741],{"class":44,"line":521},[42,2731,2732],{"class":48},"    await",[42,2734,2466],{"class":56},[42,2736,2469],{"class":73},[42,2738,93],{"class":56},[42,2740,170],{"class":114},[42,2742,173],{"class":56},[42,2744,2745,2747,2749,2751,2753,2755,2758],{"class":44,"line":526},[42,2746,2732],{"class":48},[42,2748,2466],{"class":56},[42,2750,2469],{"class":73},[42,2752,93],{"class":56},[42,2754,170],{"class":114},[42,2756,2757],{"class":56},");  ",[42,2759,2760],{"class":83},"\u002F\u002F should use cache\n",[42,2762,2763],{"class":44,"line":540},[42,2764,64],{"emptyLinePlaceholder":63},[42,2766,2767],{"class":44,"line":565},[42,2768,2769],{"class":83},"    \u002F\u002F Verify the HTTP call happened only once (second was cached).\n",[42,2771,2772,2774,2776,2778,2780,2782,2784,2786,2788,2790,2792,2794],{"class":44,"line":570},[42,2773,2505],{"class":73},[42,2775,338],{"class":56},[42,2777,341],{"class":48},[42,2779,2411],{"class":56},[42,2781,2414],{"class":73},[42,2783,93],{"class":56},[42,2785,2419],{"class":52},[42,2787,2422],{"class":56},[42,2789,2522],{"class":73},[42,2791,93],{"class":56},[42,2793,170],{"class":114},[42,2795,173],{"class":56},[42,2797,2798,2801],{"class":44,"line":575},[42,2799,2800],{"class":73},"    verifyNoMoreInteractions",[42,2802,2371],{"class":56},[42,2804,2805],{"class":44,"line":587},[42,2806,478],{"class":56},[42,2808,2809],{"class":44,"line":601},[42,2810,64],{"emptyLinePlaceholder":63},[42,2812,2813],{"class":44,"line":606},[42,2814,2815],{"class":83},"  \u002F\u002F ── Argument matchers ──\n",[42,2817,2818,2820,2822,2825,2827,2829],{"class":44,"line":611},[42,2819,679],{"class":73},[42,2821,93],{"class":56},[42,2823,2824],{"class":52},"'any argument'",[42,2826,621],{"class":56},[42,2828,534],{"class":48},[42,2830,537],{"class":56},[42,2832,2833,2835,2837,2839,2841,2843,2845,2848,2851,2853,2856,2858,2860,2863],{"class":44,"line":635},[42,2834,2404],{"class":48},[42,2836,338],{"class":56},[42,2838,341],{"class":48},[42,2840,2411],{"class":56},[42,2842,2414],{"class":73},[42,2844,93],{"class":56},[42,2846,2847],{"class":73},"any",[42,2849,2850],{"class":56},"())).",[42,2852,2425],{"class":73},[42,2854,2855],{"class":56},"((_) ",[42,2857,534],{"class":48},[42,2859,2437],{"class":48},[42,2861,2862],{"class":52}," '{}'",[42,2864,173],{"class":56},[42,2866,2867],{"class":44,"line":640},[42,2868,2869],{"class":83},"    \u002F\u002F `any()` matches any argument. Use `that()` for specific matching:\n",[42,2871,2872,2874,2876,2878,2880,2882,2884,2886,2889,2891,2894,2896,2899],{"class":44,"line":1536},[42,2873,2404],{"class":48},[42,2875,338],{"class":56},[42,2877,341],{"class":48},[42,2879,2411],{"class":56},[42,2881,2414],{"class":73},[42,2883,93],{"class":56},[42,2885,2847],{"class":73},[42,2887,2888],{"class":56},"(that",[42,2890,757],{"class":48},[42,2892,2893],{"class":73}," startsWith",[42,2895,93],{"class":56},[42,2897,2898],{"class":52},"'\u002Fusers\u002F'",[42,2900,2901],{"class":56},"))))\n",[42,2903,2904,2907,2909,2911,2913,2915,2917],{"class":44,"line":1563},[42,2905,2906],{"class":56},"        .",[42,2908,2425],{"class":73},[42,2910,2855],{"class":56},[42,2912,534],{"class":48},[42,2914,2437],{"class":48},[42,2916,2862],{"class":52},[42,2918,173],{"class":56},[42,2920,2921],{"class":44,"line":1569},[42,2922,478],{"class":56},[42,2924,2925],{"class":44,"line":1580},[42,2926,643],{"class":56},[14,2928,2930],{"id":2929},"parameterized-tests-loop-based","Parameterized Tests — Loop-Based",[30,2932,2933],{"language":32},[34,2934,2936],{"className":36,"code":2935,"language":32,"meta":38,"style":38},"import 'package:test\u002Ftest.dart';\n\n\u002F\u002F Dart's test package doesn't have built-in parameterized tests (like JUnit),\n\u002F\u002F but you can loop — each iteration creates a separate test (visible in output).\n\nvoid main() {\n  group('string reverse', () {\n    for (final entry in {\n      'hello': 'olleh',\n      '': '',\n      'a': 'a',\n      'racecar': 'racecar',\n      'ab': 'ba',\n    }.entries) {\n      test('reverse(\"${entry.key}\") == \"${entry.value}\"', () {\n        expect(entry.key.split('').reversed.join(), equals(entry.value));\n      });\n    }\n  });\n\n  group('temperature conversion', () {\n    final cases = [\n      (0, 32.0),     \u002F\u002F 0°C = 32°F\n      (100, 212.0),  \u002F\u002F 100°C = 212°F\n      (-40, -40.0),  \u002F\u002F -40°C = -40°F\n      (37, 98.6),    \u002F\u002F 37°C = 98.6°F\n    ];\n    for (final (celsius, fahrenheit) in cases) {\n      test('$celsius°C → $fahrenheit°F', () {\n        expect(celsiusToFahrenheit(celsius), closeTo(fahrenheit, 0.01));\n      });\n    }\n  });\n}\n\ndouble celsiusToFahrenheit(double c) => c * 9 \u002F 5 + 32;\n",[19,2937,2938,2946,2950,2955,2960,2964,2972,2983,3001,3013,3025,3037,3049,3061,3066,3108,3136,3141,3145,3149,3153,3164,3176,3194,3210,3231,3249,3254,3270,3298,3319,3323,3327,3331,3335,3339],{"__ignoreMap":38},[42,2939,2940,2942,2944],{"class":44,"line":45},[42,2941,49],{"class":48},[42,2943,53],{"class":52},[42,2945,57],{"class":56},[42,2947,2948],{"class":44,"line":60},[42,2949,64],{"emptyLinePlaceholder":63},[42,2951,2952],{"class":44,"line":67},[42,2953,2954],{"class":83},"\u002F\u002F Dart's test package doesn't have built-in parameterized tests (like JUnit),\n",[42,2956,2957],{"class":44,"line":80},[42,2958,2959],{"class":83},"\u002F\u002F but you can loop — each iteration creates a separate test (visible in output).\n",[42,2961,2962],{"class":44,"line":87},[42,2963,64],{"emptyLinePlaceholder":63},[42,2965,2966,2968,2970],{"class":44,"line":102},[42,2967,70],{"class":48},[42,2969,74],{"class":73},[42,2971,77],{"class":56},[42,2973,2974,2976,2978,2981],{"class":44,"line":108},[42,2975,90],{"class":73},[42,2977,93],{"class":56},[42,2979,2980],{"class":52},"'string reverse'",[42,2982,99],{"class":56},[42,2984,2985,2988,2990,2993,2996,2999],{"class":44,"line":127},[42,2986,2987],{"class":48},"    for",[42,2989,1503],{"class":56},[42,2991,2992],{"class":48},"final",[42,2994,2995],{"class":56}," entry ",[42,2997,2998],{"class":48},"in",[42,3000,537],{"class":56},[42,3002,3003,3006,3008,3011],{"class":44,"line":132},[42,3004,3005],{"class":52},"      'hello'",[42,3007,757],{"class":48},[42,3009,3010],{"class":52}," 'olleh'",[42,3012,2443],{"class":56},[42,3014,3015,3018,3020,3023],{"class":44,"line":141},[42,3016,3017],{"class":52},"      ''",[42,3019,757],{"class":48},[42,3021,3022],{"class":52}," ''",[42,3024,2443],{"class":56},[42,3026,3027,3030,3032,3035],{"class":44,"line":159},[42,3028,3029],{"class":52},"      'a'",[42,3031,757],{"class":48},[42,3033,3034],{"class":52}," 'a'",[42,3036,2443],{"class":56},[42,3038,3039,3042,3044,3047],{"class":44,"line":176},[42,3040,3041],{"class":52},"      'racecar'",[42,3043,757],{"class":48},[42,3045,3046],{"class":52}," 'racecar'",[42,3048,2443],{"class":56},[42,3050,3051,3054,3056,3059],{"class":44,"line":190},[42,3052,3053],{"class":52},"      'ab'",[42,3055,757],{"class":48},[42,3057,3058],{"class":52}," 'ba'",[42,3060,2443],{"class":56},[42,3062,3063],{"class":44,"line":196},[42,3064,3065],{"class":56},"    }.entries) {\n",[42,3067,3068,3071,3073,3076,3079,3082,3084,3087,3090,3093,3095,3097,3099,3101,3103,3106],{"class":44,"line":201},[42,3069,3070],{"class":73},"      test",[42,3072,93],{"class":56},[42,3074,3075],{"class":52},"'reverse(\"",[42,3077,3078],{"class":52},"${",[42,3080,3081],{"class":114},"entry",[42,3083,553],{"class":52},[42,3085,3086],{"class":114},"key",[42,3088,3089],{"class":52},"}",[42,3091,3092],{"class":52},"\") == \"",[42,3094,3078],{"class":52},[42,3096,3081],{"class":114},[42,3098,553],{"class":52},[42,3100,2032],{"class":114},[42,3102,3089],{"class":52},[42,3104,3105],{"class":52},"\"'",[42,3107,99],{"class":56},[42,3109,3110,3113,3116,3119,3121,3123,3126,3129,3131,3133],{"class":44,"line":209},[42,3111,3112],{"class":73},"        expect",[42,3114,3115],{"class":56},"(entry.key.",[42,3117,3118],{"class":73},"split",[42,3120,93],{"class":56},[42,3122,835],{"class":52},[42,3124,3125],{"class":56},").reversed.",[42,3127,3128],{"class":73},"join",[42,3130,256],{"class":56},[42,3132,259],{"class":73},[42,3134,3135],{"class":56},"(entry.value));\n",[42,3137,3138],{"class":44,"line":215},[42,3139,3140],{"class":56},"      });\n",[42,3142,3143],{"class":44,"line":221},[42,3144,1583],{"class":56},[42,3146,3147],{"class":44,"line":226},[42,3148,478],{"class":56},[42,3150,3151],{"class":44,"line":231},[42,3152,64],{"emptyLinePlaceholder":63},[42,3154,3155,3157,3159,3162],{"class":44,"line":244},[42,3156,90],{"class":73},[42,3158,93],{"class":56},[42,3160,3161],{"class":52},"'temperature conversion'",[42,3163,99],{"class":56},[42,3165,3166,3168,3171,3173],{"class":44,"line":269},[42,3167,2456],{"class":48},[42,3169,3170],{"class":56}," cases ",[42,3172,147],{"class":48},[42,3174,3175],{"class":56}," [\n",[42,3177,3178,3181,3183,3185,3188,3191],{"class":44,"line":288},[42,3179,3180],{"class":56},"      (",[42,3182,1417],{"class":114},[42,3184,22],{"class":56},[42,3186,3187],{"class":114},"32.0",[42,3189,3190],{"class":56},"),     ",[42,3192,3193],{"class":83},"\u002F\u002F 0°C = 32°F\n",[42,3195,3196,3198,3200,3202,3205,3207],{"class":44,"line":293},[42,3197,3180],{"class":56},[42,3199,1286],{"class":114},[42,3201,22],{"class":56},[42,3203,3204],{"class":114},"212.0",[42,3206,1430],{"class":56},[42,3208,3209],{"class":83},"\u002F\u002F 100°C = 212°F\n",[42,3211,3212,3214,3216,3219,3221,3223,3226,3228],{"class":44,"line":298},[42,3213,3180],{"class":56},[42,3215,1399],{"class":48},[42,3217,3218],{"class":114},"40",[42,3220,22],{"class":56},[42,3222,1399],{"class":48},[42,3224,3225],{"class":114},"40.0",[42,3227,1430],{"class":56},[42,3229,3230],{"class":83},"\u002F\u002F -40°C = -40°F\n",[42,3232,3233,3235,3238,3240,3243,3246],{"class":44,"line":310},[42,3234,3180],{"class":56},[42,3236,3237],{"class":114},"37",[42,3239,22],{"class":56},[42,3241,3242],{"class":114},"98.6",[42,3244,3245],{"class":56},"),    ",[42,3247,3248],{"class":83},"\u002F\u002F 37°C = 98.6°F\n",[42,3250,3251],{"class":44,"line":320},[42,3252,3253],{"class":56},"    ];\n",[42,3255,3256,3258,3260,3262,3265,3267],{"class":44,"line":333},[42,3257,2987],{"class":48},[42,3259,1503],{"class":56},[42,3261,2992],{"class":48},[42,3263,3264],{"class":56}," (celsius, fahrenheit) ",[42,3266,2998],{"class":48},[42,3268,3269],{"class":56}," cases) {\n",[42,3271,3272,3274,3276,3279,3282,3285,3288,3290,3293,3296],{"class":44,"line":352},[42,3273,3070],{"class":73},[42,3275,93],{"class":56},[42,3277,3278],{"class":52},"'",[42,3280,3281],{"class":52},"$",[42,3283,3284],{"class":114},"celsius",[42,3286,3287],{"class":52},"°C → ",[42,3289,3281],{"class":52},[42,3291,3292],{"class":114},"fahrenheit",[42,3294,3295],{"class":52},"°F'",[42,3297,99],{"class":56},[42,3299,3300,3302,3304,3307,3310,3312,3315,3317],{"class":44,"line":357},[42,3301,3112],{"class":73},[42,3303,93],{"class":56},[42,3305,3306],{"class":73},"celsiusToFahrenheit",[42,3308,3309],{"class":56},"(celsius), ",[42,3311,1234],{"class":73},[42,3313,3314],{"class":56},"(fahrenheit, ",[42,3316,1244],{"class":114},[42,3318,266],{"class":56},[42,3320,3321],{"class":44,"line":362},[42,3322,3140],{"class":56},[42,3324,3325],{"class":44,"line":374},[42,3326,1583],{"class":56},[42,3328,3329],{"class":44,"line":394},[42,3330,478],{"class":56},[42,3332,3333],{"class":44,"line":414},[42,3334,643],{"class":56},[42,3336,3337],{"class":44,"line":419},[42,3338,64],{"emptyLinePlaceholder":63},[42,3340,3341,3344,3347,3349,3351,3354,3356,3359,3361,3364,3367,3370,3373,3376],{"class":44,"line":424},[42,3342,3343],{"class":114},"double",[42,3345,3346],{"class":73}," celsiusToFahrenheit",[42,3348,93],{"class":56},[42,3350,3343],{"class":114},[42,3352,3353],{"class":56}," c) ",[42,3355,341],{"class":48},[42,3357,3358],{"class":56}," c ",[42,3360,1519],{"class":48},[42,3362,3363],{"class":114}," 9",[42,3365,3366],{"class":48}," \u002F",[42,3368,3369],{"class":114}," 5",[42,3371,3372],{"class":48}," +",[42,3374,3375],{"class":114}," 32",[42,3377,57],{"class":56},[14,3379,3381],{"id":3380},"test-isolation-dependency-injection","Test Isolation & Dependency Injection",[30,3383,3384],{"language":32},[34,3385,3387],{"className":36,"code":3386,"language":32,"meta":38,"style":38},"import 'package:test\u002Ftest.dart';\nimport 'package:mocktail\u002Fmocktail.dart';\n\n\u002F\u002F Production code depends on abstractions (interfaces), not concretions.\n\u002F\u002F Tests inject mocks for the dependencies.\n\n\u002F\u002F ── Abstract dependency ──\nabstract class UserRepository {\n  Future\u003CUser> findById(int id);\n  Future\u003Cvoid> save(User user);\n}\n\n\u002F\u002F ── Production implementation (uses a real database) ──\nclass SqlUserRepository implements UserRepository {\n  @override\n  Future\u003CUser> findById(int id) async { \u002F* real DB query *\u002F }\n  @override\n  Future\u003Cvoid> save(User user) async { \u002F* real DB insert *\u002F }\n}\n\n\u002F\u002F ── Mock for testing ──\nclass MockUserRepository extends Mock implements UserRepository {}\n\n\u002F\u002F ── Service depends on the abstraction ──\nclass UserService {\n  final UserRepository _repo;\n  UserService(this._repo);  \u002F\u002F dependency injection via constructor\n\n  Future\u003CUser> getUser(int id) async {\n    final user = await _repo.findById(id);\n    if (user == null) throw UserNotFoundException(id);\n    return user;\n  }\n}\n\n\u002F\u002F ── Tests inject the mock ──\nvoid main() {\n  group('UserService', () {\n    late MockUserRepository mockRepo;\n    late UserService service;\n\n    setUp(() {\n      mockRepo = MockUserRepository();\n      service = UserService(mockRepo);  \u002F\u002F inject mock\n      registerFallbackValue(User(id: 0, name: ''));  \u002F\u002F for any() with value types\n    });\n\n    test('getUser returns user from repo', () async {\n      when(() => mockRepo.findById(1)).thenAnswer(\n        (_) async => User(id: 1, name: 'Alice'),\n      );\n\n      final user = await service.getUser(1);\n\n      expect(user.name, equals('Alice'));\n      verify(() => mockRepo.findById(1)).called(1);\n    });\n  });\n}\n",[19,3388,3389,3397,3405,3409,3414,3419,3423,3428,3441,3464,3484,3488,3492,3497,3510,3515,3544,3548,3576,3580,3584,3589,3606,3610,3615,3623,3632,3648,3652,3674,3692,3715,3723,3728,3732,3736,3741,3749,3760,3769,3777,3781,3787,3798,3813,3841,3845,3849,3864,3888,3915,3920,3924,3945,3949,3963,3990,3994,3998],{"__ignoreMap":38},[42,3390,3391,3393,3395],{"class":44,"line":45},[42,3392,49],{"class":48},[42,3394,53],{"class":52},[42,3396,57],{"class":56},[42,3398,3399,3401,3403],{"class":44,"line":60},[42,3400,49],{"class":48},[42,3402,2242],{"class":52},[42,3404,57],{"class":56},[42,3406,3407],{"class":44,"line":67},[42,3408,64],{"emptyLinePlaceholder":63},[42,3410,3411],{"class":44,"line":80},[42,3412,3413],{"class":83},"\u002F\u002F Production code depends on abstractions (interfaces), not concretions.\n",[42,3415,3416],{"class":44,"line":87},[42,3417,3418],{"class":83},"\u002F\u002F Tests inject mocks for the dependencies.\n",[42,3420,3421],{"class":44,"line":102},[42,3422,64],{"emptyLinePlaceholder":63},[42,3424,3425],{"class":44,"line":108},[42,3426,3427],{"class":83},"\u002F\u002F ── Abstract dependency ──\n",[42,3429,3430,3433,3436,3439],{"class":44,"line":127},[42,3431,3432],{"class":48},"abstract",[42,3434,3435],{"class":48}," class",[42,3437,3438],{"class":114}," UserRepository",[42,3440,537],{"class":56},[42,3442,3443,3446,3448,3451,3454,3457,3459,3461],{"class":44,"line":132},[42,3444,3445],{"class":114},"  Future",[42,3447,118],{"class":56},[42,3449,3450],{"class":114},"User",[42,3452,3453],{"class":56},"> ",[42,3455,3456],{"class":73},"findById",[42,3458,93],{"class":56},[42,3460,121],{"class":114},[42,3462,3463],{"class":56}," id);\n",[42,3465,3466,3468,3470,3472,3474,3477,3479,3481],{"class":44,"line":141},[42,3467,3445],{"class":114},[42,3469,118],{"class":56},[42,3471,70],{"class":48},[42,3473,3453],{"class":56},[42,3475,3476],{"class":73},"save",[42,3478,93],{"class":56},[42,3480,3450],{"class":114},[42,3482,3483],{"class":56}," user);\n",[42,3485,3486],{"class":44,"line":159},[42,3487,643],{"class":56},[42,3489,3490],{"class":44,"line":176},[42,3491,64],{"emptyLinePlaceholder":63},[42,3493,3494],{"class":44,"line":190},[42,3495,3496],{"class":83},"\u002F\u002F ── Production implementation (uses a real database) ──\n",[42,3498,3499,3501,3504,3506,3508],{"class":44,"line":196},[42,3500,2266],{"class":48},[42,3502,3503],{"class":114}," SqlUserRepository",[42,3505,2278],{"class":48},[42,3507,3438],{"class":114},[42,3509,537],{"class":56},[42,3511,3512],{"class":44,"line":201},[42,3513,3514],{"class":48},"  @override\n",[42,3516,3517,3519,3521,3523,3525,3527,3529,3531,3534,3536,3538,3541],{"class":44,"line":209},[42,3518,3445],{"class":114},[42,3520,118],{"class":56},[42,3522,3450],{"class":114},[42,3524,3453],{"class":56},[42,3526,3456],{"class":73},[42,3528,93],{"class":56},[42,3530,121],{"class":114},[42,3532,3533],{"class":56}," id) ",[42,3535,534],{"class":48},[42,3537,626],{"class":56},[42,3539,3540],{"class":83},"\u002F* real DB query *\u002F",[42,3542,3543],{"class":56}," }\n",[42,3545,3546],{"class":44,"line":215},[42,3547,3514],{"class":48},[42,3549,3550,3552,3554,3556,3558,3560,3562,3564,3567,3569,3571,3574],{"class":44,"line":221},[42,3551,3445],{"class":114},[42,3553,118],{"class":56},[42,3555,70],{"class":48},[42,3557,3453],{"class":56},[42,3559,3476],{"class":73},[42,3561,93],{"class":56},[42,3563,3450],{"class":114},[42,3565,3566],{"class":56}," user) ",[42,3568,534],{"class":48},[42,3570,626],{"class":56},[42,3572,3573],{"class":83},"\u002F* real DB insert *\u002F",[42,3575,3543],{"class":56},[42,3577,3578],{"class":44,"line":226},[42,3579,643],{"class":56},[42,3581,3582],{"class":44,"line":231},[42,3583,64],{"emptyLinePlaceholder":63},[42,3585,3586],{"class":44,"line":244},[42,3587,3588],{"class":83},"\u002F\u002F ── Mock for testing ──\n",[42,3590,3591,3593,3596,3598,3600,3602,3604],{"class":44,"line":269},[42,3592,2266],{"class":48},[42,3594,3595],{"class":114}," MockUserRepository",[42,3597,2272],{"class":48},[42,3599,2275],{"class":114},[42,3601,2278],{"class":48},[42,3603,3438],{"class":114},[42,3605,2284],{"class":56},[42,3607,3608],{"class":44,"line":288},[42,3609,64],{"emptyLinePlaceholder":63},[42,3611,3612],{"class":44,"line":293},[42,3613,3614],{"class":83},"\u002F\u002F ── Service depends on the abstraction ──\n",[42,3616,3617,3619,3621],{"class":44,"line":298},[42,3618,2266],{"class":48},[42,3620,2334],{"class":114},[42,3622,537],{"class":56},[42,3624,3625,3627,3629],{"class":44,"line":310},[42,3626,2091],{"class":48},[42,3628,3438],{"class":114},[42,3630,3631],{"class":56}," _repo;\n",[42,3633,3634,3637,3639,3642,3645],{"class":44,"line":320},[42,3635,3636],{"class":114},"  UserService",[42,3638,93],{"class":56},[42,3640,3641],{"class":114},"this",[42,3643,3644],{"class":56},"._repo);  ",[42,3646,3647],{"class":83},"\u002F\u002F dependency injection via constructor\n",[42,3649,3650],{"class":44,"line":333},[42,3651,64],{"emptyLinePlaceholder":63},[42,3653,3654,3656,3658,3660,3662,3664,3666,3668,3670,3672],{"class":44,"line":352},[42,3655,3445],{"class":114},[42,3657,118],{"class":56},[42,3659,3450],{"class":114},[42,3661,3453],{"class":56},[42,3663,2469],{"class":73},[42,3665,93],{"class":56},[42,3667,121],{"class":114},[42,3669,3533],{"class":56},[42,3671,534],{"class":48},[42,3673,537],{"class":56},[42,3675,3676,3678,3680,3682,3684,3687,3689],{"class":44,"line":357},[42,3677,2456],{"class":48},[42,3679,2459],{"class":56},[42,3681,147],{"class":48},[42,3683,548],{"class":48},[42,3685,3686],{"class":56}," _repo.",[42,3688,3456],{"class":73},[42,3690,3691],{"class":56},"(id);\n",[42,3693,3694,3697,3700,3702,3705,3707,3710,3713],{"class":44,"line":362},[42,3695,3696],{"class":48},"    if",[42,3698,3699],{"class":56}," (user ",[42,3701,1549],{"class":48},[42,3703,3704],{"class":114}," null",[42,3706,1487],{"class":56},[42,3708,3709],{"class":48},"throw",[42,3711,3712],{"class":114}," UserNotFoundException",[42,3714,3691],{"class":56},[42,3716,3717,3720],{"class":44,"line":374},[42,3718,3719],{"class":48},"    return",[42,3721,3722],{"class":56}," user;\n",[42,3724,3725],{"class":44,"line":394},[42,3726,3727],{"class":56},"  }\n",[42,3729,3730],{"class":44,"line":414},[42,3731,643],{"class":56},[42,3733,3734],{"class":44,"line":419},[42,3735,64],{"emptyLinePlaceholder":63},[42,3737,3738],{"class":44,"line":424},[42,3739,3740],{"class":83},"\u002F\u002F ── Tests inject the mock ──\n",[42,3742,3743,3745,3747],{"class":44,"line":436},[42,3744,70],{"class":48},[42,3746,74],{"class":73},[42,3748,77],{"class":56},[42,3750,3751,3753,3755,3758],{"class":44,"line":444},[42,3752,90],{"class":73},[42,3754,93],{"class":56},[42,3756,3757],{"class":52},"'UserService'",[42,3759,99],{"class":56},[42,3761,3762,3764,3766],{"class":44,"line":453},[42,3763,111],{"class":48},[42,3765,3595],{"class":114},[42,3767,3768],{"class":56}," mockRepo;\n",[42,3770,3771,3773,3775],{"class":44,"line":462},[42,3772,111],{"class":48},[42,3774,2334],{"class":114},[42,3776,2337],{"class":56},[42,3778,3779],{"class":44,"line":470},[42,3780,64],{"emptyLinePlaceholder":63},[42,3782,3783,3785],{"class":44,"line":475},[42,3784,135],{"class":73},[42,3786,138],{"class":56},[42,3788,3789,3792,3794,3796],{"class":44,"line":481},[42,3790,3791],{"class":56},"      mockRepo ",[42,3793,147],{"class":48},[42,3795,3595],{"class":114},[42,3797,317],{"class":56},[42,3799,3800,3803,3805,3807,3810],{"class":44,"line":486},[42,3801,3802],{"class":56},"      service ",[42,3804,147],{"class":48},[42,3806,2334],{"class":114},[42,3808,3809],{"class":56},"(mockRepo);  ",[42,3811,3812],{"class":83},"\u002F\u002F inject mock\n",[42,3814,3815,3818,3820,3822,3825,3827,3829,3832,3834,3836,3838],{"class":44,"line":492},[42,3816,3817],{"class":73},"      registerFallbackValue",[42,3819,93],{"class":56},[42,3821,3450],{"class":114},[42,3823,3824],{"class":56},"(id",[42,3826,757],{"class":48},[42,3828,1552],{"class":114},[42,3830,3831],{"class":56},", name",[42,3833,757],{"class":48},[42,3835,3022],{"class":52},[42,3837,408],{"class":56},[42,3839,3840],{"class":83},"\u002F\u002F for any() with value types\n",[42,3842,3843],{"class":44,"line":498},[42,3844,193],{"class":56},[42,3846,3847],{"class":44,"line":510},[42,3848,64],{"emptyLinePlaceholder":63},[42,3850,3851,3853,3855,3858,3860,3862],{"class":44,"line":521},[42,3852,234],{"class":73},[42,3854,93],{"class":56},[42,3856,3857],{"class":52},"'getUser returns user from repo'",[42,3859,621],{"class":56},[42,3861,534],{"class":48},[42,3863,537],{"class":56},[42,3865,3866,3869,3871,3873,3876,3878,3880,3882,3884,3886],{"class":44,"line":526},[42,3867,3868],{"class":48},"      when",[42,3870,338],{"class":56},[42,3872,341],{"class":48},[42,3874,3875],{"class":56}," mockRepo.",[42,3877,3456],{"class":73},[42,3879,93],{"class":56},[42,3881,170],{"class":114},[42,3883,2422],{"class":56},[42,3885,2425],{"class":73},[42,3887,1666],{"class":56},[42,3889,3890,3893,3895,3897,3900,3902,3904,3906,3908,3910,3913],{"class":44,"line":540},[42,3891,3892],{"class":56},"        (_) ",[42,3894,534],{"class":48},[42,3896,2437],{"class":48},[42,3898,3899],{"class":114}," User",[42,3901,3824],{"class":56},[42,3903,757],{"class":48},[42,3905,760],{"class":114},[42,3907,3831],{"class":56},[42,3909,757],{"class":48},[42,3911,3912],{"class":52}," 'Alice'",[42,3914,1358],{"class":56},[42,3916,3917],{"class":44,"line":565},[42,3918,3919],{"class":56},"      );\n",[42,3921,3922],{"class":44,"line":570},[42,3923,64],{"emptyLinePlaceholder":63},[42,3925,3926,3929,3931,3933,3935,3937,3939,3941,3943],{"class":44,"line":575},[42,3927,3928],{"class":48},"      final",[42,3930,2459],{"class":56},[42,3932,147],{"class":48},[42,3934,548],{"class":48},[42,3936,2466],{"class":56},[42,3938,2469],{"class":73},[42,3940,93],{"class":56},[42,3942,170],{"class":114},[42,3944,173],{"class":56},[42,3946,3947],{"class":44,"line":587},[42,3948,64],{"emptyLinePlaceholder":63},[42,3950,3951,3953,3955,3957,3959,3961],{"class":44,"line":601},[42,3952,247],{"class":73},[42,3954,2486],{"class":56},[42,3956,259],{"class":73},[42,3958,93],{"class":56},[42,3960,2493],{"class":52},[42,3962,266],{"class":56},[42,3964,3965,3968,3970,3972,3974,3976,3978,3980,3982,3984,3986,3988],{"class":44,"line":606},[42,3966,3967],{"class":73},"      verify",[42,3969,338],{"class":56},[42,3971,341],{"class":48},[42,3973,3875],{"class":56},[42,3975,3456],{"class":73},[42,3977,93],{"class":56},[42,3979,170],{"class":114},[42,3981,2422],{"class":56},[42,3983,2522],{"class":73},[42,3985,93],{"class":56},[42,3987,170],{"class":114},[42,3989,173],{"class":56},[42,3991,3992],{"class":44,"line":611},[42,3993,193],{"class":56},[42,3995,3996],{"class":44,"line":635},[42,3997,478],{"class":56},[42,3999,4000],{"class":44,"line":640},[42,4001,643],{"class":56},[14,4003,4005],{"id":4004},"tips-tricks","💡 Tips & Tricks",[4007,4008,4009,4024,4042,4069,4096],"ul",{},[4010,4011,4012,4016,4017,4020,4021,4023],"li",{},[4013,4014,4015],"strong",{},"Idiom",": name tests as sentences — ",[19,4018,4019],{},"test('Stack.pop returns the top item', ...)"," — the output reads like a spec: \"Stack.pop returns the top item ... PASSED.\" Describes behavior, not implementation. Use ",[19,4022,21],{}," for the class\u002Ffeature name.",[4010,4025,4026,4028,4029,4031,4032,4034,4035,4037,4038,4041],{},[4013,4027,4015],{},": use ",[19,4030,25],{},"\u002F",[19,4033,28],{}," for fresh state per test — each test gets a clean instance. Avoids test interdependence (one test's side effects don't affect another). ",[19,4036,25],{}," runs before EACH test; ",[19,4039,4040],{},"setUpAll"," runs once (use for expensive setup, but state isn't reset).",[4010,4043,4044,4046,4047,4049,4050,4053,4054,4056,4057,4060,4061,4064,4065,4068],{},[4013,4045,4015],{},": prefer ",[19,4048,2227],{}," over ",[19,4051,4052],{},"mockito"," — ",[19,4055,2227],{}," doesn't need code generation (",[19,4058,4059],{},"dart run build_runner build","), just ",[19,4062,4063],{},"extends Mock implements X",". Simpler setup, same functionality. Use ",[19,4066,4067],{},"when(() => ...)"," with a lambda.",[4010,4070,4071,4073,4074,4076,4077,4080,4081,4084,4085,4087,4088,4076,4090,4092,4093,553],{},[4013,4072,4015],{},": make async tests ",[19,4075,534],{}," and ",[19,4078,4079],{},"await"," — a test function that returns a ",[19,4082,4083],{},"Future"," but isn't ",[19,4086,534],{}," finishes before the future completes (false positive). Make it ",[19,4089,534],{},[19,4091,4079],{}," the result, then ",[19,4094,4095],{},"expect",[4010,4097,4098,4100,4101,4104,4105,4108,4109,4112,4113,553],{},[4013,4099,4015],{},": test file naming — ",[19,4102,4103],{},"test\u002Fuser_test.dart"," for ",[19,4106,4107],{},"lib\u002Fuser.dart",". One test file per source file. ",[19,4110,4111],{},"dart test"," only picks up ",[19,4114,4115],{},"test\u002F**\u002F*_test.dart",[14,4117,4119],{"id":4118},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[4007,4121,4122,4141,4159,4180,4196,4217,4232,4251,4260,4286],{},[4010,4123,4124,4130,4131,4133,4134,4136,4137,4140],{},[4013,4125,4126,4127],{},"Test files must end in ",[19,4128,4129],{},"_test.dart",": ",[19,4132,4111],{}," only picks up files matching ",[19,4135,4115],{},". A file named ",[19,4138,4139],{},"test\u002Fstrings.dart"," is ignored.",[4010,4142,4143,4130,4148,4151,4152,4155,4156,553],{},[4013,4144,4145,4147],{},[19,4146,4095],{}," without a matcher is invalid",[19,4149,4150],{},"expect(value)"," doesn't compile. Use ",[19,4153,4154],{},"expect(value, equals(...))"," or ",[19,4157,4158],{},"expect(value, isTrue)",[4010,4160,4161,4169,4170,4172,4173,4175,4176,4076,4178,553],{},[4013,4162,4163,4164,4031,4166,4168],{},"Async tests must be ",[19,4165,534],{},[19,4167,4079],{},"ed",": a test function that returns a ",[19,4171,4083],{}," but isn't marked ",[19,4174,534],{}," may finish before the future completes — false positive. Make it ",[19,4177,534],{},[19,4179,4079],{},[4010,4181,4182,4192,4193,4195],{},[4013,4183,4184,4186,4187,4191],{},[19,4185,25],{}," runs before ",[4188,4189,4190],"em",{},"each"," test",": not once for the whole group. If setup is expensive, use ",[19,4194,4040],{}," (runs once before all tests) — but state isn't reset between tests.",[4010,4197,4198,4130,4207,4209,4210,4212,4213,4216],{},[4013,4199,4200,4203,4204],{},[19,4201,4202],{},"throwsException"," vs ",[19,4205,4206],{},"throwsA(isA\u003CX>())",[19,4208,4202],{}," checks for an ",[19,4211,2188],{},"; ",[19,4214,4215],{},"throwsA(isA\u003CArgumentError>())"," checks for a specific type. Use the specific form for custom exceptions.",[4010,4218,4219,4130,4222,4225,4226,4228,4229,4231],{},[4013,4220,4221],{},"Mockito needs code generation",[19,4223,4224],{},"@GenerateMocks"," + ",[19,4227,4059],{}," generates the mock file. Forgetting the build step gives \"missing mock file\" errors. ",[19,4230,2227],{}," avoids this.",[4010,4233,4234,4239,4240,4243,4244,4247,4248,4250],{},[4013,4235,4236],{},[19,4237,4238],{},"verify(...).called(1)",": checks the method was called exactly once. ",[19,4241,4242],{},"called(0)"," (never), ",[19,4245,4246],{},"called(greaterThan(1))"," (at least twice). Forgetting ",[19,4249,2522],{}," means you're not verifying the call.",[4010,4252,4253,4256,4257,4259],{},[4013,4254,4255],{},"Test isolation",": tests should be independent — one test's failure shouldn't cascade. Use ",[19,4258,25],{}," for fresh state; avoid shared mutable state across tests.",[4010,4261,4262,4130,4271,4273,4274,4276,4277,4279,4280,4283,4284,553],{},[4013,4263,4264,4104,4267,4270],{},[19,4265,4266],{},"registerFallbackValue",[19,4268,4269],{},"any()"," with value types",[19,4272,2227],{}," needs a fallback value for ",[19,4275,4269],{}," with non-nullable value types (e.g., ",[19,4278,3450],{},"). Call ",[19,4281,4282],{},"registerFallbackValue(User(...))"," in ",[19,4285,25],{},[4010,4287,4288,4028,4294,4296,4297,4299,4300,4076,4303,4306,4307,4309,4310,553],{},[4013,4289,4290,4293],{},[19,4291,4292],{},"expectLater"," for async matchers",[19,4295,4292],{}," (not ",[19,4298,4095],{},") with ",[19,4301,4302],{},"completion()",[19,4304,4305],{},"throwsA()"," — it returns a ",[19,4308,4083],{}," you must ",[19,4311,4079],{},[14,4313,4315],{"id":4314},"spot-the-bug","🧠 Spot the Bug",[4317,4318,4319],"p",{},"A developer writes an async test, but it passes even when the code is broken:",[30,4321,4322],{"language":32},[34,4323,4325],{"className":36,"code":4324,"language":32,"meta":38,"style":38},"test('fetch returns data', () {\n  var future = fetchData();\n  expect(future, completion(equals('hello')));\n});\n",[19,4326,4327,4337,4349,4367],{"__ignoreMap":38},[42,4328,4329,4331,4333,4335],{"class":44,"line":45},[42,4330,1793],{"class":73},[42,4332,93],{"class":56},[42,4334,1798],{"class":52},[42,4336,99],{"class":56},[42,4338,4339,4341,4343,4345,4347],{"class":44,"line":60},[42,4340,1805],{"class":48},[42,4342,1808],{"class":56},[42,4344,147],{"class":48},[42,4346,1813],{"class":73},[42,4348,317],{"class":56},[42,4350,4351,4353,4355,4357,4359,4361,4363,4365],{"class":44,"line":67},[42,4352,1820],{"class":73},[42,4354,1823],{"class":56},[42,4356,1826],{"class":73},[42,4358,93],{"class":56},[42,4360,259],{"class":73},[42,4362,93],{"class":56},[42,4364,886],{"class":52},[42,4366,1098],{"class":56},[42,4368,4369],{"class":44,"line":80},[42,4370,1860],{"class":56},[4317,4372,4373],{},"Why is this a false positive?",[4375,4376,4377,4381,4397,4408,4523],"details",{},[4378,4379,4380],"summary",{},"Answer",[4317,4382,4383,4384,4389,4390,4392,4393,4396],{},"The test function is ",[4013,4385,4386,4387],{},"not ",[19,4388,534],{}," and doesn't ",[19,4391,4079],{},". ",[19,4394,4395],{},"expect(future, completion(...))"," registers an async expectation, but the test function returns immediately (synchronously) — the test framework thinks the test is done before the future completes. The async assertion may not run, so the test passes vacuously (false positive).",[4317,4398,4399,4400,4076,4402,4404,4405,4407],{},"The fix — make the test ",[19,4401,534],{},[19,4403,4079],{}," the expectation (or ",[19,4406,4079],{}," the future directly):",[34,4409,4411],{"className":36,"code":4410,"language":32,"meta":38,"style":38},"\u002F\u002F Option 1: await the expectation\ntest('fetch returns data', () async {\n  var future = fetchData();\n  await expectLater(future, completion(equals('hello')));\n});\n\n\u002F\u002F Option 2: await the future, then expect (cleaner)\ntest('fetch returns data', () async {\n  var data = await fetchData();\n  expect(data, equals('hello'));\n});\n",[19,4412,4413,4418,4432,4444,4464,4468,4472,4477,4491,4505,4519],{"__ignoreMap":38},[42,4414,4415],{"class":44,"line":45},[42,4416,4417],{"class":83},"\u002F\u002F Option 1: await the expectation\n",[42,4419,4420,4422,4424,4426,4428,4430],{"class":44,"line":60},[42,4421,1793],{"class":73},[42,4423,93],{"class":56},[42,4425,1798],{"class":52},[42,4427,621],{"class":56},[42,4429,534],{"class":48},[42,4431,537],{"class":56},[42,4433,4434,4436,4438,4440,4442],{"class":44,"line":67},[42,4435,1805],{"class":48},[42,4437,1808],{"class":56},[42,4439,147],{"class":48},[42,4441,1813],{"class":73},[42,4443,317],{"class":56},[42,4445,4446,4448,4450,4452,4454,4456,4458,4460,4462],{"class":44,"line":80},[42,4447,1947],{"class":48},[42,4449,1950],{"class":73},[42,4451,1823],{"class":56},[42,4453,1826],{"class":73},[42,4455,93],{"class":56},[42,4457,259],{"class":73},[42,4459,93],{"class":56},[42,4461,886],{"class":52},[42,4463,1098],{"class":56},[42,4465,4466],{"class":44,"line":87},[42,4467,1860],{"class":56},[42,4469,4470],{"class":44,"line":102},[42,4471,64],{"emptyLinePlaceholder":63},[42,4473,4474],{"class":44,"line":108},[42,4475,4476],{"class":83},"\u002F\u002F Option 2: await the future, then expect (cleaner)\n",[42,4478,4479,4481,4483,4485,4487,4489],{"class":44,"line":127},[42,4480,1793],{"class":73},[42,4482,93],{"class":56},[42,4484,1798],{"class":52},[42,4486,621],{"class":56},[42,4488,534],{"class":48},[42,4490,537],{"class":56},[42,4492,4493,4495,4497,4499,4501,4503],{"class":44,"line":132},[42,4494,1805],{"class":48},[42,4496,1891],{"class":56},[42,4498,147],{"class":48},[42,4500,548],{"class":48},[42,4502,1813],{"class":73},[42,4504,317],{"class":56},[42,4506,4507,4509,4511,4513,4515,4517],{"class":44,"line":141},[42,4508,1820],{"class":73},[42,4510,1906],{"class":56},[42,4512,259],{"class":73},[42,4514,93],{"class":56},[42,4516,886],{"class":52},[42,4518,266],{"class":56},[42,4520,4521],{"class":44,"line":159},[42,4522,1860],{"class":56},[4317,4524,4525,4526,4031,4528,4530,4531,4533,4534,4536],{},"With ",[19,4527,534],{},[19,4529,4079],{},", the test function returns a ",[19,4532,4083],{},", and the framework waits for it to complete before evaluating pass\u002Ffail. Option 2 (await the future, then ",[19,4535,4095],{},") is cleaner and more common.",[4538,4539,4540],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":38,"searchDepth":60,"depth":60,"links":4542},[4543,4545,4546,4547,4549,4550,4551,4552,4553],{"id":16,"depth":60,"text":4544},"Test Structure — group, setUp, tearDown",{"id":646,"depth":60,"text":647},{"id":1763,"depth":60,"text":1764},{"id":2223,"depth":60,"text":4548},"Mocking — mocktail (No Code Generation)",{"id":2929,"depth":60,"text":2930},{"id":3380,"depth":60,"text":3381},{"id":4004,"depth":60,"text":4005},{"id":4118,"depth":60,"text":4119},{"id":4314,"depth":60,"text":4315},"Deep-dive into Dart's test framework, matcher composition, async test semantics, mocktail vs mockito, parameterized testing, and test isolation patterns. Code-first engineering reference.","md",{},"\u002Fdart\u002F14-testing",{"title":5,"description":4554},"dart\u002F14-testing","7jyciA3gW5DKtC-NHhdtJZ-JKO9_a_to1LdbQXCWijo",1789924647954]