blob: 53ca02195511d45c0f144f03254d59b68a891f43 [file] [log] [blame] [view]
# Frequently asked questions
#### How do I mock a static method, constructor, or top-level function?
Mockito provides its stubbing and verification features by overriding class
instance methods. Since there is no mechanism for overriding static methods,
constructors, or top-level functions, mockito cannot mock them. They are what
they are.
One idea to consider is "Do I need to use mocks to test this code?" For
example, the [test_descriptor package] allows testing file system concepts using
real files, and the [test_process package] supports testing subprocesses using
real subprocesses. `dart:io` also includes an [IOOverrides] class and a
[runWithIOOverrides] function that can be used to mock out the filesystem.
[IOOverrides]: https://api.dart.dev/stable/2.7.2/dart-io/IOOverrides-class.html
[runWithIOOverrides]: https://api.dart.dev/stable/2.7.2/dart-io/IOOverrides/runWithIOOverrides.html
If mocking is still desired, the underlying code may be refactored in order to
enable mocking. One way to get around un-mockable constructors is to change the
function in which the constructor is being called. Instead of constructing an
object, accept one.
```dart
// BEFORE:
void f() {
var foo = Foo();
// ...
}
// AFTER
void f(Foo foo) {
// ...
}
```
In tests, you can declare a MockFoo class which implements Foo, and pass such
an object to `f`.
You can also refactor code which makes much use of such constructors or static
methods to use a wrapper system. For example, instead of calling
`Directory.current` or `new File` throughout your code, use the
[file package]. You can start with a [FileSystem] object (a [LocalFileSystem]
for production and a [MemoryFileSystem] for tests), and use its wrapper methods
([`currentDirectory`] replaces `Directory.current`, [`file()`] replaces
`File()`). Another example of this pattern is the [io package] and its
[ProcessManager] class.
[test_descriptor package]: https://pub.dev/documentation/test_descriptor
[test_process package]: https://pub.dev/packages/test_process
[file package]: https://pub.dev/packages/file
[FileSystem]: https://pub.dev/documentation/file/latest/file/FileSystem-class.html
[LocalFileSystem]: https://pub.dev/documentation/file/latest/local/LocalFileSystem-class.html
[MemoryFileSystem]: https://pub.dev/documentation/file/latest/memory/MemoryFileSystem-class.html
[`currentDirectory`]: https://pub.dev/documentation/file/latest/file/FileSystem/currentDirectory.html
[`file()`]: https://pub.dev/documentation/file/latest/file/FileSystem/file.html
[io package]: https://pub.dev/packages/io
[ProcessManager]: https://pub.dev/documentation/io/latest/io/ProcessManager-class.html
#### How do I mock an extension method?
If there is no way to override some kind of function, then mockito cannot mock
it. See the above answer for further explanation, and alternatives.
#### Why can a method call not be verified multiple times?
When mockito verifies a method call (via [`verify`] or [`verifyInOrder`]), it
marks the call as "verified", which excludes the call from further
verifications. For example:
```dart
cat.eatFood("fish");
verify(cat.eatFood("fish")); // This call succeeds.
verify(cat.eatFood(any)); // This call fails.
```
In order to make multiple reasonings about a call, for example to assert on the
arguments, make one verification call, and save the captured arguments:
```dart
cat.hunt("home", "birds");
var captured = verify(cat.hunt(captureAny, captureAny)).captured.single;
expect(captured[0], equals("home"));
expect(captured[1], equals("birds"));
```
If you need to verify the number of types a method was called, _and_ capture the
arguments, save the verification object:
```dart
cat.hunt("home", "birds");
cat.hunt("home", "lizards");
var verification = verify(cat.hunt("home", captureAny));
verification.called(greaterThan(2));
var firstCall = verification.captured[0];
var secondCall = verification.captured[1];
expect(firstCall, equals(["birds"]));
expect(secondCall, equals(["lizards"]));
```
#### How does mockito work?
The basics of the `Mock` class are nothing special: It uses `noSuchMethod` to
catch all method invocations, and returns the value that you have configured
beforehand with `when()` calls.
The implementation of `when()` is a bit more tricky. Take this example:
```dart
// Unstubbed methods return null:
expect(cat.sound(), nullValue);
// Stubbing - before execution:
when(cat.sound()).thenReturn("Purr");
```
Since `cat.sound()` returns `null`, how can the `when()` call configure it?
It works, because `when` is not a function, but a top level getter that
_returns_ a function. Before returning the function, it sets a flag
(`_whenInProgress`), so that all `Mock` objects know to return a "matcher"
(internally `_WhenCall`) instead of the expected value. As soon as the function
has been invoked `_whenInProgress` is set back to `false` and Mock objects
behave as normal.
Argument matchers work by storing the wrapped arguments, one after another,
until the `when` (or `verify`) call gathers everything that has been stored,
and creates an InvocationMatcher with the arguments. This is a simple process
for positional arguments: the order in which the arguments has been stored
should be preserved for matching an invocation. Named arguments are trickier:
their evaluation order is not specified, so if Mockito blindly stored them in
the order of their evaluation, it wouldn't be able to match up each argument
matcher with the correct name. This is why each named argument matcher must
repeat its own name. `foo: anyNamed('foo')` tells Mockito to store an argument
matcher for an invocation under the name 'foo'.
> **Be careful** never to write `when;` (without the function call) anywhere.
> This would set `_whenInProgress` to `true`, and the next mock invocation will
> return an unexpected value.
The same goes for "chaining" mock objects in a test call. This will fail:
```dart
var mockUtils = MockUtils();
var mockStringUtils = MockStringUtils();
// Setting up mockUtils.stringUtils to return a mock StringUtils implementation
when(mockUtils.stringUtils).thenReturn(mockStringUtils);
// Some tests
// FAILS!
verify(mockUtils.stringUtils.uppercase()).called(1);
// Instead use this:
verify(mockStringUtils.uppercase()).called(1);
```
This fails, because `verify` sets an internal flag, so mock objects don't
return their mocked values anymore but their matchers. So
`mockUtils.stringUtils` will *not* return the mocked `stringUtils` object you
put inside.
You can look at the `when` and `Mock.noSuchMethod` implementations to see how
it's done. It's very straightforward.
[`verify`]: https://pub.dev/documentation/mockito/latest/mockito/verify.html
[`verifyInOrder`]: https://pub.dev/documentation/mockito/latest/mockito/verifyInOrder.html