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

Side by Side Diff: lib/unittest/mock.dart

Issue 10694146: Added ability to disable logging in mocks, to avoid the memory overhead if you don't need behavior … (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 5 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « lib/unittest/expect.dart ('k') | lib/unittest/unittest.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « lib/unittest/expect.dart ('k') | lib/unittest/unittest.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698