mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Remove typed_mock
Bug: https://github.com/dart-lang/sdk/issues/32271 Change-Id: I32d7708f269b9618f6a031f0323f5cea646066f3 Reviewed-on: https://dart-review.googlesource.com/42862 Reviewed-by: Kevin Moore <kevmoo@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
ebd16bd33f
commit
fae3a83629
|
@ -101,7 +101,6 @@ test_reflective_loader:third_party/pkg/test_reflective_loader/lib
|
|||
testing:pkg/testing/lib
|
||||
tuple:third_party/pkg/tuple/lib
|
||||
typed_data:third_party/pkg/typed_data/lib
|
||||
typed_mock:pkg/typed_mock/lib
|
||||
unittest:third_party/pkg/unittest/lib
|
||||
usage:third_party/pkg/usage/lib
|
||||
utf:third_party/pkg/utf/lib
|
||||
|
|
|
@ -81,7 +81,6 @@ Future main(List<String> arguments) async {
|
|||
await compileModule('meta');
|
||||
if (isTravis) {
|
||||
await compileModule('microlytics', libs: ['html_channels']);
|
||||
await compileModule('typed_mock');
|
||||
}
|
||||
|
||||
// Under third_party/pkg.
|
||||
|
|
|
@ -52,7 +52,6 @@ vm/testcases/*: SkipByDesign # These are not tests but input for tests.
|
|||
|
||||
[ $compiler == dart2analyzer ]
|
||||
dev_compiler/test/options/*: SkipByDesign
|
||||
typed_mock/test/typed_mock_test: StaticWarning
|
||||
|
||||
[ $compiler != dart2analyzer ]
|
||||
analyzer/test/src/summary/summarize_fasta_test: RuntimeError, Slow
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
Copyright 2014, the Dart project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,214 +0,0 @@
|
|||
A library for mocking classes and verifying expected interaction with mocks.
|
||||
|
||||
It is inspired by [Mockito](https://code.google.com/p/mockito/).
|
||||
|
||||
Features of this package:
|
||||
- Code-completion, static validation, search and refactoring all work properly with mocks.
|
||||
- Much better error messages for unit testing.
|
||||
- Works with concrete and abstract classes.
|
||||
- Does not use mirrors.
|
||||
- No dependent packages.
|
||||
|
||||
Other Mock libraries for Dart:
|
||||
- https://pub.dartlang.org/packages/mockito
|
||||
- https://pub.dartlang.org/packages/mock (deprecated)
|
||||
|
||||
## Tutorial
|
||||
|
||||
Let's take the simple case of making sure that a method is called. The first step is to create a mock of the object, as shown below. One nice feature of Dart is that all classes automatically define interfaces, so you don't need to separately define the interface.
|
||||
|
||||
```dart
|
||||
import 'package:typed_mock/typed_mock.dart';
|
||||
|
||||
class Dog {
|
||||
String speak() => "Woof!";
|
||||
}
|
||||
|
||||
class MockDog extends TypedMock implements Dog {
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
```
|
||||
|
||||
All of the magic happens because of the noSuchMethod() function. None of the functions for Animal are actually defined because we used `implements` instead of `extends`. Therefore, all calls to this object will end up in `noSuchMethod()`.
|
||||
|
||||
#### Verify a function is called
|
||||
|
||||
Here's the code to verify that the function is called:
|
||||
|
||||
```dart
|
||||
|
||||
void main() {
|
||||
final dog = new MockDog();
|
||||
verifyZeroInteractions(dog);
|
||||
dog.speak();
|
||||
verify(dog.speak()).once();
|
||||
verifyNoMoreInteractions(dog);
|
||||
}
|
||||
```
|
||||
|
||||
One of the interesting features of typed_mock is that it internally tracks all calls to each mock object, then tracks which calls have been matched with a verify() call and which not. Therefore, typed_mock is able to detect unexpected calls, even if those calls are made to methods that didn't exist when the test was written. This can be a good incentive to update your tests whenever you change a class.
|
||||
|
||||
After creating the `MockAnimal` object, we call `verifyZeroInteractions()` to make sure that the object starts in a clean state. Next we call the `speak()` method, then prove that the speak function was actually called with `verify().once()`.
|
||||
|
||||
There are several other functions for verifying calls that can be used instead of `once()`:
|
||||
- `atLeastOnce()` Ensure the function was called one or more times.
|
||||
- `times(n)` Ensure the function was called exactly `n` times.
|
||||
- `atLeast(n)` Ensure the function was called `n` or more times.
|
||||
- `atMost(n)` Ensure the function was called no more than `n` times.
|
||||
- `any()` Mark the function call as verified if it was called, but don't fail if it wasn't called.
|
||||
- `never()` Ensure the function was never called. It's often better to use `verifyNoMoreInteractions()` instead.
|
||||
|
||||
#### Configure the mock to return a value
|
||||
|
||||
Here's how to return a value from `speak()`:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final dog = new MockDog();
|
||||
when(dog.speak()).thenReturn("woof");
|
||||
final s = dog.speak();
|
||||
print("$s");
|
||||
verify(dog.speak()).once();
|
||||
verifyNoMoreInteractions(dog);
|
||||
}
|
||||
```
|
||||
|
||||
What if `speak()` took the name of an animal as a parameter? When typed_mock tracks a function call, the call tracking is based on the function name and the parameters. For example:
|
||||
|
||||
|
||||
```dart
|
||||
import 'package:typed_mock/typed_mock.dart';
|
||||
|
||||
abstract class Animal {
|
||||
String speak();
|
||||
}
|
||||
|
||||
class MockAnimal extends TypedMock implements Animal {
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
void main() {
|
||||
final animal = new MockAnimal();
|
||||
when(animal.speak("dog")).thenReturn("woof");
|
||||
final s = animal.speak();
|
||||
print("$s");
|
||||
verify(animal.speak("dog")).once();
|
||||
}
|
||||
```
|
||||
|
||||
Note that you can reset the call and verify tracking using `resetInteractions()`. However, there is no way to reset the `when()` calls. Create a new mock instead.
|
||||
|
||||
You can define different results based on the value of the parameter. Notice that the calls to `verify()` explicitly states the parameter value:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final animal = new MockAnimal();
|
||||
when(animal.speak("cat")).thenReturn("meow");
|
||||
when(animal.speak("dog")).thenReturn("woof");
|
||||
final s = animal.speak("cat"); // Prints: meow
|
||||
verify(animal.speak("cat")).once();
|
||||
verify(animal.speak("dog")).never();
|
||||
}
|
||||
```
|
||||
|
||||
#### Match any value for a parameter
|
||||
|
||||
Sometimes you don't care about the exact value of the parameter. That's when `anyString` is used, along with its siblings `anyInt`, `anyBool` and `anyObject`.
|
||||
|
||||
The value `anyString` is a matcher that matches any String value. For example, here's how to use `anyString` in a call to `when()`:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final animal = new MockAnimal();
|
||||
when(animal.speak(anyString)).thenReturn("meow");
|
||||
final s1 = animal.speak("cat");
|
||||
final s2 = animal.speak("dog");
|
||||
print("$s1 $s2"); // Prints: meow meow
|
||||
verify(animal.speak(anyString)).times(2);
|
||||
}
|
||||
```
|
||||
|
||||
You can also use `anyString` in `verify()` calls, even if the `when()` calls use exact values. For example:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final animal = new MockAnimal();
|
||||
when(animal.speak("cat")).thenReturn("meow");
|
||||
when(animal.speak("dog")).thenReturn("woof");
|
||||
var s
|
||||
s = animal.speak("cat");
|
||||
s = animal.speak("cat");
|
||||
s = animal.speak("dog");
|
||||
verify(animal.speak(anyString)).times(3);
|
||||
}
|
||||
```
|
||||
|
||||
You can use `anyString` as the parameter for calculated values:
|
||||
```dart
|
||||
when(animal.speak(anyString)).thenInvoke((String s) => 'The $s speaks!');
|
||||
```
|
||||
|
||||
In addition to `thenReturn()` and `thenInvoke()`, typed_mock supports `thenReturnList()` and `thenThrow()`. See the link at the end of this document for examples.
|
||||
|
||||
#### Mocking operator[] and operator[]=
|
||||
|
||||
The typed_mock package is able to track set and get access with operators `[]=` and `[]`, respectively. There's nothing special about these operators - they are just functions with non-alphanumeric names that takes two or one parameters. As with other functions, typed_mock tracks both the index and the value as needed. Note the syntax to verify that a particular array element was assigned a particular value. The act of assigning true is tracked separately from the act of assigning false. The syntax is straightforward.
|
||||
|
||||
```dart
|
||||
import 'package:typed_mock/typed_mock.dart';
|
||||
|
||||
abstract class Tracker {
|
||||
operator [](int index);
|
||||
operator []=(int index, bool b);
|
||||
}
|
||||
|
||||
class MockTracker extends TypedMock implements Tracker {
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
void main() {
|
||||
final tracker = new MockTracker();
|
||||
tracker[2] = true;
|
||||
when(tracker[3]).thenReturn(false);
|
||||
when(tracker[4]).thenReturn(true);
|
||||
bool x = tracker[3];
|
||||
bool y = tracker[4];
|
||||
print("$x $y");
|
||||
verify(tracker[1] = true).never();
|
||||
verify(tracker[2] = false).never();
|
||||
verify(tracker[2] = true).once();
|
||||
verify(tracker[3]).once();
|
||||
verify(tracker[4]).once();
|
||||
verify(tracker[5]).never();
|
||||
}
|
||||
```
|
||||
|
||||
#### Passing mocks as closures
|
||||
|
||||
Passing a mock as a function parameter may not behave as you expect because a hidden function is called in the mock to get the closure. The solution is to wrap the call in a separate closure. For example, the call to `verifyNoMoreInteractions()` fails because the reference to `dog.speak` caused a hidden function to be called in `MockDog`.
|
||||
|
||||
```dart
|
||||
void doSomething(String myfunc()) {}
|
||||
|
||||
void main() {
|
||||
final dog = new MockDog();
|
||||
doSomething(dog.speak);
|
||||
verifyNoMoreInteractions(dog);
|
||||
}
|
||||
```
|
||||
|
||||
The solution is as follows:
|
||||
|
||||
```dart
|
||||
void doSomething(String myfunc()) {}
|
||||
|
||||
void main() {
|
||||
final dog = new MockDog();
|
||||
doSomething(() => dog.speak());
|
||||
verifyNoMoreInteractions(dog);
|
||||
}
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
For additional examples, see the [unit tests](https://github.com/dart-lang/sdk/blob/master/pkg/typed_mock/test/typed_mock_test.dart).
|
|
@ -1,426 +0,0 @@
|
|||
library typed_mock;
|
||||
|
||||
_InvocationMatcher _lastMatcher;
|
||||
|
||||
/// Enables stubbing methods.
|
||||
///
|
||||
/// Use it when you want the mock to return a particular value when a particular
|
||||
/// method, getter or setter is called.
|
||||
///
|
||||
/// when(obj.testProperty).thenReturn(10);
|
||||
/// expect(obj.testProperty, 10); // pass
|
||||
///
|
||||
/// You can specify multiple matchers, which are checked one after another.
|
||||
///
|
||||
/// when(obj.testMethod(anyInt)).thenReturn('was int');
|
||||
/// when(obj.testMethod(anyString)).thenReturn('was String');
|
||||
/// expect(obj.testMethod(42), 'was int'); // pass
|
||||
/// expect(obj.testMethod('foo'), 'was String'); // pass
|
||||
///
|
||||
/// You can even provide a function to calculate results.
|
||||
/// Function can be also used to capture invocation arguments (if you test some
|
||||
/// consumer).
|
||||
///
|
||||
/// when(obj.testMethod(anyInt)).thenInvoke((int p) => 10 + p);
|
||||
/// expect(obj.testMethod(1), 11); // pass
|
||||
/// expect(obj.testMethod(5), 15); // pass
|
||||
Behavior when(_ignored) {
|
||||
try {
|
||||
var mock = _lastMatcher._mock;
|
||||
mock._removeLastInvocation();
|
||||
// set behavior
|
||||
var behavior = new Behavior._(_lastMatcher);
|
||||
_lastMatcher._behavior = behavior;
|
||||
return behavior;
|
||||
} finally {
|
||||
// clear to prevent memory leak
|
||||
_lastMatcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears all interactions remembered so far.
|
||||
resetInteractions(TypedMock mock) {
|
||||
mock._invocations.clear();
|
||||
mock._verifiedInvocations.clear();
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that the given mock doesn't have any unverified interaction.
|
||||
void verifyNoMoreInteractions(TypedMock mock) {
|
||||
var notVerified = mock._computeNotVerifiedInvocations();
|
||||
// OK
|
||||
if (notVerified.isEmpty) {
|
||||
return;
|
||||
}
|
||||
// fail
|
||||
var invocationsString = _getInvocationsString(notVerified);
|
||||
throw new VerifyError('Unexpected interactions:\n$invocationsString');
|
||||
}
|
||||
|
||||
/// Verifies that no interactions happened on the given mock.
|
||||
void verifyZeroInteractions(TypedMock mock) {
|
||||
var invocations = mock._invocations;
|
||||
// OK
|
||||
if (invocations.isEmpty) {
|
||||
return;
|
||||
}
|
||||
// fail
|
||||
var invocationsString = _getInvocationsString(invocations);
|
||||
throw new VerifyError('Unexpected interactions:\n$invocationsString');
|
||||
}
|
||||
|
||||
/// [VerifyError] is thrown when one of the [verify] checks fails.
|
||||
class VerifyError {
|
||||
final String message;
|
||||
VerifyError(this.message);
|
||||
String toString() => 'VerifyError: $message';
|
||||
}
|
||||
|
||||
String _getInvocationsString(Iterable<Invocation> invocations) {
|
||||
var buffer = new StringBuffer();
|
||||
invocations.forEach((invocation) {
|
||||
var member = invocation.memberName;
|
||||
buffer.write(member);
|
||||
buffer.write(' ');
|
||||
buffer.write(invocation.positionalArguments);
|
||||
buffer.write(' ');
|
||||
buffer.write(invocation.namedArguments);
|
||||
buffer.writeln();
|
||||
});
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
class _InvocationMatcher {
|
||||
final Symbol _member;
|
||||
final TypedMock _mock;
|
||||
final List<ArgumentMatcher> _matchers = [];
|
||||
|
||||
Behavior _behavior;
|
||||
|
||||
_InvocationMatcher(this._mock, this._member, Invocation invocation) {
|
||||
invocation.positionalArguments.forEach((argument) {
|
||||
ArgumentMatcher matcher;
|
||||
if (argument is ArgumentMatcher) {
|
||||
matcher = argument;
|
||||
} else {
|
||||
matcher = new _ArgumentMatcher_equals(argument);
|
||||
}
|
||||
_matchers.add(matcher);
|
||||
});
|
||||
}
|
||||
|
||||
bool match(Invocation invocation) {
|
||||
var arguments = invocation.positionalArguments;
|
||||
if (arguments.length != _matchers.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < _matchers.length; i++) {
|
||||
var matcher = _matchers[i];
|
||||
var argument = arguments[i];
|
||||
if (!matcher.matches(argument)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Behavior {
|
||||
final _InvocationMatcher _matcher;
|
||||
|
||||
Behavior._(this._matcher);
|
||||
|
||||
bool _thenFunctionEnabled = false;
|
||||
Function _thenFunction;
|
||||
|
||||
bool _returnAlwaysEnabled = false;
|
||||
var _returnAlways;
|
||||
|
||||
bool _returnListEnabled = false;
|
||||
List _returnList;
|
||||
int _returnListIndex;
|
||||
|
||||
bool _throwExceptionEnabled = false;
|
||||
var _throwException;
|
||||
|
||||
/// Invokes the given [function] with actual arguments and returns its result.
|
||||
Behavior thenInvoke(Function function) {
|
||||
_reset();
|
||||
_thenFunctionEnabled = true;
|
||||
_thenFunction = function;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Returns the specific value.
|
||||
Behavior thenReturn(value) {
|
||||
_reset();
|
||||
_returnAlwaysEnabled = true;
|
||||
_returnAlways = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Returns values from the [list] starting from first to the last.
|
||||
/// If the end of list is reached a [StateError] is thrown.
|
||||
Behavior thenReturnList(List list) {
|
||||
_reset();
|
||||
_returnListEnabled = true;
|
||||
_returnList = list;
|
||||
_returnListIndex = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Throws the specified [exception] object.
|
||||
Behavior thenThrow(exception) {
|
||||
_reset();
|
||||
_throwExceptionEnabled = true;
|
||||
_throwException = exception;
|
||||
return this;
|
||||
}
|
||||
|
||||
_reset() {
|
||||
_thenFunctionEnabled = false;
|
||||
_returnAlwaysEnabled = false;
|
||||
_returnListEnabled = false;
|
||||
_throwExceptionEnabled = false;
|
||||
}
|
||||
|
||||
dynamic _getReturnValue(Invocation invocation) {
|
||||
// function
|
||||
if (_thenFunctionEnabled) {
|
||||
return Function.apply(_thenFunction, invocation.positionalArguments,
|
||||
invocation.namedArguments);
|
||||
}
|
||||
// always
|
||||
if (_returnAlwaysEnabled) {
|
||||
return _returnAlways;
|
||||
}
|
||||
// list
|
||||
if (_returnListEnabled) {
|
||||
if (_returnListIndex >= _returnList.length) {
|
||||
throw new StateError('All ${_returnList.length} elements for '
|
||||
'${_matcher._member} from $_returnList have been exhausted.');
|
||||
}
|
||||
return _returnList[_returnListIndex++];
|
||||
}
|
||||
// exception
|
||||
if (_throwExceptionEnabled) {
|
||||
throw _throwException;
|
||||
}
|
||||
// no value
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Verifier {
|
||||
final TypedMock _mock;
|
||||
final _InvocationMatcher _matcher;
|
||||
|
||||
Verifier._(this._mock, this._matcher);
|
||||
|
||||
/// Marks matching interactions as verified and never fails.
|
||||
void any() {
|
||||
// mark as verified, but don't check the actual count
|
||||
_count();
|
||||
}
|
||||
|
||||
/// Verifies that there was no matching interactions.
|
||||
void never() {
|
||||
times(0);
|
||||
}
|
||||
|
||||
/// Verifies that there was exactly one matching interaction.
|
||||
void once() {
|
||||
times(1);
|
||||
}
|
||||
|
||||
/// Verifies that there was the specified number of matching interactions.
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that there was at least the specified number of matching
|
||||
/// interactions.
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that there was at least one matching interaction.
|
||||
void atLeastOnce() {
|
||||
var times = _count();
|
||||
if (times == 0) {
|
||||
var member = _matcher._member;
|
||||
throw new VerifyError('At least one expected, but only zero'
|
||||
' invocations of $member recorded.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that there was at most the specified number of matching
|
||||
/// interactions.
|
||||
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;
|
||||
}
|
||||
_mock._verifiedInvocations.add(invocation);
|
||||
times++;
|
||||
});
|
||||
return times;
|
||||
}
|
||||
}
|
||||
|
||||
/// A class to extend mocks from.
|
||||
/// It supports specifying behavior using [when] and validation of interactions
|
||||
/// using [verify].
|
||||
///
|
||||
/// abstract class Name {
|
||||
/// String get firstName;
|
||||
/// String get lastName;
|
||||
/// }
|
||||
/// class NameMock extends TypedMock implements Name {
|
||||
/// noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
/// }
|
||||
class TypedMock {
|
||||
final Map<Symbol, List<_InvocationMatcher>> _matchersMap = {};
|
||||
|
||||
final List<Invocation> _invocations = [];
|
||||
final Set<Invocation> _verifiedInvocations = new Set<Invocation>();
|
||||
|
||||
noSuchMethod(Invocation invocation) {
|
||||
_invocations.add(invocation);
|
||||
var member = invocation.memberName;
|
||||
// prepare invocation matchers
|
||||
var matchers = _matchersMap[member];
|
||||
if (matchers == null) {
|
||||
matchers = [];
|
||||
_matchersMap[member] = matchers;
|
||||
}
|
||||
// check if there is a matcher
|
||||
for (var matcher in matchers) {
|
||||
if (matcher.match(invocation)) {
|
||||
_lastMatcher = matcher;
|
||||
// generate value if there is a behavior
|
||||
if (matcher._behavior != null) {
|
||||
return matcher._behavior._getReturnValue(invocation);
|
||||
}
|
||||
// probably verification
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// add a new matcher
|
||||
var matcher = new _InvocationMatcher(this, member, invocation);
|
||||
matchers.add(matcher);
|
||||
_lastMatcher = matcher;
|
||||
}
|
||||
|
||||
Iterable<Invocation> _computeNotVerifiedInvocations() {
|
||||
notVerified(e) => !_verifiedInvocations.contains(e);
|
||||
return _invocations.where(notVerified);
|
||||
}
|
||||
|
||||
void _removeLastInvocation() {
|
||||
_invocations.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/// [ArgumentMatcher] checks whether the given argument satisfies some
|
||||
/// condition.
|
||||
abstract class ArgumentMatcher {
|
||||
const ArgumentMatcher();
|
||||
|
||||
/// Checks whether this matcher accepts the given argument.
|
||||
bool matches(val);
|
||||
}
|
||||
|
||||
class _ArgumentMatcher_equals extends ArgumentMatcher {
|
||||
final expected;
|
||||
|
||||
const _ArgumentMatcher_equals(this.expected);
|
||||
|
||||
@override
|
||||
bool matches(val) {
|
||||
return val == expected;
|
||||
}
|
||||
}
|
||||
|
||||
class _ArgumentMatcher_anyBool extends ArgumentMatcher {
|
||||
const _ArgumentMatcher_anyBool();
|
||||
|
||||
@override
|
||||
bool matches(val) {
|
||||
return val is bool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches any [bool] value.
|
||||
final anyBool = const _ArgumentMatcher_anyBool() as dynamic;
|
||||
|
||||
class _ArgumentMatcher_anyInt extends ArgumentMatcher {
|
||||
const _ArgumentMatcher_anyInt();
|
||||
|
||||
@override
|
||||
bool matches(val) {
|
||||
return val is int;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches any [int] value.
|
||||
final anyInt = const _ArgumentMatcher_anyInt() as dynamic;
|
||||
|
||||
class _ArgumentMatcher_anyObject extends ArgumentMatcher {
|
||||
const _ArgumentMatcher_anyObject();
|
||||
|
||||
@override
|
||||
bool matches(val) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches any [Object] (or subclass) value.
|
||||
final anyObject = const _ArgumentMatcher_anyObject() as dynamic;
|
||||
|
||||
class _ArgumentMatcher_anyString extends ArgumentMatcher {
|
||||
const _ArgumentMatcher_anyString();
|
||||
|
||||
@override
|
||||
bool matches(val) {
|
||||
return val is String;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches any [String] value.
|
||||
final anyString = const _ArgumentMatcher_anyString() as dynamic;
|
|
@ -1,9 +0,0 @@
|
|||
name: typed_mock
|
||||
version: 0.0.4
|
||||
author: Dart Team <misc@dartlang.org>
|
||||
description: A library for mocking classes using Mockito-like syntax
|
||||
homepage: http://www.dartlang.org
|
||||
environment:
|
||||
sdk: '>=1.0.0 <2.0.0'
|
||||
dev_dependencies:
|
||||
unittest: '>=0.10.0 <0.12.0'
|
|
@ -1,391 +0,0 @@
|
|||
library test.typed_mock;
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
import 'package:typed_mock/typed_mock.dart';
|
||||
|
||||
abstract class TestInterface {
|
||||
int get testProperty;
|
||||
set testProperty(x);
|
||||
int get testPropertyB;
|
||||
String testMethod0();
|
||||
String testMethod1(a);
|
||||
String testMethod2(String a, int b);
|
||||
void testMethodVoid(a);
|
||||
int operator [](index);
|
||||
}
|
||||
|
||||
class TestInterfaceMock extends TypedMock implements TestInterface {
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
main() {
|
||||
// lets make it redable
|
||||
groupSep = ' | ';
|
||||
|
||||
group('VerifyError', () {
|
||||
test('VerifyError', () {
|
||||
var error = new VerifyError('msg');
|
||||
expect(error.message, 'msg');
|
||||
expect(error.toString(), 'VerifyError: msg');
|
||||
});
|
||||
});
|
||||
|
||||
group('Matchers', () {
|
||||
test('anyBool', () {
|
||||
expect(anyBool.matches(true), true);
|
||||
expect(anyBool.matches(false), true);
|
||||
expect(anyBool.matches(0), false);
|
||||
expect(anyBool.matches('0'), false);
|
||||
});
|
||||
|
||||
test('anyInt', () {
|
||||
expect(anyInt.matches(true), false);
|
||||
expect(anyInt.matches(-99), true);
|
||||
expect(anyInt.matches(0), true);
|
||||
expect(anyInt.matches(42), true);
|
||||
expect(anyInt.matches('0'), false);
|
||||
});
|
||||
|
||||
test('anyObject', () {
|
||||
expect(anyObject.matches(true), true);
|
||||
expect(anyObject.matches(0), true);
|
||||
expect(anyObject.matches('0'), true);
|
||||
});
|
||||
|
||||
test('anyString', () {
|
||||
expect(anyString.matches(true), false);
|
||||
expect(anyString.matches(0), false);
|
||||
expect(anyString.matches(''), true);
|
||||
expect(anyString.matches('0'), true);
|
||||
expect(anyString.matches('abc'), true);
|
||||
});
|
||||
});
|
||||
|
||||
group('when', () {
|
||||
TestInterface obj;
|
||||
setUp(() {
|
||||
obj = new TestInterfaceMock();
|
||||
});
|
||||
|
||||
test('thenInvoke for getter', () {
|
||||
when(obj.testProperty).thenInvoke(() => 123);
|
||||
expect(obj.testProperty, 123);
|
||||
});
|
||||
|
||||
test('thenInvoke for method with 1 argument', () {
|
||||
when(obj.testMethod1(anyInt)).thenInvoke((int p) => p + 10);
|
||||
expect(obj.testMethod1(1), 1 + 10);
|
||||
expect(obj.testMethod1(2), 2 + 10);
|
||||
expect(obj.testMethod1(10), 10 + 10);
|
||||
});
|
||||
|
||||
test('thenReturn can replace behavior, getter', () {
|
||||
// set a behavior
|
||||
when(obj.testProperty).thenReturn(10);
|
||||
expect(obj.testProperty, 10);
|
||||
// set another behavior
|
||||
when(obj.testProperty).thenReturn(20);
|
||||
expect(obj.testProperty, 20);
|
||||
});
|
||||
|
||||
test('thenReturn for getter', () {
|
||||
when(obj.testProperty).thenReturn(42);
|
||||
expect(obj.testProperty, 42);
|
||||
expect(obj.testProperty, 42);
|
||||
});
|
||||
|
||||
test('thenReturn for []', () {
|
||||
when(obj[1]).thenReturn(10);
|
||||
when(obj[2]).thenReturn(20);
|
||||
when(obj[anyInt]).thenReturn(99);
|
||||
expect(obj[1], 10);
|
||||
expect(obj[2], 20);
|
||||
expect(obj[5], 99);
|
||||
});
|
||||
|
||||
test('thenReturn for method with 0 arguments', () {
|
||||
when(obj.testMethod0()).thenReturn('abc');
|
||||
expect(obj.testMethod0(), 'abc');
|
||||
expect(obj.testMethod0(), 'abc');
|
||||
});
|
||||
|
||||
test('thenReturn for method with 1 argument, anyBool', () {
|
||||
when(obj.testMethod1(anyBool)).thenReturn('qwerty');
|
||||
expect(obj.testMethod1(true), 'qwerty');
|
||||
expect(obj.testMethod1(false), 'qwerty');
|
||||
});
|
||||
|
||||
test('thenReturn for method with 1 argument, anyInt', () {
|
||||
when(obj.testMethod1(anyInt)).thenReturn('qwerty');
|
||||
expect(obj.testMethod1(2), 'qwerty');
|
||||
expect(obj.testMethod1(3), 'qwerty');
|
||||
});
|
||||
|
||||
test('thenReturn for method with 1 argument, anyObject', () {
|
||||
when(obj.testMethod1(anyObject)).thenReturn('qwerty');
|
||||
expect(obj.testMethod1([]), 'qwerty');
|
||||
expect(obj.testMethod1([1, 2, 3]), 'qwerty');
|
||||
});
|
||||
|
||||
test('thenReturn for method with 1 argument, argument value', () {
|
||||
when(obj.testMethod1(10)).thenReturn('ten');
|
||||
when(obj.testMethod1(20)).thenReturn('twenty');
|
||||
expect(obj.testMethod1(10), 'ten');
|
||||
expect(obj.testMethod1(10), 'ten');
|
||||
expect(obj.testMethod1(20), 'twenty');
|
||||
expect(obj.testMethod1(20), 'twenty');
|
||||
});
|
||||
|
||||
test('thenReturn for method with 2 arguments', () {
|
||||
when(obj.testMethod2(anyString, 10)).thenReturn('any+10');
|
||||
when(obj.testMethod2(anyString, 20)).thenReturn('any+20');
|
||||
when(obj.testMethod2(anyString, anyInt)).thenReturn('everything else');
|
||||
expect(obj.testMethod2('aaa', 10), 'any+10');
|
||||
expect(obj.testMethod2('bbb', 10), 'any+10');
|
||||
expect(obj.testMethod2('ccc', 20), 'any+20');
|
||||
expect(obj.testMethod2('ddd', 20), 'any+20');
|
||||
expect(obj.testMethod2('eee', 99), 'everything else');
|
||||
});
|
||||
|
||||
test('thenReturnList for getter', () {
|
||||
when(obj.testProperty).thenReturnList(['a', 'b', 'c']);
|
||||
expect(obj.testProperty, 'a');
|
||||
expect(obj.testProperty, 'b');
|
||||
expect(obj.testProperty, 'c');
|
||||
expect(() => obj.testProperty, throwsA(new isInstanceOf<StateError>()));
|
||||
});
|
||||
|
||||
test('thenThrow for getter', () {
|
||||
Exception e = new Exception();
|
||||
when(obj.testProperty).thenThrow(e);
|
||||
expect(() => obj.testProperty, throwsA(e));
|
||||
});
|
||||
|
||||
test('thenThrow for setter, anyInt', () {
|
||||
Exception e = new Exception();
|
||||
when(obj.testProperty = anyInt).thenThrow(e);
|
||||
expect(() => (obj.testProperty = 2), throwsA(e));
|
||||
});
|
||||
|
||||
test('thenThrow for setter, argument value', () {
|
||||
Exception e1 = new Exception('one');
|
||||
Exception e2 = new Exception('two');
|
||||
when(obj.testProperty = 1).thenThrow(e1);
|
||||
when(obj.testProperty = 2).thenThrow(e2);
|
||||
expect(() => (obj.testProperty = 1), throwsA(e1));
|
||||
expect(() => (obj.testProperty = 1), throwsA(e1));
|
||||
expect(() => (obj.testProperty = 2), throwsA(e2));
|
||||
expect(() => (obj.testProperty = 2), throwsA(e2));
|
||||
});
|
||||
});
|
||||
|
||||
test('resetInteractions', () {
|
||||
TestInterfaceMock obj = new TestInterfaceMock();
|
||||
obj.testProperty;
|
||||
obj.testMethod0();
|
||||
resetInteractions(obj);
|
||||
verifyZeroInteractions(obj);
|
||||
});
|
||||
|
||||
group('verify', () {
|
||||
TestInterfaceMock obj;
|
||||
setUp(() {
|
||||
obj = new TestInterfaceMock();
|
||||
});
|
||||
|
||||
group('times', () {
|
||||
test('OK, getter', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).times(2);
|
||||
});
|
||||
|
||||
test('OK, 2 getters', () {
|
||||
obj.testProperty;
|
||||
obj.testPropertyB;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).times(2);
|
||||
verify(obj.testPropertyB).times(1);
|
||||
});
|
||||
|
||||
test('OK, method with 1 argument', () {
|
||||
obj.testMethod1(10);
|
||||
obj.testMethod1('abc');
|
||||
obj.testMethod1(20);
|
||||
verify(obj.testMethod1(10)).times(1);
|
||||
verify(obj.testMethod1(20)).times(1);
|
||||
verify(obj.testMethod1(30)).times(0);
|
||||
verify(obj.testMethod1(anyInt)).times(2);
|
||||
verify(obj.testMethod1(anyString)).times(1);
|
||||
verify(obj.testMethod1(anyBool)).times(0);
|
||||
});
|
||||
|
||||
test('OK, getter, with thenReturn', () {
|
||||
when(obj.testProperty).thenReturn('abc');
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).times(2);
|
||||
});
|
||||
|
||||
test('OK, void method', () {
|
||||
obj.testMethodVoid(10);
|
||||
obj.testMethodVoid(20);
|
||||
verify(obj.testMethodVoid(anyInt)).times(2);
|
||||
});
|
||||
|
||||
test('mismatch, getter', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
expect(() {
|
||||
verify(obj.testProperty).times(2);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('never', () {
|
||||
test('OK', () {
|
||||
verify(obj.testProperty).never();
|
||||
});
|
||||
test('mismatch', () {
|
||||
obj.testProperty;
|
||||
expect(() {
|
||||
verify(obj.testProperty).never();
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('any', () {
|
||||
test('OK, 0', () {
|
||||
verify(obj.testProperty).any();
|
||||
});
|
||||
test('OK, 1', () {
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).any();
|
||||
});
|
||||
});
|
||||
|
||||
group('once', () {
|
||||
test('OK', () {
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).once();
|
||||
});
|
||||
test('mismatch, actually 0', () {
|
||||
expect(() {
|
||||
verify(obj.testProperty).once();
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
test('mismatch, actually 2', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
expect(() {
|
||||
verify(obj.testProperty).once();
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('atLeast', () {
|
||||
test('OK, 1', () {
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).atLeast(1);
|
||||
});
|
||||
test('OK, 2', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).atLeast(1);
|
||||
verify(obj.testProperty).atLeast(2);
|
||||
});
|
||||
test('mismatch', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
expect(() {
|
||||
verify(obj.testProperty).atLeast(10);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('atLeastOnce', () {
|
||||
test('OK, 1', () {
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).atLeastOnce();
|
||||
});
|
||||
test('OK, 2', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).atLeastOnce();
|
||||
});
|
||||
test('fail', () {
|
||||
expect(() {
|
||||
verify(obj.testProperty).atLeastOnce();
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('atMost', () {
|
||||
test('OK, 0', () {
|
||||
verify(obj.testProperty).atMost(5);
|
||||
verify(obj.testProperty).atMost(0);
|
||||
});
|
||||
test('OK, 2', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
verify(obj.testProperty).atMost(5);
|
||||
verify(obj.testProperty).atMost(3);
|
||||
verify(obj.testProperty).atMost(2);
|
||||
});
|
||||
test('mismatch', () {
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
obj.testProperty;
|
||||
expect(() {
|
||||
verify(obj.testProperty).atMost(2);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('verifyNoMoreInteractions', () {
|
||||
test('OK, 0', () {
|
||||
verifyNoMoreInteractions(obj);
|
||||
});
|
||||
test('OK, single method, 1', () {
|
||||
obj.testMethod1(10);
|
||||
verify(obj.testMethod1(anyInt)).any();
|
||||
verifyNoMoreInteractions(obj);
|
||||
});
|
||||
test('fail, 3', () {
|
||||
obj.testMethod1(10);
|
||||
obj.testMethod2('foo', 20);
|
||||
obj.testMethod1(30);
|
||||
expect(() {
|
||||
verifyNoMoreInteractions(obj);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
|
||||
group('verifyZeroInteractions', () {
|
||||
test('OK, 0', () {
|
||||
verifyZeroInteractions(obj);
|
||||
});
|
||||
test('OK, 0, local var', () {
|
||||
var obj = new TestInterfaceMock();
|
||||
verifyZeroInteractions(obj);
|
||||
});
|
||||
test('fail, 1, no verify()', () {
|
||||
obj.testMethod1(10);
|
||||
expect(() {
|
||||
verifyZeroInteractions(obj);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
test('fail, 1, with verify()', () {
|
||||
obj.testMethod1(10);
|
||||
verify(obj.testMethod1(anyInt)).any();
|
||||
expect(() {
|
||||
verifyZeroInteractions(obj);
|
||||
}, throwsA(_isVerifyError));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const _isVerifyError = const isInstanceOf<VerifyError>();
|
Loading…
Reference in a new issue