View Javadoc
1   package mockit;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertNull;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   import static org.junit.jupiter.api.Assertions.fail;
8   
9   import jakarta.xml.bind.annotation.XmlElement;
10  
11  import java.lang.reflect.Constructor;
12  import java.util.Date;
13  import java.util.List;
14  
15  import mockit.internal.expectations.invocation.MissingInvocation;
16  import mockit.internal.expectations.invocation.UnexpectedInvocation;
17  
18  import org.junit.Ignore;
19  import org.junit.Rule;
20  import org.junit.Test;
21  import org.junit.rules.ExpectedException;
22  
23  /**
24   * The Class PartialMockingTest.
25   */
26  @SuppressWarnings("deprecation")
27  public final class PartialMockingTest {
28  
29      /** The thrown. */
30      @Rule
31      public final ExpectedException thrown = ExpectedException.none();
32  
33      /**
34       * The Class Collaborator.
35       */
36      @Deprecated
37      static class Collaborator {
38  
39          /** The value. */
40          @Deprecated
41          protected int value;
42  
43          /**
44           * Instantiates a new collaborator.
45           */
46          Collaborator() {
47              value = -1;
48          }
49  
50          /**
51           * Instantiates a new collaborator.
52           *
53           * @param value
54           *            the value
55           */
56          @Deprecated
57          Collaborator(@Deprecated int value) {
58              this.value = value;
59          }
60  
61          /**
62           * Gets the value.
63           *
64           * @return the value
65           */
66          final int getValue() {
67              return value;
68          }
69  
70          /**
71           * Sets the value.
72           *
73           * @param value
74           *            the new value
75           */
76          void setValue(int value) {
77              this.value = value;
78          }
79  
80          /**
81           * Simple operation.
82           *
83           * @param a
84           *            the a
85           * @param b
86           *            the b
87           * @param c
88           *            the c
89           *
90           * @return true, if successful
91           */
92          @SuppressWarnings("unused")
93          final boolean simpleOperation(int a, @XmlElement(name = "test") String b, Date c) {
94              return true;
95          }
96  
97          /**
98           * Do something.
99           *
100          * @param b
101          *            the b
102          * @param s
103          *            the s
104          */
105         static void doSomething(@SuppressWarnings("unused") boolean b, @SuppressWarnings("unused") String s) {
106             throw new IllegalStateException();
107         }
108 
109         /**
110          * Method which calls another in the same class.
111          *
112          * @return true, if successful
113          */
114         @Ignore("test")
115         boolean methodWhichCallsAnotherInTheSameClass() {
116             return simpleOperation(1, "internal", null);
117         }
118 
119         /**
120          * Overridable method.
121          *
122          * @return the string
123          */
124         String overridableMethod() {
125             return "base";
126         }
127     }
128 
129     /**
130      * Attempt to partially mock A class.
131      */
132     @Test
133     public void attemptToPartiallyMockAClass() {
134         thrown.expect(IllegalArgumentException.class);
135         thrown.expectMessage("Invalid Class");
136         thrown.expectMessage("partial mocking");
137         thrown.expectMessage("Collaborator");
138 
139         new Expectations(Collaborator.class) {
140         };
141     }
142 
143     /**
144      * Dynamic mock fully verified verify all recorded expectations but not all of the replayed ones.
145      */
146     @Test
147     public void dynamicMockFullyVerified_verifyAllRecordedExpectationsButNotAllOfTheReplayedOnes() {
148         final Collaborator collaborator = new Collaborator(0);
149 
150         new Expectations(collaborator) {
151             {
152                 collaborator.setValue(1);
153             }
154         };
155 
156         collaborator.setValue(1);
157         collaborator.setValue(2);
158 
159         // Verifies all the *mocked* invocations that would be left unverified; ignores those not mocked:
160         new FullVerifications() {
161             // No need to verify "setValue(1)" since it was recorded and implicitly verified.
162             // No need to verify "setValue(2)" since it was not recorded.
163         };
164     }
165 
166     /**
167      * Dynamic mock fully verified in order verify all recorded expectations but not all of the replayed ones.
168      */
169     @Test
170     public void dynamicMockFullyVerifiedInOrder_verifyAllRecordedExpectationsButNotAllOfTheReplayedOnes() {
171         final Collaborator collaborator = new Collaborator(0);
172 
173         new Expectations(collaborator) {
174             {
175                 collaborator.setValue(2);
176                 collaborator.setValue(3);
177             }
178         };
179 
180         collaborator.setValue(1);
181         collaborator.setValue(2);
182         collaborator.setValue(3);
183 
184         // Verifies all the *mocked* (recorded) invocations, ignoring those not mocked:
185         new VerificationsInOrder() {
186             {
187                 // No need to verify "setValue(1)" since it was not recorded.
188                 collaborator.setValue(2);
189                 collaborator.setValue(3);
190             }
191         };
192         new FullVerifications() {
193         };
194     }
195 
196     /**
197      * Expect two invocations on dynamic mock but replay once.
198      */
199     @Test
200     public void expectTwoInvocationsOnDynamicMockButReplayOnce() {
201         final Collaborator collaborator = new Collaborator();
202 
203         new Expectations(collaborator) {
204             {
205                 collaborator.getValue();
206                 times = 2;
207             }
208         };
209 
210         assertEquals(0, collaborator.getValue());
211         thrown.expect(MissingInvocation.class);
212     }
213 
214     /**
215      * Expect one invocation on dynamic mock but replay twice.
216      */
217     @Test
218     public void expectOneInvocationOnDynamicMockButReplayTwice() {
219         final Collaborator collaborator = new Collaborator(1);
220 
221         new Expectations(collaborator) {
222             {
223                 collaborator.getValue();
224                 times = 1;
225             }
226         };
227 
228         // Mocked:
229         assertEquals(0, collaborator.getValue());
230 
231         // Still mocked because it's not strict:
232         thrown.expect(UnexpectedInvocation.class);
233         assertEquals(0, collaborator.getValue());
234     }
235 
236     /**
237      * Dynamically mock an instance.
238      */
239     @Test
240     public void dynamicallyMockAnInstance() {
241         final Collaborator collaborator = new Collaborator(2);
242 
243         new Expectations(collaborator) {
244             {
245                 collaborator.simpleOperation(1, "", null);
246                 result = false;
247                 Collaborator.doSomething(anyBoolean, "test");
248             }
249         };
250 
251         // Mocked:
252         assertFalse(collaborator.simpleOperation(1, "", null));
253         Collaborator.doSomething(true, "test");
254 
255         // Not mocked:
256         assertEquals(2, collaborator.getValue());
257         assertEquals(45, new Collaborator(45).value);
258         assertEquals(-1, new Collaborator().value);
259 
260         try {
261             Collaborator.doSomething(false, null);
262             fail();
263         } catch (IllegalStateException ignore) {
264         }
265 
266         new Verifications() {
267             {
268                 collaborator.getValue();
269                 times = 1;
270             }
271         };
272     }
273 
274     /**
275      * Mock method in same class.
276      */
277     @Test
278     public void mockMethodInSameClass() {
279         final Collaborator collaborator = new Collaborator();
280 
281         new Expectations(collaborator) {
282             {
283                 collaborator.simpleOperation(1, anyString, null);
284                 result = false;
285             }
286         };
287 
288         assertFalse(collaborator.methodWhichCallsAnotherInTheSameClass());
289         assertTrue(collaborator.simpleOperation(2, "", null));
290         assertFalse(collaborator.simpleOperation(1, "", null));
291     }
292 
293     /**
294      * The Class SubCollaborator.
295      */
296     static final class SubCollaborator extends Collaborator {
297 
298         /**
299          * Instantiates a new sub collaborator.
300          */
301         @SuppressWarnings("unused")
302         SubCollaborator() {
303             this(1);
304         }
305 
306         /**
307          * Instantiates a new sub collaborator.
308          *
309          * @param value
310          *            the value
311          */
312         SubCollaborator(int value) {
313             super(value);
314         }
315 
316         @Override
317         String overridableMethod() {
318             return super.overridableMethod() + " overridden";
319         }
320 
321         /**
322          * Format.
323          *
324          * @return the string
325          */
326         String format() {
327             return String.valueOf(value);
328         }
329 
330         /**
331          * Cause failure.
332          */
333         static void causeFailure() {
334             throw new RuntimeException();
335         }
336     }
337 
338     /**
339      * Dynamically mock A sub collaborator instance.
340      */
341     @Test
342     public void dynamicallyMockASubCollaboratorInstance() {
343         final SubCollaborator collaborator = new SubCollaborator();
344 
345         new Expectations(collaborator) {
346             {
347                 collaborator.getValue();
348                 result = 5;
349                 collaborator.format();
350                 result = "test";
351                 SubCollaborator.causeFailure();
352             }
353         };
354 
355         // Mocked:
356         assertEquals(5, collaborator.getValue());
357         SubCollaborator.causeFailure();
358         assertEquals("test", collaborator.format());
359 
360         // Not mocked:
361         assertTrue(collaborator.simpleOperation(0, null, null)); // not recorded
362         assertEquals("1", new SubCollaborator().format()); // was recorded but on a different instance
363 
364         try {
365             Collaborator.doSomething(true, null); // not recorded
366             fail();
367         } catch (IllegalStateException ignore) {
368         }
369     }
370 
371     /**
372      * The Interface Dependency.
373      */
374     interface Dependency {
375 
376         /**
377          * Do something.
378          *
379          * @return true, if successful
380          */
381         boolean doSomething();
382 
383         /**
384          * Do something else.
385          *
386          * @param n
387          *            the n
388          *
389          * @return the list
390          */
391         List<?> doSomethingElse(int n);
392     }
393 
394     /**
395      * Dynamically mock an anonymous class instance through the implemented interface.
396      */
397     @Test
398     public void dynamicallyMockAnAnonymousClassInstanceThroughTheImplementedInterface() {
399         final Collaborator collaborator = new Collaborator();
400 
401         final Dependency dependency = new Dependency() {
402             @Override
403             public boolean doSomething() {
404                 return false;
405             }
406 
407             @Override
408             public List<?> doSomethingElse(int n) {
409                 return null;
410             }
411         };
412 
413         new Expectations(collaborator, dependency) {
414             {
415                 collaborator.getValue();
416                 result = 5;
417                 dependency.doSomething();
418                 result = true;
419             }
420         };
421 
422         // Mocked:
423         assertEquals(5, collaborator.getValue());
424         assertTrue(dependency.doSomething());
425 
426         // Not mocked:
427         assertTrue(collaborator.simpleOperation(0, null, null));
428         assertNull(dependency.doSomethingElse(3));
429 
430         new FullVerifications() {
431             {
432                 dependency.doSomethingElse(anyInt);
433                 collaborator.simpleOperation(0, null, null);
434             }
435         };
436     }
437 
438     /**
439      * The Interface AnotherInterface.
440      */
441     public interface AnotherInterface {
442     }
443 
444     /**
445      * The Interface NonPublicInterface.
446      */
447     interface NonPublicInterface {
448     }
449 
450     /**
451      * Attempt to use dynamic mocking for invalid types.
452      *
453      * @param publicInterfaceMock
454      *            the public interface mock
455      * @param nonPublicInterfaceMock
456      *            the non public interface mock
457      */
458     @Test
459     public void attemptToUseDynamicMockingForInvalidTypes(@Mocked AnotherInterface publicInterfaceMock,
460             @Injectable NonPublicInterface nonPublicInterfaceMock) {
461         assertInvalidTypeForDynamicPartialMocking(new String[1]);
462         assertInvalidTypeForDynamicPartialMocking(123);
463         assertInvalidTypeForDynamicPartialMocking(true);
464         assertInvalidTypeForDynamicPartialMocking(2.5);
465         assertInvalidTypeForDynamicPartialMocking(publicInterfaceMock);
466         assertInvalidTypeForDynamicPartialMocking(nonPublicInterfaceMock);
467     }
468 
469     /**
470      * Assert invalid type for dynamic partial mocking.
471      *
472      * @param object
473      *            the object
474      */
475     void assertInvalidTypeForDynamicPartialMocking(Object object) {
476         try {
477             new Expectations(object) {
478             };
479             fail();
480         } catch (IllegalArgumentException e) {
481             assertTrue(e.getMessage().contains("partial mocking"));
482         }
483     }
484 
485     /**
486      * Dynamic partial mocking with exact argument matching.
487      */
488     @Test
489     public void dynamicPartialMockingWithExactArgumentMatching() {
490         final Collaborator collaborator = new Collaborator();
491 
492         new Expectations(collaborator) {
493             {
494                 collaborator.simpleOperation(1, "s", null);
495                 result = false;
496             }
497         };
498 
499         assertFalse(collaborator.simpleOperation(1, "s", null));
500         assertTrue(collaborator.simpleOperation(2, "s", null));
501         assertTrue(collaborator.simpleOperation(1, "S", null));
502         assertTrue(collaborator.simpleOperation(1, "s", new Date()));
503         assertTrue(collaborator.simpleOperation(1, null, new Date()));
504         assertFalse(collaborator.simpleOperation(1, "s", null));
505 
506         new FullVerifications() {
507             {
508                 collaborator.simpleOperation(anyInt, null, null);
509             }
510         };
511     }
512 
513     /**
514      * Dynamic partial mocking with flexible argument matching.
515      */
516     @Test
517     public void dynamicPartialMockingWithFlexibleArgumentMatching() {
518         final Collaborator mock = new Collaborator();
519 
520         new Expectations(mock) {
521             {
522                 mock.simpleOperation(anyInt, withPrefix("s"), null);
523                 result = false;
524             }
525         };
526 
527         assertFalse(mock.simpleOperation(1, "sSs", null));
528         assertTrue(mock.simpleOperation(2, " s", null));
529         assertTrue(mock.simpleOperation(1, "S", null));
530         assertFalse(mock.simpleOperation(-1, "s", new Date()));
531         assertTrue(mock.simpleOperation(1, null, null));
532         assertFalse(mock.simpleOperation(0, "string", null));
533 
534         Collaborator collaborator = new Collaborator();
535         assertTrue(collaborator.simpleOperation(1, "sSs", null));
536         assertTrue(collaborator.simpleOperation(-1, null, new Date()));
537     }
538 
539     /**
540      * Dynamic partial mocking with instance specific matching.
541      */
542     @Test
543     public void dynamicPartialMockingWithInstanceSpecificMatching() {
544         final Collaborator collaborator1 = new Collaborator();
545         final Collaborator collaborator2 = new Collaborator(4);
546 
547         new Expectations(collaborator1, collaborator2) {
548             {
549                 collaborator1.getValue();
550                 result = 3;
551             }
552         };
553 
554         assertEquals(3, collaborator1.getValue());
555         assertEquals(4, collaborator2.getValue());
556 
557         new VerificationsInOrder() {
558             {
559                 collaborator1.getValue();
560                 times = 1;
561                 collaborator2.getValue();
562                 times = 1;
563             }
564         };
565     }
566 
567     /**
568      * Dynamic partial mocking with instance specific matching on two instances of same class.
569      */
570     @Test
571     public void dynamicPartialMockingWithInstanceSpecificMatchingOnTwoInstancesOfSameClass() {
572         final Collaborator mock1 = new Collaborator();
573         final Collaborator mock2 = new Collaborator();
574 
575         new Expectations(mock1, mock2) {
576             {
577                 mock1.getValue();
578                 result = 1;
579                 mock2.getValue();
580                 result = 2;
581             }
582         };
583 
584         assertEquals(2, mock2.getValue());
585         assertEquals(1, mock1.getValue());
586     }
587 
588     /**
589      * Method with no recorded expectation called twice during replay.
590      */
591     @Test
592     public void methodWithNoRecordedExpectationCalledTwiceDuringReplay() {
593         final Collaborator collaborator = new Collaborator(123);
594 
595         new Expectations(collaborator) {
596         };
597 
598         assertEquals(123, collaborator.getValue());
599         assertEquals(123, collaborator.getValue());
600 
601         new FullVerifications() {
602             {
603                 collaborator.getValue();
604                 times = 2;
605             }
606         };
607     }
608 
609     /**
610      * The Class ClassWithNative.
611      */
612     static final class ClassWithNative {
613 
614         /**
615          * Do something.
616          *
617          * @return the int
618          */
619         int doSomething() {
620             return nativeMethod();
621         }
622 
623         /**
624          * Native method.
625          *
626          * @return the int
627          */
628         private native int nativeMethod();
629     }
630 
631     /**
632      * Attempt to partially mock native method.
633      */
634     @Test
635     public void attemptToPartiallyMockNativeMethod() {
636         thrown.expect(UnsatisfiedLinkError.class);
637 
638         final ClassWithNative mock = new ClassWithNative();
639 
640         new Expectations(mock) {
641             {
642                 // The native method is ignored when using dynamic mocking, so this actually tries to execute the real
643                 // method,
644                 // failing since there is no native implementation.
645                 mock.nativeMethod();
646             }
647         };
648     }
649 
650     /**
651      * Mock annotated constructor.
652      *
653      * @param mock
654      *            the mock
655      *
656      * @throws Exception
657      *             the exception
658      */
659     @Test
660     public void mockAnnotatedConstructor(@Mocked Collaborator mock) throws Exception {
661         Constructor<?> mockedConstructor = Collaborator.class.getDeclaredConstructor(int.class);
662 
663         assertTrue(mockedConstructor.isAnnotationPresent(Deprecated.class));
664         assertTrue(mockedConstructor.getParameterAnnotations()[0][0] instanceof Deprecated);
665     }
666 
667     /**
668      * The Class TestedClass.
669      */
670     static final class TestedClass {
671 
672         /**
673          * Instantiates a new tested class.
674          */
675         TestedClass() {
676             this(true);
677         }
678 
679         /**
680          * Instantiates a new tested class.
681          *
682          * @param value
683          *            the value
684          */
685         TestedClass(boolean value) {
686             initialize(value);
687         }
688 
689         /**
690          * Initialize.
691          *
692          * @param flag
693          *            the flag
694          */
695         private void initialize(@SuppressWarnings("unused") boolean flag) {
696         }
697     }
698 
699     /**
700      * The Class BaseClass.
701      */
702     static class BaseClass {
703         /**
704          * Instantiates a new base class.
705          *
706          * @param o
707          *            the o
708          */
709         @SuppressWarnings("unused")
710         BaseClass(Object o) {
711             assert o != null;
712         }
713 
714         /**
715          * Instantiates a new base class.
716          */
717         BaseClass() {
718         }
719     }
720 
721     /**
722      * The Class SubClass.
723      */
724     static class SubClass extends BaseClass {
725     }
726 
727     /**
728      * The Class SubSubClass.
729      */
730     static class SubSubClass extends SubClass {
731     }
732 
733     /**
734      * Mock class indirectly extending base whose first constructor has more parameters than the second one.
735      *
736      * @param mock
737      *            the mock
738      */
739     @Test
740     public void mockClassIndirectlyExtendingBaseWhoseFirstConstructorHasMoreParametersThanTheSecondOne(
741             @Mocked SubSubClass mock) {
742         new SubClass();
743     }
744 }