| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * The error formatter for mocking is a bit different from the default one | 6 * The error formatter for mocking is a bit different from the default one |
| 7 * for unit testing; instead of the third argument being a 'reason' | 7 * for unit testing; instead of the third argument being a 'reason' |
| 8 * it is instead a [signature] describing the method signature filter | 8 * it is instead a [signature] describing the method signature filter |
| 9 * that was used to select the logs that were verified. | 9 * that was used to select the logs that were verified. |
| 10 */ | 10 */ |
| 11 String _mockingErrorFormatter(actual, Matcher matcher, String signature) { | 11 String _mockingErrorFormatter(actual, Matcher matcher, String signature) { |
| 12 var description = new StringDescription(); | 12 var description = new StringDescription(); |
| 13 description.add('Expected ${signature} ').addDescriptionOf(matcher). | 13 description.add('Expected ${signature} ').addDescriptionOf(matcher). |
| 14 add('\n but: '); | 14 add('\n but: '); |
| 15 matcher.describeMismatch(actual, description); | 15 matcher.describeMismatch(actual, description).add('.'); |
| 16 return description.toString(); | 16 return description.toString(); |
| 17 } | 17 } |
| 18 | 18 |
| 19 /** | 19 /** |
| 20 * The failure handler for the [expect()] calls that occur in [verify()] | 20 * The failure handler for the [expect()] calls that occur in [verify()] |
| 21 * methods in the mock objects. This calls the real failure handler used | 21 * methods in the mock objects. This calls the real failure handler used |
| 22 * by the unit test library after formatting the error message with | 22 * by the unit test library after formatting the error message with |
| 23 * the custom formatter. | 23 * the custom formatter. |
| 24 */ | 24 */ |
| 25 class _MockFailureHandler implements FailureHandler { | 25 class _MockFailureHandler implements FailureHandler { |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 | 149 |
| 150 /** | 150 /** |
| 151 * Given a [method] name and list of [arguments], return true | 151 * Given a [method] name and list of [arguments], return true |
| 152 * if it matches this [CallMatcher. | 152 * if it matches this [CallMatcher. |
| 153 */ | 153 */ |
| 154 bool matches(String method, List arguments) { | 154 bool matches(String method, List arguments) { |
| 155 if (!nameFilter.matches(method)) { | 155 if (!nameFilter.matches(method)) { |
| 156 return false; | 156 return false; |
| 157 } | 157 } |
| 158 if (arguments.length < argMatchers.length) { | 158 if (arguments.length < argMatchers.length) { |
| 159 throw new Exception("Less arguments than matchers for $method"); | 159 throw new Exception("Less arguments than matchers for $method."); |
| 160 } | 160 } |
| 161 for (var i = 0; i < argMatchers.length; i++) { | 161 for (var i = 0; i < argMatchers.length; i++) { |
| 162 if (!argMatchers[i].matches(arguments[i])) { | 162 if (!argMatchers[i].matches(arguments[i])) { |
| 163 return false; | 163 return false; |
| 164 } | 164 } |
| 165 } | 165 } |
| 166 return true; | 166 return true; |
| 167 } | 167 } |
| 168 } | 168 } |
| 169 | 169 |
| (...skipping 551 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 721 * real = new Foo(); | 721 * real = new Foo(); |
| 722 * this.when(callsTo('bar')).alwaysCall(real.bar); | 722 * this.when(callsTo('bar')).alwaysCall(real.bar); |
| 723 * } | 723 * } |
| 724 * } | 724 * } |
| 725 * | 725 * |
| 726 */ | 726 */ |
| 727 class Mock { | 727 class Mock { |
| 728 /** The mock name. Needed if the log is shared; optional otherwise. */ | 728 /** The mock name. Needed if the log is shared; optional otherwise. */ |
| 729 final String name; | 729 final String name; |
| 730 | 730 |
| 731 /** The set of [behavior]s supported. */ | 731 /** The set of [Behavior]s supported. */ |
| 732 Map<String,Behavior> behaviors; | 732 Map<String,Behavior> _behaviors; |
| 733 | 733 |
| 734 /** The [log] of calls made. Only used if [name] is null. */ | 734 /** The [log] of calls made. Only used if [name] is null. */ |
| 735 LogEntryList log; | 735 LogEntryList log; |
| 736 | 736 |
| 737 /** How to handle unknown method calls - swallow or throw. */ | 737 /** How to handle unknown method calls - swallow or throw. */ |
| 738 final bool throwIfNoBehavior = false; | 738 final bool _throwIfNoBehavior; |
| 739 |
| 740 /** Whether to create an audit log or not. */ |
| 741 bool _logging; |
| 742 |
| 743 bool get logging() => _logging; |
| 744 bool set logging(bool value) { |
| 745 if (value && log == null) { |
| 746 log = new LogEntryList(); |
| 747 } |
| 748 _logging = value; |
| 749 } |
| 739 | 750 |
| 740 /** | 751 /** |
| 741 * Default constructor. Unknown method calls are allowed and logged, | 752 * Default constructor. Unknown method calls are allowed and logged, |
| 742 * the mock has no name, and has its own log. | 753 * the mock has no name, and has its own log. |
| 743 */ | 754 */ |
| 744 Mock() : throwIfNoBehavior = false, name = null { | 755 Mock() : _throwIfNoBehavior = false, log = null, name = null { |
| 745 log = new LogEntryList(); | 756 logging = true; |
| 746 behaviors = new Map<String,Behavior>(); | 757 _behaviors = new Map<String,Behavior>(); |
| 747 } | 758 } |
| 748 | 759 |
| 749 /** | 760 /** |
| 750 * This constructor makes a mock that has a [name] and possibly uses | 761 * This constructor makes a mock that has a [name] and possibly uses |
| 751 * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods | 762 * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods |
| 752 * that have no defined behaviors will throw an exception; otherwise they | 763 * that have no defined behaviors will throw an exception; otherwise they |
| 753 * will be allowed and logged (but will not do anything). | 764 * will be allowed and logged (but will not do anything). |
| 765 * If [enableLogging] is false, no logging will be done initially (whether |
| 766 * or not a [log] is supplied), but [logging] can be set to true later. |
| 754 */ | 767 */ |
| 755 Mock.custom([this.name, | 768 Mock.custom([this.name, |
| 756 this.log, | 769 this.log, |
| 757 this.throwIfNoBehavior = false]) { | 770 throwIfNoBehavior = false, |
| 758 if (log == null) { | 771 enableLogging = true]) : _throwIfNoBehavior = throwIfNoBehavior { |
| 759 log = new LogEntryList(); | 772 logging = enableLogging; |
| 760 } | 773 _behaviors = new Map<String,Behavior>(); |
| 761 behaviors = new Map<String,Behavior>(); | |
| 762 } | 774 } |
| 763 | 775 |
| 764 /** | 776 /** |
| 765 * [when] is used to create a new or extend an existing [Behavior]. | 777 * [when] is used to create a new or extend an existing [Behavior]. |
| 766 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for | 778 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for |
| 767 * that signature are returned (being created first if needed). | 779 * that signature are returned (being created first if needed). |
| 768 * | 780 * |
| 769 * Typical use case: | 781 * Typical use case: |
| 770 * | 782 * |
| 771 * mock.when(callsTo(...)).alwaysReturn(...); | 783 * mock.when(callsTo(...)).alwaysReturn(...); |
| 772 */ | 784 */ |
| 773 Behavior when(CallMatcher logFilter) { | 785 Behavior when(CallMatcher logFilter) { |
| 774 String key = logFilter.toString(); | 786 String key = logFilter.toString(); |
| 775 if (!behaviors.containsKey(key)) { | 787 if (!_behaviors.containsKey(key)) { |
| 776 Behavior b = new Behavior(logFilter); | 788 Behavior b = new Behavior(logFilter); |
| 777 behaviors[key] = b; | 789 _behaviors[key] = b; |
| 778 return b; | 790 return b; |
| 779 } else { | 791 } else { |
| 780 return behaviors[key]; | 792 return _behaviors[key]; |
| 781 } | 793 } |
| 782 } | 794 } |
| 783 | 795 |
| 784 /** | 796 /** |
| 785 * This is the handler for method calls. We loo through the list | 797 * This is the handler for method calls. We loo through the list |
| 786 * of [Behavior]s, and find the first match that still has return | 798 * of [Behavior]s, and find the first match that still has return |
| 787 * values available, and then do the action specified by that | 799 * values available, and then do the action specified by that |
| 788 * return value. If we find no [Behavior] to apply an exception is | 800 * return value. If we find no [Behavior] to apply an exception is |
| 789 * thrown. | 801 * thrown. |
| 790 */ | 802 */ |
| 791 noSuchMethod(String method, List args) { | 803 noSuchMethod(String method, List args) { |
| 792 if (method.startsWith('get:')) { | 804 if (method.startsWith('get:')) { |
| 793 method = 'get ${method.substring(4)}'; | 805 method = 'get ${method.substring(4)}'; |
| 794 } | 806 } |
| 795 bool matchedMethodName = false; | 807 bool matchedMethodName = false; |
| 796 for (String k in behaviors.getKeys()) { | 808 for (String k in _behaviors.getKeys()) { |
| 797 Behavior b = behaviors[k]; | 809 Behavior b = _behaviors[k]; |
| 798 if (b.matcher.nameFilter.matches(method)) { | 810 if (b.matcher.nameFilter.matches(method)) { |
| 799 matchedMethodName = true; | 811 matchedMethodName = true; |
| 800 } | 812 } |
| 801 if (b.matches(method, args)) { | 813 if (b.matches(method, args)) { |
| 802 List actions = b.actions; | 814 List actions = b.actions; |
| 803 if (actions == null || actions.length == 0) { | 815 if (actions == null || actions.length == 0) { |
| 804 continue; // No return values left in this Behavior. | 816 continue; // No return values left in this Behavior. |
| 805 } | 817 } |
| 806 // Get the first response. | 818 // Get the first response. |
| 807 Responder response = actions[0]; | 819 Responder response = actions[0]; |
| 808 // If it is exhausted, remove it from the list. | 820 // If it is exhausted, remove it from the list. |
| 809 // Note that for endlessly repeating values, we started the count at | 821 // Note that for endlessly repeating values, we started the count at |
| 810 // 0, so we get a potentially useful value here, which is the | 822 // 0, so we get a potentially useful value here, which is the |
| 811 // (negation of) the number of times we returned the value. | 823 // (negation of) the number of times we returned the value. |
| 812 if (--response.count == 0) { | 824 if (--response.count == 0) { |
| 813 actions.removeRange(0, 1); | 825 actions.removeRange(0, 1); |
| 814 } | 826 } |
| 815 // Do the response. | 827 // Do the response. |
| 816 _Action action = response.action; | 828 _Action action = response.action; |
| 817 var value = response.value; | 829 var value = response.value; |
| 818 if (action == _Action.RETURN) { | 830 if (action == _Action.RETURN) { |
| 819 log.add(new LogEntry(name, method, args, action, value)); | 831 if (_logging) { |
| 832 log.add(new LogEntry(name, method, args, action, value)); |
| 833 } |
| 820 return value; | 834 return value; |
| 821 } else if (action == _Action.THROW) { | 835 } else if (action == _Action.THROW) { |
| 822 log.add(new LogEntry(name, method, args, action, value)); | 836 if (_logging) { |
| 837 log.add(new LogEntry(name, method, args, action, value)); |
| 838 } |
| 823 throw value; | 839 throw value; |
| 824 } else if (action == _Action.PROXY) { | 840 } else if (action == _Action.PROXY) { |
| 825 var rtn; | 841 var rtn; |
| 826 switch (args.length) { | 842 switch (args.length) { |
| 827 case 0: | 843 case 0: |
| 828 rtn = value(); | 844 rtn = value(); |
| 829 break; | 845 break; |
| 830 case 1: | 846 case 1: |
| 831 rtn = value(args[0]); | 847 rtn = value(args[0]); |
| 832 break; | 848 break; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 857 case 9: | 873 case 9: |
| 858 rtn = value(args[0], args[1], args[2], args[3], | 874 rtn = value(args[0], args[1], args[2], args[3], |
| 859 args[4], args[5], args[6], args[7], args[8]); | 875 args[4], args[5], args[6], args[7], args[8]); |
| 860 break; | 876 break; |
| 861 case 9: | 877 case 9: |
| 862 rtn = value(args[0], args[1], args[2], args[3], | 878 rtn = value(args[0], args[1], args[2], args[3], |
| 863 args[4], args[5], args[6], args[7], args[8], args[9]); | 879 args[4], args[5], args[6], args[7], args[8], args[9]); |
| 864 break; | 880 break; |
| 865 default: | 881 default: |
| 866 throw new Exception( | 882 throw new Exception( |
| 867 "Cannot proxy calls with more than 10 parameters"); | 883 "Cannot proxy calls with more than 10 parameters."); |
| 868 } | 884 } |
| 869 log.add(new LogEntry(name, method, args, action, rtn)); | 885 if (_logging) { |
| 886 log.add(new LogEntry(name, method, args, action, rtn)); |
| 887 } |
| 870 return rtn; | 888 return rtn; |
| 871 } | 889 } |
| 872 } | 890 } |
| 873 } | 891 } |
| 874 if (matchedMethodName) { | 892 if (matchedMethodName) { |
| 875 // User did specify behavior for this method, but all the | 893 // User did specify behavior for this method, but all the |
| 876 // actions are exhausted. This is considered an error. | 894 // actions are exhausted. This is considered an error. |
| 877 throw new Exception('No more actions for method ' | 895 throw new Exception('No more actions for method ' |
| 878 '${_qualifiedName(name, method)}'); | 896 '${_qualifiedName(name, method)}.'); |
| 879 } else if (throwIfNoBehavior) { | 897 } else if (_throwIfNoBehavior) { |
| 880 throw new Exception('No behavior specified for method ' | 898 throw new Exception('No behavior specified for method ' |
| 881 '${_qualifiedName(name, method)}'); | 899 '${_qualifiedName(name, method)}.'); |
| 882 } | 900 } |
| 883 // User hasn't specified behavior for this method; we don't throw | 901 // Otherwise user hasn't specified behavior for this method; we don't throw |
| 884 // so we can underspecify. | 902 // so we can underspecify. |
| 885 log.add(new LogEntry(name, method, args, _Action.IGNORE)); | 903 if (_logging) { |
| 904 log.add(new LogEntry(name, method, args, _Action.IGNORE)); |
| 905 } |
| 886 } | 906 } |
| 887 | 907 |
| 888 /** [verifyZeroInteractions] returns true if no calls were made */ | 908 /** [verifyZeroInteractions] returns true if no calls were made */ |
| 889 bool verifyZeroInteractions() => log.logs.length == 0; | 909 bool verifyZeroInteractions() { |
| 910 if (log == null) { |
| 911 // This means we created the mock with logging off and have never turned |
| 912 // it on, so it doesn't make sense to verify behavior on such a mock. |
| 913 throw new |
| 914 Exception("Can't verify behavior when logging was never enabled."); |
| 915 } |
| 916 return log.logs.length == 0; |
| 917 } |
| 890 | 918 |
| 891 /** | 919 /** |
| 892 * [getLogs] extracts all calls from the call log that match the | 920 * [getLogs] extracts all calls from the call log that match the |
| 893 * [logFilter] [CallMatcher], and returns the matching list of | 921 * [logFilter] [CallMatcher], and returns the matching list of |
| 894 * [LogEntry]s. If [destructive] is false (the default) the matching | 922 * [LogEntry]s. If [destructive] is false (the default) the matching |
| 895 * calls are left in the log, else they are removed. Removal allows | 923 * calls are left in the log, else they are removed. Removal allows |
| 896 * us to verify a set of interactions and then verify that there are | 924 * us to verify a set of interactions and then verify that there are |
| 897 * no other interactions left. [actionMatcher] can be used to further | 925 * no other interactions left. [actionMatcher] can be used to further |
| 898 * restrict the returned logs based on the action the mock performed. | 926 * restrict the returned logs based on the action the mock performed. |
| 899 * | 927 * |
| 900 * Typical usage: | 928 * Typical usage: |
| 901 * | 929 * |
| 902 * getLogs(callsTo(...)).verify(...); | 930 * getLogs(callsTo(...)).verify(...); |
| 903 */ | 931 */ |
| 904 LogEntryList getLogs([CallMatcher logFilter, | 932 LogEntryList getLogs([CallMatcher logFilter, |
| 905 Matcher actionMatcher, | 933 Matcher actionMatcher, |
| 906 bool destructive = false]) { | 934 bool destructive = false]) { |
| 907 return log.getMatches(name, logFilter, actionMatcher, destructive); | 935 if (log == null) { |
| 936 // This means we created the mock with logging off and have never turned |
| 937 // it on, so it doesn't make sense to get logs from such a mock. |
| 938 throw new |
| 939 Exception("Can't retrieve logs when logging was never enabled."); |
| 940 } else { |
| 941 return log.getMatches(name, logFilter, actionMatcher, destructive); |
| 942 } |
| 908 } | 943 } |
| 909 } | 944 } |
| OLD | NEW |