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