Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(452)

Unified Diff: pkg/analysis_server/test/typed_mock.dart

Issue 257783005: verify() for mocks (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/test/typed_mock.dart
diff --git a/pkg/analysis_server/test/typed_mock.dart b/pkg/analysis_server/test/typed_mock.dart
index 74b19abf8979351b6132ac40a8b8efcbdb2af1dd..0b991257f7fc64c78fd99b8bd84b28af02a36d49 100644
--- a/pkg/analysis_server/test/typed_mock.dart
+++ b/pkg/analysis_server/test/typed_mock.dart
@@ -9,7 +9,10 @@ _InvocationMatcher _lastMatcher;
/// method is called.
Behavior when(_ignored) {
try {
- var behavior = new Behavior();
+ var mock = _lastMatcher._mock;
+ mock._removeLastInvocation();
+ // set behavior
+ var behavior = new Behavior._(_lastMatcher);
_lastMatcher._behavior = behavior;
return behavior;
} finally {
@@ -18,13 +21,34 @@ Behavior when(_ignored) {
}
}
+/// Verifies certain behavior happened a specified number of times.
+Verifier verify(_ignored) {
+ try {
+ var mock = _lastMatcher._mock;
+ mock._removeLastInvocation();
+ // set verifier
+ return new Verifier._(mock, _lastMatcher);
+ } finally {
+ // clear to prevent memory leak
+ _lastMatcher = null;
+ }
+}
+
+/// [VerifyError] is thrown when one of the [verify] checks fails.
+class VerifyError {
+ final String message;
+ VerifyError(this.message);
+ String toString() => 'VerifyError: $message';
+}
class _InvocationMatcher {
- List<ArgumentMatcher> _matchers = [];
+ final Symbol _member;
+ final TypedMock _mock;
+ final List<ArgumentMatcher> _matchers = [];
Behavior _behavior;
- _InvocationMatcher(Invocation invocation) {
+ _InvocationMatcher(this._mock, this._member, Invocation invocation) {
invocation.positionalArguments.forEach((argument) {
ArgumentMatcher matcher;
if (argument is ArgumentMatcher) {
@@ -52,7 +76,12 @@ class _InvocationMatcher {
}
}
+
class Behavior {
+ final _InvocationMatcher _matcher;
+
+ Behavior._(this._matcher);
+
bool _thenFunctionEnabled = false;
Function _thenFunction;
@@ -115,8 +144,8 @@ class Behavior {
// list
if (_returnListEnabled) {
if (_returnListIndex >= _returnList.length) {
- throw new StateError('List of ${_returnList.length} elements'
- ' $_returnList has been exhausted.');
+ throw new StateError('All ${_returnList.length} elements for '
+ '${_matcher._member} from $_returnList have been exhausted.');
}
return _returnList[_returnListIndex++];
}
@@ -130,29 +159,98 @@ class Behavior {
}
+class Verifier {
+ final TypedMock _mock;
+ final _InvocationMatcher _matcher;
+
+ Verifier._(this._mock, this._matcher);
+
+ void never() {
+ times(0);
+ }
+
+ void once() {
+ times(1);
+ }
+
+ void times(int expected) {
+ var times = _count();
+ if (times != expected) {
+ var member = _matcher._member;
+ throw new VerifyError('$expected expected, but $times'
+ ' invocations of $member recorded.');
+ }
+ }
+
+ void atLeast(int expected) {
+ var times = _count();
+ if (times < expected) {
+ var member = _matcher._member;
+ throw new VerifyError('At least $expected expected, but only $times'
+ ' invocations of $member recorded.');
+ }
+ }
+
+ void atMost(int expected) {
+ var times = _count();
+ if (times > expected) {
+ var member = _matcher._member;
+ throw new VerifyError('At most $expected expected, but $times'
+ ' invocations of $member recorded.');
+ }
+ }
+
+ int _count() {
+ var times = 0;
+ _mock._invocations.forEach((invocation) {
+ if (invocation.memberName != _matcher._member) {
+ return;
+ }
+ if (!_matcher.match(invocation)) {
+ return;
+ }
+ times++;
+ });
+ return times;
+ }
+}
+
+
class TypedMock {
- final Map<Symbol, List<_InvocationMatcher>> _invocationMatchersMap = {};
+ final Map<Symbol, List<_InvocationMatcher>> _matchersMap = {};
+
+ final List<Invocation> _invocations = [];
noSuchMethod(Invocation invocation) {
+ _invocations.add(invocation);
var member = invocation.memberName;
// prepare invocation matchers
- var matchers = _invocationMatchersMap[member];
+ var matchers = _matchersMap[member];
if (matchers == null) {
matchers = [];
- _invocationMatchersMap[member] = matchers;
+ _matchersMap[member] = matchers;
}
// check if there is a matcher
for (var matcher in matchers) {
if (matcher.match(invocation)) {
_lastMatcher = matcher;
- return matcher._behavior._getReturnValue(invocation);
+ // generate value if there is a behavior
+ if (matcher._behavior != null) {
+ return matcher._behavior._getReturnValue(invocation);
+ }
+ // probably verification
+ return null;
}
}
// add a new matcher
- _InvocationMatcher matcher = new _InvocationMatcher(invocation);
+ var matcher = new _InvocationMatcher(this, member, invocation);
matchers.add(matcher);
_lastMatcher = matcher;
}
+
+ void _removeLastInvocation() {
+ _invocations.removeLast();
+ }
}
« no previous file with comments | « no previous file | pkg/analysis_server/test/typed_mock_test.dart » ('j') | pkg/analysis_server/test/typed_mock_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698