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