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 }*/