| import 'package:mockito/annotations.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'example.mocks.dart'; |
| |
| // Real class |
| class Cat { |
| String? sound() => 'Meow'; |
| bool? eatFood(String? food, {bool? hungry}) => true; |
| Future<void> chew() async => print('Chewing...'); |
| int? walk(List<String>? places) => 7; |
| void sleep() {} |
| void hunt(String? place, String? prey) {} |
| int lives = 9; |
| } |
| |
| // Fake class |
| class FakeCat extends Fake implements Cat { |
| @override |
| bool? eatFood(String? food, {bool? hungry}) { |
| print('Fake eat $food'); |
| return true; |
| } |
| } |
| |
| abstract class Callbacks { |
| Cat findCat(String name); |
| String? makeSound(); |
| } |
| |
| @GenerateMocks([ |
| Cat, |
| Callbacks, |
| ], customMocks: [ |
| MockSpec<Cat>( |
| as: #MockCatRelaxed, |
| onMissingStub: OnMissingStub.returnDefault, |
| ), |
| ]) |
| void main() { |
| late Cat cat; |
| |
| setUp(() { |
| // Create mock object. |
| cat = MockCat(); |
| }); |
| |
| test("Let's verify some behaviour!", () { |
| // Stub a method before interacting with it. |
| when(cat.sound()).thenReturn('Meow'); |
| |
| // Interact with the mock object. |
| cat.sound(); |
| |
| // Verify the interaction. |
| verify(cat.sound()); |
| }); |
| |
| test('How about some stubbing?', () { |
| // Unstubbed methods throw MissingStubError. |
| expect(() => cat.sound(), throwsA(isA<MissingStubError>())); |
| |
| // Stub a method before interacting with it. |
| when(cat.sound()).thenReturn('Purr'); |
| expect(cat.sound(), 'Purr'); |
| |
| // You can call it again. |
| expect(cat.sound(), 'Purr'); |
| |
| // Let's change the stub. |
| when(cat.sound()).thenReturn('Meow'); |
| expect(cat.sound(), 'Meow'); |
| |
| // You can stub getters. |
| when(cat.lives).thenReturn(9); |
| expect(cat.lives, 9); |
| |
| // You can stub a method to throw. |
| when(cat.lives).thenThrow(RangeError('Boo')); |
| expect(() => cat.lives, throwsRangeError); |
| |
| // We can calculate a response at call time. |
| final responses = ['Purr', 'Meow']; |
| when(cat.sound()).thenAnswer((_) => responses.removeAt(0)); |
| expect(cat.sound(), 'Purr'); |
| expect(cat.sound(), 'Meow'); |
| }); |
| |
| test('Argument matchers', () { |
| // You can use plain arguments themselves |
| when(cat.eatFood('fish')).thenReturn(true); |
| |
| // ... including collections |
| when(cat.walk(['roof', 'tree'])).thenReturn(2); |
| |
| // ... or matchers |
| when(cat.eatFood(argThat(startsWith('dry')))).thenReturn(false); |
| |
| // ... or mix arguments with matchers |
| when(cat.eatFood(argThat(startsWith('dry')), hungry: true)) |
| .thenReturn(true); |
| expect(cat.eatFood('fish'), isTrue); |
| expect(cat.walk(['roof', 'tree']), equals(2)); |
| expect(cat.eatFood('dry food'), isFalse); |
| expect(cat.eatFood('dry food', hungry: true), isTrue); |
| |
| // You can also verify using an argument matcher. |
| verify(cat.eatFood('fish')); |
| verify(cat.walk(['roof', 'tree'])); |
| verify(cat.eatFood(argThat(contains('food')))); |
| |
| // You can verify setters. |
| cat.lives = 9; |
| verify(cat.lives = 9); |
| |
| cat.hunt('backyard', null); |
| verify(cat.hunt('backyard', null)); // OK: no arg matchers. |
| |
| cat.hunt('backyard', null); |
| verify(cat.hunt(argThat(contains('yard')), |
| argThat(isNull))); // OK: null is wrapped in an arg matcher. |
| }); |
| |
| test('Named arguments', () { |
| // GOOD: argument matchers include their names. |
| when(cat.eatFood(any, hungry: anyNamed('hungry'))).thenReturn(true); |
| when(cat.eatFood(any, hungry: argThat(isNotNull, named: 'hungry'))) |
| .thenReturn(false); |
| when(cat.eatFood(any, hungry: captureAnyNamed('hungry'))).thenReturn(false); |
| when(cat.eatFood(any, hungry: captureThat(isNotNull, named: 'hungry'))) |
| .thenReturn(true); |
| }); |
| |
| test('Verifying exact number of invocations / at least x / never', () { |
| when(cat.sound()).thenReturn('Meow'); |
| |
| cat.sound(); |
| cat.sound(); |
| // Exact number of invocations |
| verify(cat.sound()).called(2); |
| |
| cat.sound(); |
| cat.sound(); |
| cat.sound(); |
| // Or using matcher |
| verify(cat.sound()).called(greaterThan(1)); |
| |
| // Or never called |
| verifyNever(cat.eatFood(any)); |
| }); |
| |
| test('Verification in order', () { |
| when(cat.sound()).thenReturn('Meow'); |
| when(cat.eatFood(any)).thenReturn(true); |
| |
| cat.eatFood('Milk'); |
| cat.sound(); |
| cat.eatFood('Fish'); |
| verifyInOrder([cat.eatFood('Milk'), cat.sound(), cat.eatFood('Fish')]); |
| }); |
| |
| test('Making sure interaction(s) never happened on mock', () { |
| verifyZeroInteractions(cat); |
| }); |
| |
| test('Finding redundant invocations', () { |
| when(cat.sound()).thenReturn('Meow'); |
| |
| cat.sound(); |
| verify(cat.sound()); |
| verifyNoMoreInteractions(cat); |
| }); |
| |
| test('Capturing arguments for further assertions', () { |
| when(cat.eatFood(any)).thenReturn(true); |
| |
| // Simple capture: |
| cat.eatFood('Fish'); |
| expect(verify(cat.eatFood(captureAny)).captured.single, 'Fish'); |
| |
| // Capture multiple calls: |
| cat.eatFood('Milk'); |
| cat.eatFood('Fish'); |
| expect(verify(cat.eatFood(captureAny)).captured, ['Milk', 'Fish']); |
| |
| // Conditional capture: |
| cat.eatFood('Milk'); |
| cat.eatFood('Fish'); |
| expect( |
| verify(cat.eatFood(captureThat(startsWith('F')))).captured, ['Fish']); |
| }); |
| |
| test('Waiting for an interaction', () async { |
| when(cat.eatFood(any)).thenReturn(true); |
| |
| Future<void> chewHelper(Cat cat) { |
| return cat.chew(); |
| } |
| |
| // Waiting for a call. |
| chewHelper(cat); |
| await untilCalled(cat.chew()); // This completes when cat.chew() is called. |
| |
| // Waiting for a call that has already happened. |
| cat.eatFood('Fish'); |
| await untilCalled(cat.eatFood(any)); // This completes immediately. |
| }); |
| |
| test('Mocked callbacks', () { |
| final makeSoundCallback = MockCallbacks().makeSound; |
| when(makeSoundCallback()).thenReturn('woof'); |
| expect(makeSoundCallback(), 'woof'); |
| |
| final findCatCallback = MockCallbacks().findCat; |
| final mockCat = MockCat(); |
| when(findCatCallback('Pete')).thenReturn(mockCat); |
| when(mockCat.sound()).thenReturn('meow'); |
| expect(findCatCallback('Pete').sound(), 'meow'); |
| }); |
| |
| test('Fake class', () { |
| // Create a new fake Cat at runtime. |
| final cat = FakeCat(); |
| |
| cat.eatFood('Milk'); // Prints 'Fake eat Milk'. |
| expect(() => cat.sleep(), throwsUnimplementedError); |
| }); |
| |
| test('Relaxed mock class', () { |
| // Create a new mock Cat at runtime. |
| final cat = MockCatRelaxed(); |
| |
| // You can call it without stubbing. |
| cat.sleep(); |
| |
| // Non-null properties and function return values |
| // default to reasonable defaults. |
| expect(cat.lives, 0); |
| |
| // Nullable properties and function return values |
| // default to null unless you stub them. |
| expect(cat.sound(), null); |
| expect(cat.eatFood('Milk'), null); |
| |
| verify(cat.sleep()); |
| }); |
| } |