Skip to content

Commit

Permalink
refactor!: v0.0.1-dev.6 (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored Jan 9, 2021
1 parent 88948b9 commit 6969998
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 117 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.0.1-dev.6

- **BREAKING** refactor: use `Symbol` rather than `String`
- fix: strict argument matching on verification when arguments are provided

# 0.0.1-dev.5

- fix: override `toString` on `MocktailFailure` to improve visibility of failure messages
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,53 +38,53 @@ The `MockCat` instance can then be used to stub and verify calls.

```dart
// Stub the `sound` method.
when(cat).calls('sound').thenReturn('meow');
when(cat).calls(#sound).thenReturn('meow');
// Interact with the mock cat instance.
cat.sound();
// Verify the interaction occurred (with matcher support).
verify(cat).calls('sound').times(equals(1));
verify(cat).calls(#sound).times(equals(1));
```

## Additional Usage

```dart
// Stub a method before interacting with the mock.
when(cat).calls('sound').thenReturn('purrr!');
when(cat).calls(#sound).thenReturn('purrr!');
expect(cat.sound(), 'purrr!');
// You can interact with the mock multiple times.
expect(cat.sound(), 'purrr!');
// You can change the stub.
when(cat).calls('sound').thenReturn('meow');
when(cat).calls(#sound).thenReturn('meow');
expect(cat.sound(), 'meow');
// You can stub getters.
when(cat).calls('lives').thenReturn(10);
when(cat).calls(#lives).thenReturn(10);
expect(cat.lives, 10);
// You can stub a method for specific arguments.
when(cat).calls('likes').withArgs(
when(cat).calls(#likes).withArgs(
positional: ['fish'],
named: {'isHungry': false},
named: {#isHungry: false},
).thenReturn(true);
expect(cat.likes('fish'), isTrue);
// You can verify the interaction for specific arguments.
verify(cat).calls('likes').withArgs(
verify(cat).calls(#likes).withArgs(
positional: ['fish'],
named: {'isHungry': false},
named: {#isHungry: false},
).times(1);
// You can stub a method to throw.
when(cat).calls('sound').thenThrow(Exception('oops'));
when(cat).calls(#sound).thenThrow(Exception('oops'));
expect(() => cat.sound(), throwsA(isA<Exception>()));
// You can calculate stubs dynamically.
final sounds = ['purrr', 'meow'];
when(cat).calls('sound').thenAnswer((_) => sounds.removeAt(0));
when(cat).calls(#sound).thenAnswer((_) => sounds.removeAt(0));
expect(cat.sound(), 'purrr');
expect(cat.sound(), 'meow');
```
Expand Down
12 changes: 6 additions & 6 deletions example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,25 @@ void main() {

test('example', () {
// Stub a method before interacting with the mock.
when(cat).calls('sound').thenReturn('purr');
when(cat).calls(#sound).thenReturn('purr');

// Interact with the mock.
expect(cat.sound(), 'purr');

// Verify the interaction.
verify(cat).calls('sound').times(1);
verify(cat).calls(#sound).times(1);

// Stub a method with parameters
when(cat).calls('likes').withArgs(
when(cat).calls(#likes).withArgs(
positional: ['fish'],
named: {'isHungry': false},
named: {#isHungry: false},
).thenReturn(true);
expect(cat.likes('fish'), isTrue);

// Verify the interaction.
verify(cat).calls('likes').withArgs(
verify(cat).calls(#likes).withArgs(
positional: ['fish'],
named: {'isHungry': false},
named: {#isHungry: false},
).times(1);
});
});
Expand Down
76 changes: 48 additions & 28 deletions lib/src/mocktail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,30 +187,30 @@ class _WhenCall {
const _WhenCall(this._object);
final Mock _object;

_StubFunction calls(String memberName) => _StubFunction(_object, memberName);
_StubFunction calls(Symbol memberName) => _StubFunction(_object, memberName);
}

class _VerifyCall {
const _VerifyCall(this._object);
final Mock _object;

_VerifyArgsCall calls(String memberName) =>
_VerifyArgsCall calls(Symbol memberName) =>
_VerifyArgsCall(_object, memberName);
}

class _VerifyArgsCall extends _CallCountCall {
_VerifyArgsCall(
Mock object,
String memberName, {
Symbol memberName, {
Iterable<Object?>? positionalArguments,
Map<String, Object?>? namedArguments,
Map<Symbol, Object?>? namedArguments,
}) : super(object, memberName,
positionalArguments: positionalArguments,
namedArguments: namedArguments);

_CallCountCall withArgs({
Iterable<Object?>? positional,
Map<String, Object?>? named,
Map<Symbol, Object?>? named,
}) {
return _CallCountCall(
_object,
Expand All @@ -224,9 +224,9 @@ class _VerifyArgsCall extends _CallCountCall {
class _CallCountCall extends _MockInvocationCall {
_CallCountCall(
Mock object,
String memberName, {
Symbol memberName, {
Iterable<Object?>? positionalArguments,
Map<String, Object?>? namedArguments,
Map<Symbol, Object?>? namedArguments,
}) : super(object, memberName,
positionalArguments: positionalArguments,
namedArguments: namedArguments);
Expand All @@ -237,22 +237,38 @@ class _CallCountCall extends _MockInvocationCall {
// Lax Invocation Verification (any)
if (_positionalArguments == null && _namedArguments == null) {
for (final entry in _object._stubs.entries) {
if (entry.key.memberName == Symbol(_memberName)) {
if (entry.key.memberName == _memberName) {
actualCallCount += entry.value.callCount;
}
}
}
// Strict Invocation Verification
else {
final stub = _object._stubs[_invocation] ??
_object._stubs[_Invocation(memberName: Symbol(_memberName))];
actualCallCount = stub?.callCount ?? 0;
final strictStub = _object._stubs[_invocation];
if (strictStub != null) {
actualCallCount = strictStub.callCount;
} else {
final laxStub = _object._stubs[_Invocation(memberName: _memberName)];
final invocation = laxStub?._invocation;
final positionalArgsMatch = _listEquals<Object?>(
invocation?.positionalArguments,
_positionalArguments?.toList() ?? <Object?>[],
);
final namedArgsMatch = _mapEquals<Symbol, Object?>(
invocation?.namedArguments,
_namedArguments ?? <Symbol, Object?>{},
);

if (positionalArgsMatch && namedArgsMatch) {
actualCallCount = laxStub?._callCount ?? 0;
}
}
}

final matcher = wrapMatcher(callCount);
if (!matcher.matches(actualCallCount, <dynamic, dynamic>{})) {
throw MocktailFailure(
'''Expected ${_object.runtimeType}.$_memberName to be called ${matcher.describe(StringDescription())} time(s) but actual call count was <$actualCallCount>.''',
'''Expected ${_object.runtimeType}.${_memberName.value} to be called ${matcher.describe(StringDescription())} time(s) but actual call count was <$actualCallCount>.''',
);
}
}
Expand All @@ -261,16 +277,16 @@ class _CallCountCall extends _MockInvocationCall {
class _StubFunction extends _MockInvocationCall {
_StubFunction(
Mock object,
String memberName, {
Symbol memberName, {
Iterable<Object?>? positionalArguments,
Map<String, Object?>? namedArguments,
Map<Symbol, Object?>? namedArguments,
}) : super(object, memberName,
positionalArguments: positionalArguments,
namedArguments: namedArguments);

_StubFunction withArgs({
Iterable<Object?>? positional,
Map<String, Object?>? named,
Map<Symbol, Object?>? named,
}) {
return _StubFunction(
_object,
Expand Down Expand Up @@ -299,39 +315,35 @@ class _MockInvocationCall {
this._object,
this._memberName, {
Iterable<Object?>? positionalArguments,
Map<String, Object?>? namedArguments,
Map<Symbol, Object?>? namedArguments,
}) : _positionalArguments = positionalArguments,
_namedArguments = namedArguments;

final Mock _object;
final String _memberName;
final Symbol _memberName;
final Iterable<Object?>? _positionalArguments;
final Map<String, Object?>? _namedArguments;
final Map<Symbol, Object?>? _namedArguments;

_Invocation get _invocation {
if (_positionalArguments == null && _namedArguments == null) {
return _Invocation(memberName: Symbol(_memberName));
return _Invocation(memberName: _memberName);
}
if (_positionalArguments != null && _namedArguments == null) {
return _Invocation(
memberName: Symbol(_memberName),
memberName: _memberName,
positionalArguments: _positionalArguments!,
);
}
if (_positionalArguments == null && _namedArguments != null) {
return _Invocation(
memberName: Symbol(_memberName),
namedArguments: _namedArguments!.map<Symbol, Object?>(
(key, value) => MapEntry(Symbol(key), value),
),
memberName: _memberName,
namedArguments: _namedArguments!,
);
}
return _Invocation(
memberName: Symbol(_memberName),
memberName: _memberName,
positionalArguments: _positionalArguments!,
namedArguments: _namedArguments!.map<Symbol, Object?>(
(key, value) => MapEntry(Symbol(key), value),
),
namedArguments: _namedArguments!,
);
}
}
Expand All @@ -357,3 +369,11 @@ bool _mapEquals<T, U>(Map<T, U>? a, Map<T, U>? b) {
}
return true;
}

final _memberNameRegExp = RegExp(r'Symbol\("(.*?)"\)');

extension on Symbol {
String get value {
return _memberNameRegExp.firstMatch(toString())?.group(1) ?? toString();
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: mocktail
description: A Dart mocking library which simplifies mocking with null safety support and no manual mocks or code generation.
version: 0.0.1-dev.5
version: 0.0.1-dev.6
repository: https://github.com/felangel/mocktail
homepage: https://github.com/felangel/mocktail

Expand Down
Loading

0 comments on commit 6969998

Please sign in to comment.