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