View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit;
7   
8   import static org.junit.jupiter.api.Assertions.assertEquals;
9   import static org.junit.jupiter.api.Assertions.assertFalse;
10  import static org.junit.jupiter.api.Assertions.assertNull;
11  import static org.junit.jupiter.api.Assertions.assertTrue;
12  import static org.junit.jupiter.api.Assertions.fail;
13  
14  import jakarta.xml.bind.annotation.XmlElement;
15  
16  import java.lang.reflect.Constructor;
17  import java.util.Date;
18  import java.util.List;
19  
20  import mockit.integration.junit5.ExpectedException;
21  import mockit.integration.junit5.JMockitExtension;
22  import mockit.internal.expectations.invocation.MissingInvocation;
23  import mockit.internal.expectations.invocation.UnexpectedInvocation;
24  
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.Disabled;
27  import org.junit.jupiter.api.Test;
28  import org.junit.jupiter.api.extension.ExtendWith;
29  
30  /**
31   * The Class PartialMockingTest.
32   */
33  @ExtendWith(JMockitExtension.class)
34  @SuppressWarnings("deprecation")
35  class PartialMockingTest {
36  
37      /**
38       * The Class Collaborator.
39       */
40      @Deprecated
41      static class Collaborator {
42  
43          /** The value. */
44          @Deprecated
45          protected int value;
46  
47          /**
48           * Instantiates a new collaborator.
49           */
50          Collaborator() {
51              value = -1;
52          }
53  
54          /**
55           * Instantiates a new collaborator.
56           *
57           * @param value
58           *            the value
59           */
60          @Deprecated
61          Collaborator(@Deprecated int value) {
62              this.value = value;
63          }
64  
65          /**
66           * Gets the value.
67           *
68           * @return the value
69           */
70          final int getValue() {
71              return value;
72          }
73  
74          /**
75           * Sets the value.
76           *
77           * @param value
78           *            the new value
79           */
80          void setValue(int value) {
81              this.value = value;
82          }
83  
84          /**
85           * Simple operation.
86           *
87           * @param a
88           *            the a
89           * @param b
90           *            the b
91           * @param c
92           *            the c
93           *
94           * @return true, if successful
95           */
96          @SuppressWarnings("unused")
97          final boolean simpleOperation(int a, @XmlElement(name = "test") String b, Date c) {
98              return true;
99          }
100 
101         /**
102          * Do something.
103          *
104          * @param b
105          *            the b
106          * @param s
107          *            the s
108          */
109         static void doSomething(@SuppressWarnings("unused") boolean b, @SuppressWarnings("unused") String s) {
110             throw new IllegalStateException();
111         }
112 
113         /**
114          * Method which calls another in the same class.
115          *
116          * @return true, if successful
117          */
118         @Disabled("test")
119         boolean methodWhichCallsAnotherInTheSameClass() {
120             return simpleOperation(1, "internal", null);
121         }
122 
123         /**
124          * Overridable method.
125          *
126          * @return the string
127          */
128         String overridableMethod() {
129             return "base";
130         }
131     }
132 
133     /**
134      * Attempt to partially mock A class.
135      */
136     @Test
137     @ExpectedException(value = IllegalArgumentException.class, expectedMessages = { "Invalid Class", "partial mocking",
138             "Collaborator" })
139     void attemptToPartiallyMockAClass() {
140         new Expectations(Collaborator.class) {
141         };
142     }
143 
144     /**
145      * Dynamic mock fully verified verify all recorded expectations but not all of the replayed ones.
146      */
147     @Test
148     void dynamicMockFullyVerified_verifyAllRecordedExpectationsButNotAllOfTheReplayedOnes() {
149         final Collaborator collaborator = new Collaborator(0);
150 
151         new Expectations(collaborator) {
152             {
153                 collaborator.setValue(1);
154             }
155         };
156 
157         collaborator.setValue(1);
158         collaborator.setValue(2);
159 
160         // Verifies all the *mocked* invocations that would be left unverified; ignores those not mocked:
161         new FullVerifications() {
162             // No need to verify "setValue(1)" since it was recorded and implicitly verified.
163             // No need to verify "setValue(2)" since it was not recorded.
164         };
165     }
166 
167     /**
168      * Dynamic mock fully verified in order verify all recorded expectations but not all of the replayed ones.
169      */
170     @Test
171     void dynamicMockFullyVerifiedInOrder_verifyAllRecordedExpectationsButNotAllOfTheReplayedOnes() {
172         final Collaborator collaborator = new Collaborator(0);
173 
174         new Expectations(collaborator) {
175             {
176                 collaborator.setValue(2);
177                 collaborator.setValue(3);
178             }
179         };
180 
181         collaborator.setValue(1);
182         collaborator.setValue(2);
183         collaborator.setValue(3);
184 
185         // Verifies all the *mocked* (recorded) invocations, ignoring those not mocked:
186         new VerificationsInOrder() {
187             {
188                 // No need to verify "setValue(1)" since it was not recorded.
189                 collaborator.setValue(2);
190                 collaborator.setValue(3);
191             }
192         };
193         new FullVerifications() {
194         };
195     }
196 
197     /**
198      * Expect two invocations on dynamic mock but replay once.
199      */
200     @Test
201     @ExpectedException(MissingInvocation.class)
202     void expectTwoInvocationsOnDynamicMockButReplayOnce() {
203         final Collaborator collaborator = new Collaborator();
204 
205         new Expectations(collaborator) {
206             {
207                 collaborator.getValue();
208                 times = 2;
209             }
210         };
211 
212         assertEquals(0, collaborator.getValue());
213     }
214 
215     /**
216      * Expect one invocation on dynamic mock but replay twice.
217      */
218     @Test
219     void expectOneInvocationOnDynamicMockButReplayTwice() {
220         final Collaborator collaborator = new Collaborator(1);
221 
222         new Expectations(collaborator) {
223             {
224                 collaborator.getValue();
225                 times = 1;
226             }
227         };
228 
229         // Mocked:
230         assertEquals(0, collaborator.getValue());
231 
232         // Still mocked because it's not strict:
233         Assertions.assertThrows(UnexpectedInvocation.class, () -> assertEquals(0, collaborator.getValue()));
234     }
235 
236     /**
237      * Dynamically mock an instance.
238      */
239     @Test
240     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     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     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     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     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     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     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     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     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     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     @ExpectedException(UnsatisfiedLinkError.class)
636     void attemptToPartiallyMockNativeMethod() {
637         final ClassWithNative mock = new ClassWithNative();
638 
639         new Expectations(mock) {
640             {
641                 // The native method is ignored when using dynamic mocking, so this actually tries to execute the real
642                 // method, failing since there is no native implementation.
643                 mock.nativeMethod();
644             }
645         };
646     }
647 
648     /**
649      * Mock annotated constructor.
650      *
651      * @param mock
652      *            the mock
653      *
654      * @throws Exception
655      *             the exception
656      */
657     @Test
658     void mockAnnotatedConstructor(@Mocked Collaborator mock) throws Exception {
659         Constructor<?> mockedConstructor = Collaborator.class.getDeclaredConstructor(int.class);
660 
661         assertTrue(mockedConstructor.isAnnotationPresent(Deprecated.class));
662         assertTrue(mockedConstructor.getParameterAnnotations()[0][0] instanceof Deprecated);
663     }
664 
665     /**
666      * The Class TestedClass.
667      */
668     static final class TestedClass {
669 
670         /**
671          * Instantiates a new tested class.
672          */
673         TestedClass() {
674             this(true);
675         }
676 
677         /**
678          * Instantiates a new tested class.
679          *
680          * @param value
681          *            the value
682          */
683         TestedClass(boolean value) {
684             initialize(value);
685         }
686 
687         /**
688          * Initialize.
689          *
690          * @param flag
691          *            the flag
692          */
693         private void initialize(@SuppressWarnings("unused") boolean flag) {
694         }
695     }
696 
697     /**
698      * The Class BaseClass.
699      */
700     static class BaseClass {
701         /**
702          * Instantiates a new base class.
703          *
704          * @param o
705          *            the o
706          */
707         @SuppressWarnings("unused")
708         BaseClass(Object o) {
709             assert o != null;
710         }
711 
712         /**
713          * Instantiates a new base class.
714          */
715         BaseClass() {
716         }
717     }
718 
719     /**
720      * The Class SubClass.
721      */
722     static class SubClass extends BaseClass {
723     }
724 
725     /**
726      * The Class SubSubClass.
727      */
728     static class SubSubClass extends SubClass {
729     }
730 
731     /**
732      * Mock class indirectly extending base whose first constructor has more parameters than the second one.
733      *
734      * @param mock
735      *            the mock
736      */
737     @Test
738     void mockClassIndirectlyExtendingBaseWhoseFirstConstructorHasMoreParametersThanTheSecondOne(
739             @Mocked SubSubClass mock) {
740         new SubClass();
741     }
742 }