blob: 5e3676d506dfb5a10ef058b5aa21062559df901a [file] [log] [blame] [view] [edit]
# 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
## How can I customize where Mockito outputs its mocks?
Mockito supports configuration of outputs by the configuration provided by the `build`
package by creating (if it doesn't exist already) the `build.yaml` at the root folder
of the project.
It uses the `build_extensions` option, which can be used to alter not only the output directory but you
can also do other filename manipulation, eg.: append/prepend strings to the filename or add another extension
to the filename.
To use `build_extensions` you can use `^` on the input string to match on the project root, and `{{}}` to capture the remaining path/filename.
You can also have multiple build_extensions options, but they can't conflict with each other.
For consistency, the output pattern must always end with `.mocks.dart` and the input pattern must always end with `.dart`.
If you specify a build extension, you **MUST** ensure that your patterns cover all input files that you want generate mocks from. Failing to do so will lead to the unmatched file from not being generated at all.
```yaml
targets:
$default:
builders:
mockito|mockBuilder:
generate_for:
options:
# build_extensions takes a source pattern and if it matches it will transform the output
# to your desired path. The default behaviour is to the .mocks.dart file to be in the same
# directory as the source .dart file. As seen below this is customizable, but the generated
# file must always end in `.mocks.dart`.
build_extensions:
'^tests/{{}}.dart' : 'tests/mocks/{{}}.mocks.dart'
'^integration-tests/{{}}.dart' : 'integration-tests/{{}}.mocks.dart'
```
Also, you can also check out the example configuration in the Mockito repository.
## How do I mock a `sealed` class?
`sealed` clases cannot be `extend`ed nor `implement`ed (outside of their Dart
library) and therefore cannot be mocked.
## How do I mock a class that requires instances of a `sealed` class?
Suppose that you have code such as:
```dart
class Bar {
int value;
Bar(this.value);
Bar.zero() : value = 0;
}
class Foo {
Bar someMethod(int value) => Bar(value);
}
```
and now you want to mock `Foo`. A mock implementation of `Foo`, generated by
`@GenerateNiceMocks([MockSpec<Foo>()])`, needs to return *something* if
`someMethod` is called. It can't return `null` since its return type is
non-nullable. It can't construct a `Bar` on its own without understanding the
semantics of `Bar`'s constructors. That is, which `Bar` constructor should be
called and with what arguments? To avoid this, Mockito instead generates its
own, fake implementation of `Bar` that it does know how to construct, something
`like:
```dart
class _FakeBar extends Fake implements Bar {}
```
And then `MockFoo` can have its `someMethod` override return a `_FakeBar`
instance.
However, if `Bar` is `sealed` (or is marked with `base` or `final`), then it
cannot be `implemented` in generated code. Therefore Mockito can't generate a
default value for a `Bar` on its own, and it needs users to specify the default
value to use via `provideDummy` or `provideDummyBuilder`:
```dart
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
@GenerateNiceMocks([MockSpec<Foo>()])
import 'foo.mocks.dart';
sealed class Bar {
int value;
Bar(this.value);
Bar.zero() : value = 0;
}
class Foo {
Bar someMethod(int value) => Bar(value);
}
void main() {
provideDummy(Bar.zero());
var mockFoo = MockFoo();
}
```
Note that the value used as the "dummy" usually doesn't matter since methods on
the mock typically will be stubbed, overriding the method's return value.