blob: 9742c3a94ae5395b57a46995d0b49eae50d30d39 [file] [log] [blame]
// ignore_for_file: sdk_version_async_exported_from_core
// ignore_for_file: unawaited_futures
import 'package:mockito/mockito.dart';
import 'package:test/test.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;
}
// Mock class
class MockCat extends Mock implements Cat {}
// Fake class
class FakeCat extends Fake implements Cat {
@override
bool eatFood(String food, {bool hungry}) {
print('Fake eat $food');
return true;
}
}
void main() {
Cat cat;
setUp(() {
// Create mock object.
cat = MockCat();
});
test("Let's verify some behaviour!", () {
// Interact with the mock object.
cat.sound();
// Verify the interaction.
verify(cat.sound());
});
test("How about some stubbing?", () {
// Unstubbed methods return null.
expect(cat.sound(), null);
// 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.
var 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 aguments 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", () {
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", () {
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", () {
cat.sound();
verify(cat.sound());
verifyNoMoreInteractions(cat);
});
test("Capturing arguments for further assertions", () {
// 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 {
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("Fake class", () {
// Create a new fake Cat at runtime.
var cat = FakeCat();
cat.eatFood("Milk"); // Prints 'Fake eat Milk'.
expect(() => cat.sleep(), throwsUnimplementedError);
});
}