@@ -11,6 +11,8 @@ _WhenCall _whenCall = null;
11
11
final List <_VerifyCall > _verifyCalls = < _VerifyCall > [];
12
12
final _TimeStampProvider _timer = new _TimeStampProvider ();
13
13
final List _capturedArgs = [];
14
+ final List <_ArgMatcher > _typedArgs = < _ArgMatcher > [];
15
+ final Map <String , _ArgMatcher > _typedNamedArgs = < String , _ArgMatcher > {};
14
16
15
17
class Mock {
16
18
final List <RealCall > _realCalls = < RealCall > [];
@@ -24,6 +26,9 @@ class Mock {
24
26
}
25
27
26
28
dynamic noSuchMethod (Invocation invocation) {
29
+ if (_typedArgs.isNotEmpty || _typedNamedArgs.isNotEmpty) {
30
+ invocation = _reconstituteInvocation (invocation);
31
+ }
27
32
if (_whenInProgress) {
28
33
_whenCall = new _WhenCall (this , invocation);
29
34
return null ;
@@ -48,6 +53,119 @@ class Mock {
48
53
String toString () => _givenName != null ? _givenName : runtimeType.toString ();
49
54
}
50
55
56
+ // Return a new [Invocation], reconstituted from [invocation], [_typedArgs],
57
+ // and [_typedNamedArgs].
58
+ Invocation _reconstituteInvocation (Invocation invocation) {
59
+ var newInvocation = new FakeInvocation (invocation);
60
+ _typedArgs.clear ();
61
+ _typedNamedArgs.clear ();
62
+ return newInvocation;
63
+ }
64
+
65
+ /// An Invocation class with a functional constructor.
66
+ class FakeInvocation extends Invocation {
67
+ final Symbol memberName;
68
+ final Map <Symbol , dynamic > namedArguments;
69
+ final List <dynamic > positionalArguments;
70
+ final bool isGetter;
71
+ final bool isMethod;
72
+ final bool isSetter;
73
+
74
+ factory FakeInvocation (Invocation invocation) {
75
+ if (_typedArgs.isEmpty && _typedNamedArgs.isEmpty) {
76
+ return null ;
77
+ }
78
+ var positionalArguments = < dynamic > [];
79
+ var namedArguments = < Symbol , dynamic > {};
80
+
81
+ // Handle named arguments first, so that we can provide useful errors for
82
+ // the various bad states. If all is well with the named arguments, then we
83
+ // can process the positional arguments, and resort to more general errors
84
+ // if the state is still bad.
85
+ var _typedNamedArgSymbols = _typedNamedArgs.keys.map ((name) => new Symbol (name));
86
+ invocation.namedArguments.forEach ((name, arg) {
87
+ if (arg == null ) {
88
+ if (! _typedNamedArgSymbols.contains (name)) {
89
+ // Incorrect usage of [typed], something like:
90
+ // `when(obj.fn(a: typed(any)))`.
91
+ throw new ArgumentError (
92
+ 'A typed argument was passed in as a named argument named "$name ", '
93
+ 'but did not a value for its name. Each typed argument that is '
94
+ 'passed as a named argument needs to specify the `name` argument. '
95
+ 'For example: `when(obj.fn(x: typed(any, name: "x")))`.' );
96
+ }
97
+ } else {
98
+ // Add each real named argument that was _not_ passed with [typed].
99
+ namedArguments[name] = arg;
100
+ }
101
+ });
102
+
103
+ _typedNamedArgs.forEach ((name, arg) {
104
+ Symbol nameSymbol = new Symbol (name);
105
+ if (! invocation.namedArguments.containsKey (nameSymbol)) {
106
+ // Incorrect usage of [name], something like:
107
+ // `when(obj.fn(typed(any, name: 'a')))`.
108
+ throw new ArgumentError (
109
+ 'A typed argument was declared with name $name , but was not passed '
110
+ 'as an argument named $name .' );
111
+ }
112
+ if (invocation.namedArguments[nameSymbol] != null ) {
113
+ // Incorrect usage of [name], something like:
114
+ // `when(obj.fn(a: typed(any, name: 'b'), b: "string"))`.
115
+ throw new ArgumentError (
116
+ 'A typed argument was declared with name $name , but a different '
117
+ 'value (${invocation .namedArguments [nameSymbol ]}) was passed as '
118
+ '$name .' );
119
+ }
120
+ namedArguments[nameSymbol] = arg;
121
+ });
122
+
123
+ var nullPositionalArguments =
124
+ invocation.positionalArguments.where ((arg) => arg == null );
125
+ if (_typedArgs.length != nullPositionalArguments.length) {
126
+ throw new ArgumentError (
127
+ 'null arguments are not allowed alongside typed(); use '
128
+ '"typed(eq(null))"' );
129
+ }
130
+ int i = 0 ;
131
+ int j = 0 ;
132
+ while (i < _typedArgs.length && j < invocation.positionalArguments.length) {
133
+ var arg = _typedArgs[i];
134
+ if (invocation.positionalArguments[j] == null ) {
135
+ // [typed] was used; add the [_ArgMatcher] given to [typed].
136
+ positionalArguments.add (arg);
137
+ i++ ;
138
+ j++ ;
139
+ } else {
140
+ // [typed] was not used; add the [_ArgMatcher] from [invocation].
141
+ positionalArguments.add (invocation.positionalArguments[j]);
142
+ j++ ;
143
+ }
144
+ }
145
+ while (j < invocation.positionalArguments.length) {
146
+ // Some trailing non-[typed] arguments.
147
+ positionalArguments.add (invocation.positionalArguments[j]);
148
+ j++ ;
149
+ }
150
+
151
+ return new FakeInvocation ._(
152
+ invocation.memberName,
153
+ positionalArguments,
154
+ namedArguments,
155
+ invocation.isGetter,
156
+ invocation.isMethod,
157
+ invocation.isSetter);
158
+ }
159
+
160
+ FakeInvocation ._(
161
+ this .memberName,
162
+ this .positionalArguments,
163
+ this .namedArguments,
164
+ this .isGetter,
165
+ this .isMethod,
166
+ this .isSetter);
167
+ }
168
+
51
169
named (var mock, {String name, int hashCode}) => mock
52
170
.._givenName = name
53
171
.._givenHashCode = hashCode;
@@ -291,6 +409,15 @@ get captureAny => new _ArgMatcher(anything, true);
291
409
captureThat (Matcher matcher) => new _ArgMatcher (matcher, true );
292
410
argThat (Matcher matcher) => new _ArgMatcher (matcher, false );
293
411
412
+ /*=T*/ typed/*<T>*/ (_ArgMatcher matcher, {String name}) {
413
+ if (name == null ) {
414
+ _typedArgs.add (matcher);
415
+ } else {
416
+ _typedNamedArgs[name] = matcher;
417
+ }
418
+ return null ;
419
+ }
420
+
294
421
class VerificationResult {
295
422
List captured = [];
296
423
int callCount;
@@ -404,3 +531,14 @@ void logInvocations(List<Mock> mocks) {
404
531
print (inv.toString ());
405
532
});
406
533
}
534
+
535
+ /// Should only be used during Mockito testing.
536
+ void resetMockitoState () {
537
+ _whenInProgress = false ;
538
+ _verificationInProgress = false ;
539
+ _whenCall = null ;
540
+ _verifyCalls.clear ();
541
+ _capturedArgs.clear ();
542
+ _typedArgs.clear ();
543
+ _typedNamedArgs.clear ();
544
+ }
0 commit comments