Skip to content

Commit db19e8c

Browse files
jamesderlinsrawlins
authored andcommitted
Provide better documentation for provideDummy/provideDummyBuilder
Mostly fixes #697. I'm still not quite satisfied with the documentation for `provideDummyBuilder` since the `DummyBuilder` `typedef` remains undocumented, but I can't write documentation for that because I don't understand its arguments.
1 parent 330976e commit db19e8c

File tree

3 files changed

+91
-12
lines changed

3 files changed

+91
-12
lines changed

FAQ.md

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Frequently asked questions
22

3-
#### How do I mock a static method, constructor, or top-level function?
3+
## How do I mock a static method, constructor, or top-level function?
44

55
Mockito provides its stubbing and verification features by overriding class
66
instance methods. Since there is no mechanism for overriding static methods,
@@ -58,12 +58,12 @@ for production and a [MemoryFileSystem] for tests), and use its wrapper methods
5858
[io package]: https://pub.dev/packages/io
5959
[ProcessManager]: https://pub.dev/documentation/io/latest/io/ProcessManager-class.html
6060

61-
#### How do I mock an extension method?
61+
## How do I mock an extension method?
6262

6363
If there is no way to override some kind of function, then mockito cannot mock
6464
it. See the above answer for further explanation, and alternatives.
6565

66-
#### Why can a method call not be verified multiple times?
66+
## Why can a method call not be verified multiple times?
6767

6868
When mockito verifies a method call (via [`verify`] or [`verifyInOrder`]), it
6969
marks the call as "verified", which excludes the call from further
@@ -100,7 +100,7 @@ expect(firstCall, equals(["birds"]));
100100
expect(secondCall, equals(["lizards"]));
101101
```
102102

103-
#### How does mockito work?
103+
## How does mockito work?
104104

105105
The basics of the `Mock` class are nothing special: It uses `noSuchMethod` to
106106
catch all method invocations, and returns the value that you have configured
@@ -169,7 +169,7 @@ it's done. It's very straightforward.
169169
[`verifyInOrder`]: https://pub.dev/documentation/mockito/latest/mockito/verifyInOrder.html
170170

171171

172-
### How can I customize where Mockito outputs its mocks?
172+
## How can I customize where Mockito outputs its mocks?
173173

174174
Mockito supports configuration of outputs by the configuration provided by the `build`
175175
package by creating (if it doesn't exist already) the `build.yaml` at the root folder
@@ -203,3 +203,71 @@ targets:
203203
```
204204

205205
Also, you can also check out the example configuration in the Mockito repository.
206+
207+
208+
## How do I mock a `sealed` class?
209+
210+
Suppose that you have code such as:
211+
212+
```dart
213+
class Bar {
214+
int value;
215+
216+
Bar(this.value);
217+
218+
Bar.zero() : value = 0;
219+
}
220+
221+
class Foo {
222+
Bar someMethod(int value) => Bar(value);
223+
}
224+
```
225+
226+
and now you want to mock `Foo`. The generated implementation for `MockFoo`
227+
needs to return *something* if `someMethod` is called. It can't return `null`
228+
since its return type is non-nullable. It can't construct a `Bar` on its own
229+
without understanding the semantics of `Bar`'s constructors. That is, which
230+
`Bar` constructor should be called and with what arguments? To avoid this,
231+
Mockito instead generates its own, fake implementation of `Bar` that it does
232+
know how to construct, something like:
233+
234+
```dart
235+
class _FakeBar extends Fake implements Bar {}
236+
```
237+
238+
And then the generated implementation of `MockFoo` can have its `someMethod`
239+
override return a `_FakeBar` instance.
240+
241+
However, if `Bar` is `sealed` (or is marked with `base` or `final`), then it
242+
cannot be `implemented` in generated code. Therefore Mockito can't generate a
243+
default value for a `Bar` on its own, and it needs users to specify the default
244+
value to use via `provideDummy` or `provideDummyBuilder`:
245+
246+
```dart
247+
import 'package:mockito/annotations.dart';
248+
import 'package:mockito/mockito.dart';
249+
250+
@GenerateNiceMocks([MockSpec<Foo>()])
251+
import 'foo.mocks.dart';
252+
253+
sealed class Bar {
254+
int value;
255+
256+
Bar(this.value);
257+
258+
Bar.zero() : value = 0;
259+
}
260+
261+
class Foo {
262+
Bar someMethod(int value) => Bar(value);
263+
}
264+
265+
void main() {
266+
provideDummy(Bar.zero());
267+
268+
var mockFoo = MockFoo();
269+
}
270+
```
271+
272+
Note that the value used as the "dummy" usually doesn't matter since methods on
273+
the mock typically will be stubbed, overriding the method's return value.

lib/mockito.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// ignore_for_file: combinators_ordering
16+
1517
// ignore: deprecated_member_use
1618
export 'package:test_api/fake.dart' show Fake;
1719

1820
export 'src/dummies.dart'
19-
show provideDummy, provideDummyBuilder, MissingDummyValueError;
21+
show
22+
provideDummy,
23+
provideDummyBuilder,
24+
DummyBuilder,
25+
MissingDummyValueError;
2026
export 'src/mock.dart'
2127
show
2228
Mock,

lib/src/dummies.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,13 @@ class MissingDummyValueError {
9090
MissingDummyValueError: $type
9191
9292
This means Mockito was not smart enough to generate a dummy value of type
93-
'$type'. Please consider using either 'provideDummy' or 'provideDummyBuilder'
94-
functions to give Mockito a proper dummy value.
93+
'$type'. Due to implementation details, Mockito sometimes needs users to
94+
provide dummy values for some types, even if they plan to explicitly stub all
95+
the called methods. Call either `provideDummy` or `provideDummyBuilder` to
96+
provide Mockito with a dummy value.
9597
96-
Please note that due to implementation details Mockito sometimes needs users
97-
to provide dummy values for some types, even if they plan to explicitly stub
98-
all the called methods.
98+
For more details, see:
99+
<https://github.com/dart-lang/mockito/blob/master/FAQ.md#how-do-i-mock-a-sealed-class>
99100
''';
100101
}
101102

@@ -156,7 +157,8 @@ T dummyValue<T>(Object parent, Invocation invocation) {
156157
throw MissingDummyValueError(T);
157158
}
158159

159-
/// Provide a builder for that could create a dummy value of type `T`.
160+
/// Specifies a builder to create a dummy value of type `T`.
161+
///
160162
/// This could be useful for nice mocks, such that information about the
161163
/// specific invocation that caused the creation of a dummy value could be
162164
/// preserved.
@@ -165,6 +167,9 @@ void provideDummyBuilder<T>(DummyBuilder<T> dummyBuilder) =>
165167

166168
/// Provide a dummy value for `T` to be used both while adding expectations
167169
/// and as a default value for unstubbed methods, if using a nice mock.
170+
///
171+
/// For details and for example usage, see:
172+
/// https://github.com/dart-lang/mockito/blob/master/FAQ.md#how-do-i-mock-a-sealed-class
168173
void provideDummy<T>(T dummy) =>
169174
provideDummyBuilder((parent, invocation) => dummy);
170175

0 commit comments

Comments
 (0)