1 module dmocks.mocks;
2
3 public import dmocks.dynamic;
4 import dmocks.factory;
5 public import dmocks.object_mock;
6 import dmocks.repository;
7 import dmocks.util;
8
9 /++
10 A class through which one creates mock objects and manages expectations about calls to their methods.
11 ++/
12 public class Mocker
13 {
14 private MockRepository _repository;
15
16 public
17 {
18 this ()
19 {
20 _repository = new MockRepository();
21 }
22
23 /**
24 * Start setting up expectations. Method calls on mock object will create (and record) new expectations.
25 * You can just call methods directly or use Mocker.expect/lastCall to customize expectations.
26 */
27 void record ()
28 {
29 _repository.BackToRecord();
30 }
31
32 /**
33 * Stop setting up expectations. Any method calls after this point will
34 * be matched against the expectations set up before calling replay and
35 * expectations' actions will be executed.
36 */
37 void replay ()
38 {
39 _repository.Replay();
40 }
41
42 /**
43 * Verifies that certain expectation requirements were satisfied during replay phase.
44 *
45 * checkUnmatchedExpectations - Check to see if there are any expectations that haven't been
46 * matched to a call.
47 *
48 * checkUnexpectedCalls - Check to see if there are any calls that there were no
49 * expectation set up for.
50 *
51 * Throws an ExpectationViolationException if those issues occur.
52 */
53 void verify (bool checkUnmatchedExpectations = true, bool checkUnexpectedCalls = true)
54 {
55 _repository.Verify(checkUnmatchedExpectations, checkUnexpectedCalls);
56 }
57
58 /**
59 * By default, all expectations are unordered. If I want to require that
60 * one call happen immediately after another, I call Mocker.ordered, make
61 * those expectations, and call Mocker.unordered to avoid requiring a
62 * particular order afterward.
63 */
64 void ordered ()
65 {
66 _repository.Ordered(true);
67 }
68
69 void unordered ()
70 {
71 _repository.Ordered(false);
72 }
73
74 /**
75 * Disables exceptions thrown on unexpected calls while in Replay phase
76 * Unexpected methods called will return default value of their type
77 *
78 * Useful when using mocks as stubs or when you don't want exceptions
79 * to change flow of execution of your tests, for example when using nothrow functions
80 *
81 * Default: false
82 */
83 void allowUnexpectedCalls(bool allow)
84 {
85 _repository.AllowUnexpected(allow);
86 }
87
88 /**
89 * Creates a mock object for a given type.
90 *
91 * Calls matching expectations with passThrough enabled
92 * will call equivalent methods of T object constructed with args.
93 *
94 * Type returned is binarily compatibile with given type
95 * All virtual calls made to the object will be mocked
96 * Final and template calls will not be mocked
97 *
98 * Use this type of mock to substitute interface/class objects
99 */
100 T mock (T, CONSTRUCTOR_ARGS...) (CONSTRUCTOR_ARGS args)
101 {
102 static assert (is(T == class) || is(T == interface),
103 "only classes and interfaces can be mocked using this type of mock");
104 return dmocks.factory.mock!(T)(_repository, args);
105 }
106
107 /**
108 * Creates a mock object for a given type.
109 *
110 * Calls matching expectations with passThrough enabled
111 * will call equivalent methods of T object constructed with args.
112 *
113 * Type of the mock is incompatibile with given type
114 * Final, template and virtual methods will be mocked
115 *
116 * Use this type of mock to substitute template parameters
117 */
118 MockedFinal!T mockFinal(T, CONSTRUCTOR_ARGS...) (CONSTRUCTOR_ARGS args)
119 {
120 static assert (is(T == class) || is(T == interface),
121 "only classes and interfaces can be mocked using this type of mock");
122 return dmocks.factory.mockFinal!(T)(_repository, args);
123 }
124
125 /**
126 * Creates a mock object for a given type.
127 *
128 * Calls matching expectations with passThrough enabled
129 * will call equivalent methods of "to" object.
130 *
131 * Type of the mock is incompatibile with given type
132 * Final, template and virtual methods will be mocked
133 *
134 * Use this type of mock to substitute template parameters
135 */
136 MockedFinal!T mockFinalPassTo(T) (T to)
137 {
138 static assert (is(T == class) || is(T == interface),
139 "only classes and interfaces can be mocked using this type of mock");
140 return dmocks.factory.mockFinalPassTo!(T)(_repository, to);
141 }
142
143 /**
144 * Creates a mock object for a given type.
145 *
146 * Calls matching expectations with passThrough enabled
147 * will call equivalent methods of T object constructed with args.
148 *
149 * Type of the mock is incompatibile with given type
150 * Final, template and virtual methods will be mocked
151 *
152 * Use this type of mock to substitute template parameters
153 */
154 MockedStruct!T mockStruct(T, CONSTRUCTOR_ARGS...) (CONSTRUCTOR_ARGS args)
155 {
156 static assert (is(T == struct),
157 "only structs can be mocked using this type of mock");
158 return dmocks.factory.mockStruct!(T)(_repository, args);
159 }
160
161 /**
162 * Creates a mock object for a given type.
163 *
164 * Calls matching expectations with passThrough enabled
165 * will call equivalent methods of "to" object.
166 *
167 * Type of the mock is incompatibile with given type
168 * Final, template and virtual methods will be mocked
169 *
170 * Use this type of mock to substitute template parameters
171 */
172 MockedStruct!T mockStructPassTo(T) (T to)
173 {
174 static assert (is(T == struct),
175 "only structs can be mocked using this type of mock");
176 return dmocks.factory.mockStructPassTo!(T)(_repository, to);
177 }
178
179 /**
180 * Record new expectation that will exactly match method called in methodCall argument
181 *
182 * Returns an object that allows you to set various properties of the expectation,
183 * such as return value, number of repetitions or matching options.
184 *
185 * Examples:
186 * ---
187 * Mocker m = new Mocker;
188 * Object o = m.Mock!(Object);
189 * m.expect(o.toString).returns("hello?");
190 * ---
191 */
192 ExpectationSetup expect (T) (lazy T methodCall) {
193 auto pre = _repository.LastRecordedCallExpectation();
194 methodCall();
195 auto post = _repository.LastRecordedCallExpectation();
196 if (pre is post)
197 throw new InvalidOperationException("mocks.Mocker.expect: you did not call a method mocked by the mocker!");
198 return lastCall();
199 }
200
201 /**
202 * Returns ExpectationSetup object for most recent call on a method of a mock object.
203 *
204 * This object allows you to set various properties of the expectation,
205 * such as return value, number of repetitions or matching options.
206 *
207 * Examples:
208 * ---
209 * Mocker m = new Mocker;
210 * Object o = m.Mock!(Object);
211 * o.toString;
212 * m.LastCall().returns("hello?");
213 * ---
214 */
215 ExpectationSetup lastCall () {
216 return new ExpectationSetup(_repository.LastRecordedCallExpectation(), _repository.LastRecordedCall());
217 }
218
219 /**
220 * Set up a result for a method, but without any backend accounting for it.
221 * Things where you want to allow this method to be called, but you aren't
222 * currently testing for it.
223 */
224 ExpectationSetup allowing (T) (T ignored) {
225 return lastCall().repeatAny;
226 }
227
228 /** Ditto */
229 ExpectationSetup allowing (T = void) () {
230 return lastCall().repeatAny();
231 }
232
233 /**
234 * Do not require explicit return values for expectations. If no return
235 * value is set, return the default value (null / 0 / nan, in most
236 * cases). By default, if no return value, exception, delegate, or
237 * passthrough option is set, an exception will be thrown.
238 */
239 void allowDefaults () {
240 _repository.AllowDefaults(true);
241 }
242 }
243 }
244
245 /++
246 An ExpectationSetup object allows you to set various properties of the expectation,
247 such as:
248 - what action should be taken when method matching expectation is called
249 - return value, action to call, exception to throw, etc
250
251 Examples:
252 ---
253 Mocker m = new Mocker;
254 Object o = m.Mock!(Object);
255 o.toString;
256 m.LastCall().returns("Are you still there?").repeat(1, 12);
257 ---
258 ++/
259 public class ExpectationSetup
260 {
261 import dmocks.arguments;
262 import dmocks.expectation;
263 import dmocks.dynamic;
264 import dmocks.qualifiers;
265 import dmocks.call;
266
267 private CallExpectation _expectation;
268
269 private Call _setUpCall;
270
271 this (CallExpectation expectation, Call setUpCall)
272 {
273 assert (expectation !is null, "can't create an ExpectationSetup if expectation is null");
274 assert (setUpCall !is null, "can't create an ExpectationSetup if setUpCall is null");
275 _expectation = expectation;
276 _setUpCall = setUpCall;
277 }
278
279 /**
280 * Ignore method argument values in matching calls to this expectation.
281 */
282 ExpectationSetup ignoreArgs ()
283 {
284 _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, (Dynamic a, Dynamic b)=>true);
285 return this;
286 }
287
288 /**
289 * Allow providing custom argument comparator for matching calls to this expectation.
290 */
291 ExpectationSetup customArgsComparator (bool delegate(Dynamic expected, Dynamic provided) del)
292 {
293 _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, del);
294 return this;
295 }
296
297 /**
298 * This expectation must match to at least min number of calls and at most to max number of calls.
299 */
300 ExpectationSetup repeat (int min, int max)
301 {
302 if (min > max)
303 {
304 throw new InvalidOperationException("The specified range is invalid.");
305 }
306 _expectation.repeatInterval = Interval(min, max);
307 return this;
308 }
309
310 /**
311 * This expectation will match exactly i times.
312 */
313 ExpectationSetup repeat (int i)
314 {
315 repeat(i,i);
316 return this;
317 }
318
319 /**
320 * This expectation will match to any number of calls.
321 */
322 ExpectationSetup repeatAny ()
323 {
324 return repeat(0, int.max);
325 }
326
327 /**
328 * When the method which matches this expectation is called execute the
329 * given delegate. The delegate's signature must match the signature
330 * of the called method. If it does not, an exception will be thrown.
331 * The called method will return whatever the given delegate returns.
332 * Examples:
333 * ---
334 * m.expect(myObj.myFunc(0, null, null, 'a')
335 * .ignoreArgs()
336 * .action((int i, char[] s, Object o, char c) { return -1; });
337 * ---
338 */
339 ExpectationSetup action (T, U...)(T delegate(U) action)
340 {
341 _expectation.action.action = dynamic(action);
342 return this;
343 }
344
345 // TODO: how can I get validation here that the type you're
346 // inserting is the type expected before trying to execute it?
347 // Not really an issue, since it'd be revealed in the space
348 // of a single test.
349 /**
350 * Set the value to return when method matching this expectation is called on a mock object.
351 * Params:
352 * value = the value to return
353 */
354 ExpectationSetup returns (T)(T value)
355 {
356 _expectation.action.returnValue = dynamic(value);
357 return this;
358 }
359
360 /**
361 * When the method which matches this expectation is called,
362 * throw the given exception. If there are any
363 * actions specified (via the action method), they will not be executed.
364 */
365 ExpectationSetup throws (Exception e)
366 {
367 _expectation.action.toThrow = e;
368 return this;
369 }
370
371 /**
372 * Instead of returning or throwing a given value, pass the call through to
373 * the mocked type object. For mock***PassTo(obj) obj has to be valid for this to work.
374 *
375 * This is useful for example for enabling use of mock object in hashmaps by enabling
376 * toHash and opEquals of your class.
377 */
378 ExpectationSetup passThrough ()
379 {
380 _expectation.action.passThrough = true;
381 return this;
382 }
383 }
384
385 /// backward compatibility alias
386 alias ExpectationSetup ExternalCall;
387
388 version (unittest)
389 {
390 import std.exception;
391 import std.stdio;
392
393 class Templated(T)
394 {
395 }
396 interface IM
397 {
398 void bar();
399 }
400
401 class ConstructorArg
402 {
403 this(int i)
404 {
405 a = i;
406 }
407 int a;
408 int getA()
409 {
410 return a;
411 }
412 }
413
414 class SimpleObject
415 {
416 this()
417 {
418 }
419 void print()
420 {
421 writeln(toString());
422 }
423 }
424
425 interface IRM
426 {
427 IM get();
428 void set(IM im);
429 }
430
431 class HasPrivateMethods
432 {
433 protected void method()
434 {
435 }
436 }
437
438 interface IFace
439 {
440 void foo(string s);
441 }
442
443 class Smthng : IFace
444 {
445 void foo(string s)
446 {
447 }
448 }
449
450 class HasMember
451 {
452 int member;
453 }
454
455 class Overloads
456 {
457 void foo()
458 {
459 }
460 void foo(int i)
461 {
462 }
463 }
464
465 class Qualifiers
466 {
467 int make() shared
468 {
469 return 0;
470 }
471
472 int make() const
473 {
474 return 1;
475 }
476
477 int make() shared const
478 {
479 return 2;
480 }
481
482 int make()
483 {
484 return 3;
485 }
486
487 int make() immutable
488 {
489 return 4;
490 }
491 }
492
493 interface VirtualFinal
494 {
495 int makeVir();
496 }
497
498 class MakeAbstract
499 {
500 int con;
501 this(int con)
502 {
503 this.con = con;
504 }
505 abstract int abs();
506
507 int concrete()
508 {
509 return con;
510 }
511 }
512
513 class FinalMethods : VirtualFinal
514 {
515 final int make()
516 {
517 return 0;
518 }
519 final int make(int i)
520 {
521 return 2;
522 }
523 int makeVir()
524 {
525 return 5;
526 }
527 }
528
529 final class FinalClass
530 {
531 int fortyTwo()
532 {
533 return 42;
534 }
535 }
536
537 class TemplateMethods
538 {
539 string get(T)(T t)
540 {
541 import std.traits;
542 return fullyQualifiedName!T;
543 }
544
545 int getSomethings(T...)(T t)
546 {
547 return T.length;
548 }
549 }
550
551 struct Struct
552 {
553 int get()
554 {
555 return 1;
556 }
557 }
558
559 struct StructWithFields
560 {
561 int field;
562 int get()
563 {
564 return field;
565 }
566 }
567
568
569 struct StructWithConstructor
570 {
571 int field;
572 this(int i)
573 {
574 field = i;
575 }
576 int get()
577 {
578 return field;
579 }
580 }
581
582 class Dependency
583 {
584 private int[] arr = [1, 2];
585 private int index = 0;
586 public int foo()
587 {
588 return arr[index++];
589 }
590 }
591
592 class TakesFloat
593 {
594 public void foo(float a)
595 {
596 }
597 }
598
599 class Property
600 {
601 private int _foo;
602 @property int foo()
603 {
604 return _foo;
605 }
606
607 @property void foo(int i)
608 {
609 _foo = i;
610 }
611
612 @property T foot(T)()
613 {
614 static if (is(T == int))
615 {
616 return _foo;
617 }
618 else
619 {
620 return T.init;
621 }
622 }
623
624 @property void foot(T)(T i)
625 {
626 static if (is(T == int))
627 {
628 _foo = i;
629 }
630 }
631 }
632
633 }
634
635 @("nontemplated mock")
636 unittest
637 {
638 (new Mocker()).mock!(Object);
639 }
640
641 @("templated mock")
642 unittest
643 {
644 (new Mocker()).mock!(Templated!(int));
645 }
646
647 @("templated mock")
648 unittest
649 {
650 (new Mocker()).mock!(IM);
651 }
652
653 @("execute mock method")
654 unittest
655 {
656 auto r = new Mocker();
657 auto o = r.mock!(Object);
658 o.toString();
659 }
660
661 @("constructor argument")
662 unittest
663 {
664 auto r = new Mocker();
665 auto o = r.mock!(ConstructorArg)(4);
666 }
667
668 @("lastCall")
669 unittest
670 {
671 Mocker m = new Mocker();
672 SimpleObject o = m.mock!(SimpleObject);
673 o.print;
674 auto e = m.lastCall;
675
676 assert (e !is null);
677 }
678
679 @("return a value")
680 unittest
681 {
682 Mocker m = new Mocker();
683 Object o = m.mock!(Object);
684 o.toString;
685 auto e = m.lastCall;
686
687 assert (e !is null);
688 e.returns("frobnitz");
689 }
690
691 @("unexpected call")
692 unittest
693 {
694 Mocker m = new Mocker();
695 Object o = m.mock!(Object);
696 m.replay();
697 assertThrown!ExpectationViolationException(o.toString);
698 }
699
700 @("expect")
701 unittest
702 {
703 Mocker m = new Mocker();
704 Object o = m.mock!(Object);
705 m.expect(o.toString).repeat(0).returns("mrow?");
706 m.replay();
707 assertThrown(o.toString);
708 }
709
710 @("repeat single")
711 unittest
712 {
713 Mocker m = new Mocker();
714 Object o = m.mock!(Object);
715 m.expect(o.toString).repeat(2).returns("foom?");
716
717 m.replay();
718
719 o.toString;
720 o.toString;
721 assertThrown!ExpectationViolationException(o.toString);
722 }
723
724 @("repository match counts")
725 unittest
726 {
727 auto r = new Mocker();
728 auto o = r.mock!(Object);
729 o.toString;
730 r.lastCall().repeat(2, 2).returns("mew.");
731 r.replay();
732 assertThrown!ExpectationViolationException(r.verify());
733 }
734
735 @("delegate payload")
736 unittest
737 {
738 bool calledPayload = false;
739 Mocker r = new Mocker();
740 auto o = r.mock!(SimpleObject);
741
742 //o.print;
743 r.expect(o.print).action({ calledPayload = true; });
744 r.replay();
745
746 o.print;
747 assert (calledPayload);
748 }
749
750 @("exception payload")
751 unittest
752 {
753 Mocker r = new Mocker();
754 auto o = r.mock!(SimpleObject);
755
756 string msg = "divide by cucumber error";
757 o.print;
758 r.lastCall().throws(new Exception(msg));
759 r.replay();
760
761 try {
762 o.print;
763 assert (false, "expected exception not thrown");
764 } catch (Exception e) {
765 // Careful -- assertion errors derive from Exception
766 assert (e.msg == msg, e.msg);
767 }
768 }
769
770 @("passthrough")
771 unittest
772 {
773 Mocker r = new Mocker();
774 auto o = r.mock!(Object);
775 o.toString;
776 r.lastCall().passThrough();
777
778 r.replay();
779 string str = o.toString;
780 assert (str == "dmocks.object_mock.Mocked!(Object).Mocked", str);
781 }
782
783 @("class with constructor init check")
784 unittest
785 {
786 auto r = new Mocker();
787 auto o = r.mock!(ConstructorArg)(4);
788 o.getA();
789 r.lastCall().passThrough();
790 r.replay();
791 assert (4 == o.getA());
792 }
793
794 @("associative arrays")
795 unittest
796 {
797 Mocker r = new Mocker();
798 auto o = r.mock!(Object);
799 r.expect(o.toHash()).passThrough().repeatAny;
800 r.expect(o.opEquals(null)).ignoreArgs().passThrough().repeatAny;
801
802 r.replay();
803 int[Object] i;
804 i[o] = 5;
805 int j = i[o];
806 }
807
808 @("ordering in order")
809 unittest
810 {
811 Mocker r = new Mocker();
812 auto o = r.mock!(Object);
813 r.ordered;
814 r.expect(o.toHash).returns(cast(hash_t)5);
815 r.expect(o.toString).returns("mow!");
816
817 r.replay();
818 o.toHash;
819 o.toString;
820 r.verify;
821 }
822
823 @("ordering not in order")
824 unittest
825 {
826 Mocker r = new Mocker();
827 auto o = r.mock!(Object);
828 r.ordered;
829 r.expect(o.toHash).returns(5);
830 r.expect(o.toString).returns("mow!");
831
832 r.replay();
833 try {
834 o.toString;
835 o.toHash;
836 assert (false);
837 } catch (ExpectationViolationException) {}
838 }
839
840 @("ordering interposed")
841 unittest
842 {
843 Mocker r = new Mocker();
844 auto o = r.mock!(SimpleObject);
845 r.ordered;
846 r.expect(o.toHash).returns(cast(hash_t)5);
847 r.expect(o.toString).returns("mow!");
848 r.unordered;
849 o.print;
850
851 r.replay();
852 o.toHash;
853 o.print;
854 o.toString;
855 }
856
857 @("allow unexpected")
858 unittest
859 {
860 Mocker r = new Mocker();
861 auto o = r.mock!(Object);
862 r.ordered;
863 r.allowUnexpectedCalls(true);
864 r.expect(o.toString).returns("mow!");
865 r.replay();
866 o.toHash; // unexpected tohash calls
867 o.toString;
868 o.toHash;
869 assertThrown!ExpectationViolationException(r.verify(false, true));
870 r.verify(true, false);
871 }
872
873 @("allowing")
874 unittest
875 {
876 Mocker r = new Mocker();
877 auto o = r.mock!(Object);
878 r.allowing(o.toString).returns("foom?");
879
880 r.replay();
881 o.toString;
882 o.toString;
883 o.toString;
884 r.verify;
885 }
886
887 @("nothing for method to do")
888 unittest
889 {
890 try {
891 Mocker r = new Mocker();
892 auto o = r.mock!(Object);
893 r.allowing(o.toString);
894
895 r.replay();
896 assert (false, "expected a mocks setup exception");
897 } catch (MocksSetupException e) {
898 }
899 }
900
901 @("allow defaults test")
902 unittest
903 {
904 Mocker r = new Mocker();
905 auto o = r.mock!(Object);
906 r.allowDefaults;
907 r.allowing(o.toString);
908
909 r.replay();
910 assert (o.toString == (char[]).init);
911 }
912
913 // Going through the guts of Smthng
914 // unittest
915 // {
916 // auto foo = new Smthng();
917 // auto guts = *(cast(int**)&foo);
918 // auto len = __traits(classInstanceSize, Smthng) / size_t.sizeof;
919 // auto end = guts + len;
920 // for (; guts < end; guts++) {
921 // writefln("\t%x", *guts);
922 // }
923 // }
924
925 @("mock interface")
926 unittest
927 {
928 auto r = new Mocker;
929 IFace o = r.mock!(IFace);
930 debugLog("about to call once...");
931 o.foo("hallo");
932 r.replay;
933 debugLog("about to call twice...");
934 o.foo("hallo");
935 r.verify;
936 }
937
938 @("cast mock to interface")
939 unittest
940 {
941 auto r = new Mocker;
942 IFace o = r.mock!(Smthng);
943 debugLog("about to call once...");
944 o.foo("hallo");
945 r.replay;
946 debugLog("about to call twice...");
947 o.foo("hallo");
948 r.verify;
949 }
950
951 @("cast mock to interface")
952 unittest
953 {
954 auto r = new Mocker;
955 IFace o = r.mock!(Smthng);
956 debugLog("about to call once...");
957 o.foo("hallo");
958 r.replay;
959 debugLog("about to call twice...");
960 o.foo("hallo");
961 r.verify;
962 }
963
964 @("return user-defined type")
965 unittest
966 {
967 auto r = new Mocker;
968 auto o = r.mock!(IRM);
969 auto im = r.mock!(IM);
970 debugLog("about to call once...");
971 r.expect(o.get).returns(im);
972 o.set(im);
973 r.replay;
974 debugLog("about to call twice...");
975 assert (o.get is im, "returned the wrong value");
976 o.set(im);
977 r.verify;
978 }
979
980 @("return user-defined type")
981 unittest
982 {
983 auto r = new Mocker;
984 auto o = r.mock!(HasMember);
985 }
986
987 @("overloaded method")
988 unittest
989 {
990 auto r = new Mocker;
991 auto o = r.mock!(Overloads);
992 o.foo();
993 o.foo(1);
994 r.replay;
995 o.foo(1);
996 o.foo;
997 r.verify;
998 }
999
1000 @("overloaded method qualifiers")
1001 unittest
1002 {
1003 {
1004 auto r = new Mocker;
1005 auto s = r.mock!(shared(Qualifiers));
1006 auto sc = cast(shared const) s;
1007
1008 r.expect(s.make).passThrough;
1009 r.expect(sc.make).passThrough;
1010 r.replay;
1011
1012 assert(s.make == 0);
1013 assert(sc.make == 2);
1014
1015 r.verify;
1016 }
1017 {
1018 auto r = new Mocker;
1019 auto m = r.mock!(Qualifiers);
1020 auto c = cast(const) m;
1021 auto i = cast(immutable) m;
1022
1023 r.expect(i.make).passThrough;
1024 r.expect(m.make).passThrough;
1025 r.expect(c.make).passThrough;
1026 r.replay;
1027
1028 assert(i.make == 4);
1029 assert(m.make == 3);
1030 assert(c.make == 1);
1031
1032 r.verify;
1033 }
1034 {
1035 auto r = new Mocker;
1036 auto m = r.mock!(Qualifiers);
1037 auto c = cast(const) m;
1038 auto i = cast(immutable) m;
1039
1040 r.expect(i.make).passThrough;
1041 r.expect(m.make).passThrough;
1042 r.expect(m.make).passThrough;
1043 r.replay;
1044
1045 assert(i.make == 4);
1046 assert(m.make == 3);
1047 try
1048 {
1049 assert(c.make == 1);
1050 assert(false, "exception not thrown");
1051 }
1052 catch (ExpectationViolationException e)
1053 {
1054 }
1055 }
1056 }
1057
1058 @("final mock of virtual methods")
1059 unittest
1060 {
1061 import std.exception;
1062
1063 auto r = new Mocker;
1064 auto o = r.mockFinal!(VirtualFinal);
1065 r.expect(o.makeVir()).returns(5);
1066 r.replay;
1067 assert(o.makeVir == 5);
1068 }
1069
1070 @("final mock of abstract methods")
1071 unittest
1072 {
1073 auto r = new Mocker;
1074 auto o = r.mockFinal!(MakeAbstract)(6);
1075 r.expect(o.concrete()).passThrough;
1076 r.replay;
1077 assert(o.concrete == 6);
1078 r.verify;
1079 }
1080
1081 @("final methods")
1082 unittest
1083 {
1084 auto r = new Mocker;
1085 auto o = r.mockFinal!(FinalMethods);
1086 r.expect(o.make()).passThrough;
1087 r.expect(o.make(1)).passThrough;
1088 r.replay;
1089 static assert(!is(typeof(o)==FinalMethods));
1090 assert(o.make == 0);
1091 assert(o.make(1) == 2);
1092 r.verify;
1093 }
1094
1095 @("final class")
1096 unittest
1097 {
1098 auto r = new Mocker;
1099 auto o = r.mockFinal!(FinalClass);
1100 r.expect(o.fortyTwo()).passThrough;
1101 r.replay;
1102 assert(o.fortyTwo == 42);
1103 r.verify;
1104 }
1105
1106 @("final class with no underlying object")
1107 unittest
1108 {
1109 auto r = new Mocker;
1110 auto o = r.mockFinalPassTo!(FinalClass)(null);
1111 r.expect(o.fortyTwo()).returns(43);
1112 r.replay;
1113 assert(o.fortyTwo == 43);
1114 r.verify;
1115 }
1116
1117 @("template methods")
1118 unittest
1119 {
1120 auto r = new Mocker;
1121 auto o = r.mockFinal!(TemplateMethods);
1122 r.expect(o.get(1)).passThrough;
1123 r.expect(o.getSomethings(1, 2, 3)).passThrough;
1124 r.replay;
1125 assert(o.get(1) == "int");
1126 auto tm = new TemplateMethods();
1127 assert(o.getSomethings(1, 2, 3) == 3);
1128 r.verify;
1129 }
1130
1131 @("struct")
1132 unittest
1133 {
1134 auto r = new Mocker;
1135 auto o = r.mockStruct!(Struct);
1136 r.expect(o.get).passThrough;
1137 r.replay;
1138 assert(o.get() == 1);
1139 r.verify;
1140 }
1141
1142 @("struct with fields")
1143 unittest
1144 {
1145 auto r = new Mocker;
1146 auto o = r.mockStruct!(StructWithFields)(5);
1147 r.expect(o.get).passThrough;
1148 r.replay;
1149 assert(o.get() == 5);
1150 r.verify;
1151 }
1152
1153 @("struct with fields")
1154 unittest
1155 {
1156 auto r = new Mocker;
1157 auto o = r.mockStruct!(StructWithConstructor)(5);
1158 r.expect(o.get).passThrough;
1159 r.replay;
1160 assert(o.get() == 5);
1161 r.verify;
1162 }
1163
1164 @("struct with no underlying object")
1165 unittest
1166 {
1167 auto r = new Mocker;
1168 auto o = r.mockStructPassTo(StructWithConstructor.init);
1169 r.expect(o.get).returns(6);
1170 r.replay;
1171 assert(o.get() == 6);
1172 r.verify;
1173 }
1174
1175 @("returning different values on the same expectation")
1176 unittest
1177 {
1178 auto mocker = new Mocker;
1179 auto dependency = mocker.mock!Dependency;
1180
1181 //mocker.ordered;
1182 mocker.expect(dependency.foo).returns(1);
1183 mocker.expect(dependency.foo).returns(2);
1184 mocker.replay;
1185 assert(dependency.foo == 1);
1186 assert(dependency.foo == 2);
1187 mocker.verify;
1188 }
1189
1190 @("customArgsComparator")
1191 unittest
1192 {
1193 import std.math;
1194
1195 auto mocker = new Mocker;
1196 auto dependency = mocker.mock!TakesFloat;
1197 mocker.expect(dependency.foo(1.0f)).customArgsComparator(
1198 (Dynamic a, Dynamic b)
1199 {
1200 if (a.type == typeid(float))
1201 { return ( abs(a.get!float() - b.get!float()) < 0.1f); }
1202 return true;
1203 }).repeat(2);
1204 mocker.replay;
1205
1206 // custom comparison example - treat similar floats as equals
1207 dependency.foo(1.01);
1208 dependency.foo(1.02);
1209 }
1210
1211 unittest
1212 {
1213 auto mocker = new Mocker;
1214 auto dependency = mocker.mockFinal!Property;
1215 mocker.ordered;
1216 mocker.expect(dependency.foo = 2).ignoreArgs.passThrough;
1217 mocker.expect(dependency.foo).passThrough;
1218 //TODO: these 2 don't work yet
1219 //mocker.expect(dependency.foot!int = 5).passThrough;
1220 //mocker.expect(dependency.foot!int).passThrough;
1221 mocker.replay;
1222
1223 dependency.foo = 7;
1224 assert(dependency.foo ==7);
1225 //dependency.foot!int = 3;
1226 //assert(dependency.foot!int == 3);
1227 mocker.verify;
1228 }
1229
1230 /*TODO - typesafe variadic methods do not work yet
1231 class Foo {
1232 int x;
1233 string s;
1234
1235 this(int x, string s) {
1236 this.x = x;
1237 this.s = s;
1238 }
1239 }
1240
1241 class Varargs
1242 {
1243 import core.vararg;
1244
1245 int varDyn(int first, ...)
1246 {
1247 return vvarDyn(first, _arguments, _argptr);
1248 }
1249
1250 // idiom from C - for every dynamic vararg function there has to be vfunction(Args, TypeInfo[] arguments, va_list argptr)
1251 // otherwise passThrough is impossible
1252 int vvarDyn(int first, TypeInfo[] arguments, va_list argptr)
1253 {
1254 assert(arguments[0] == typeid(int));
1255 int second = va_arg!int(argptr);
1256 return first + second;
1257 }
1258
1259 int varArray(int first, int[] next...)
1260 {
1261 return first + next[0];
1262 }
1263
1264 int varClass(int first, Foo f...)
1265 {
1266 return first + f.x;
1267 }
1268 }
1269
1270 unittest
1271 {
1272 import core.vararg;
1273
1274 auto mocker = new Mocker;
1275 auto dependency = mocker.mock!Varargs;
1276 mocker.record;
1277 // we only specify non-vararg arguments in setup because typeunsafe varargs can't have meaningful operations on them (like comparision, etc)
1278 mocker.expect(dependency.varDyn(42)).passThrough; // passThrough works with typeunsafe vararg functions only when v[funcname](Args, Typeinfo[], va_list) function variant is provided
1279 mocker.replay;
1280
1281 assert(dependency.varDyn(42, 5) == 47);
1282 }*/