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.assertNotNull;
10  import static org.junit.jupiter.api.Assertions.assertTrue;
11  
12  import java.lang.reflect.Method;
13  
14  import mockit.integration.junit5.ExpectedException;
15  import mockit.integration.junit5.JMockitExtension;
16  import mockit.internal.expectations.invocation.MissingInvocation;
17  import mockit.internal.expectations.invocation.UnexpectedInvocation;
18  
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.Disabled;
21  import org.junit.jupiter.api.Test;
22  import org.junit.jupiter.api.extension.ExtendWith;
23  
24  /**
25   * The Class ExpectationsTest.
26   */
27  @ExtendWith(JMockitExtension.class)
28  @SuppressWarnings({ "unused", "ParameterHidesMemberVariable" })
29  class ExpectationsTest {
30  
31      /**
32       * The Class Dependency.
33       */
34      @Deprecated
35      public static class Dependency {
36  
37          /** The value. */
38          @Deprecated
39          int value;
40  
41          /**
42           * Instantiates a new dependency.
43           */
44          @Deprecated
45          public Dependency() {
46              value = -1;
47          }
48  
49          /**
50           * Sets the something.
51           *
52           * @param value
53           *            the new something
54           */
55          @Disabled("test")
56          public void setSomething(@Deprecated int value) {
57          }
58  
59          /**
60           * Sets the something else.
61           *
62           * @param value
63           *            the new something else
64           */
65          public void setSomethingElse(String value) {
66          }
67  
68          /**
69           * Do something.
70           *
71           * @param i
72           *            the i
73           * @param b
74           *            the b
75           *
76           * @return the int
77           */
78          public int doSomething(Integer i, boolean b) {
79              return i;
80          }
81  
82          /**
83           * Edits the A bunch more stuff.
84           *
85           * @return the int
86           */
87          public int editABunchMoreStuff() {
88              return 1;
89          }
90  
91          /**
92           * Notify before save.
93           *
94           * @return true, if successful
95           */
96          public boolean notifyBeforeSave() {
97              return true;
98          }
99  
100         /**
101          * Prepare.
102          */
103         public void prepare() {
104         }
105 
106         /**
107          * Save.
108          */
109         public void save() {
110         }
111 
112         /**
113          * Static method.
114          *
115          * @param o
116          *            the o
117          * @param e
118          *            the e
119          *
120          * @return the int
121          */
122         static int staticMethod(Object o, Exception e) {
123             return -1;
124         }
125     }
126 
127     /** The mock. */
128     @Mocked
129     Dependency mock;
130 
131     /**
132      * Exercise code under test.
133      */
134     void exerciseCodeUnderTest() {
135         mock.prepare();
136         mock.setSomething(123);
137         mock.setSomethingElse("anotherValue");
138         mock.setSomething(45);
139         mock.editABunchMoreStuff();
140         mock.notifyBeforeSave();
141         mock.save();
142     }
143 
144     /**
145      * Record simple invocations.
146      */
147     @Test
148     void recordSimpleInvocations() {
149         new Expectations() {
150             {
151                 mock.prepare();
152                 mock.editABunchMoreStuff();
153                 mock.setSomething(45);
154             }
155         };
156 
157         exerciseCodeUnderTest();
158     }
159 
160     /**
161      * Record invocation that will not occur.
162      */
163     @Test
164     void recordInvocationThatWillNotOccur() {
165         new Expectations() {
166             {
167                 mock.editABunchMoreStuff();
168                 result = 123;
169                 times = 0;
170             }
171         };
172 
173         mock.setSomething(123);
174         mock.prepare();
175     }
176 
177     /**
178      * Expectations recorded on same method with same matchers but different arguments.
179      */
180     @Test
181     void expectationsRecordedOnSameMethodWithSameMatchersButDifferentArguments() {
182         new Expectations() {
183             {
184                 mock.doSomething(1, anyBoolean);
185                 result = 1;
186                 mock.doSomething(2, anyBoolean);
187                 result = 2;
188             }
189         };
190 
191         assertEquals(1, mock.doSomething(1, true));
192         assertEquals(2, mock.doSomething(2, false));
193         assertEquals(0, mock.doSomething(3, false));
194     }
195 
196     /**
197      * Expectations recorded on same method with matcher in one and fixed argument in another.
198      */
199     @Test
200     void expectationsRecordedOnSameMethodWithMatcherInOneAndFixedArgumentInAnother() {
201         new Expectations() {
202             {
203                 mock.doSomething(1, anyBoolean);
204                 result = 1;
205                 mock.doSomething(anyInt, anyBoolean);
206                 result = 2;
207             }
208         };
209 
210         assertEquals(1, mock.doSomething(1, true));
211         assertEquals(2, mock.doSomething(null, false));
212         assertEquals(2, mock.doSomething(2, true));
213         assertEquals(1, mock.doSomething(1, false));
214     }
215 
216     /**
217      * Record invocation with exact expected number of invocations but fail to satisfy.
218      */
219     @Test
220     @ExpectedException(MissingInvocation.class)
221     void recordInvocationWithExactExpectedNumberOfInvocationsButFailToSatisfy() {
222         new Expectations() {
223             {
224                 mock.editABunchMoreStuff();
225                 times = 1;
226             }
227         };
228     }
229 
230     /**
231      * Record invocation with minimum expected number of invocations but fail to satisfy.
232      */
233     @Test
234     @ExpectedException(MissingInvocation.class)
235     void recordInvocationWithMinimumExpectedNumberOfInvocationsButFailToSatisfy() {
236         new Expectations() {
237             {
238                 mock.editABunchMoreStuff();
239                 minTimes = 2;
240             }
241         };
242         mock.editABunchMoreStuff();
243     }
244 
245     /**
246      * Record invocation with maximum expected number of invocations but fail to satisfy.
247      */
248     @Test
249     @ExpectedException(UnexpectedInvocation.class)
250     void recordInvocationWithMaximumExpectedNumberOfInvocationsButFailToSatisfy() {
251         new Expectations() {
252             {
253                 mock.editABunchMoreStuff();
254                 maxTimes = 1;
255             }
256         };
257 
258         mock.editABunchMoreStuff();
259         mock.editABunchMoreStuff();
260     }
261 
262     /**
263      * Record invocations with expected invocation counts.
264      */
265     @Test
266     void recordInvocationsWithExpectedInvocationCounts() {
267         new Expectations() {
268             {
269                 mock.setSomethingElse(anyString);
270                 minTimes = 1;
271                 mock.save();
272                 times = 2;
273             }
274         };
275 
276         mock.setSomething(3);
277         mock.save();
278         mock.setSomethingElse("test");
279         mock.save();
280     }
281 
282     /**
283      * Record invocations with min invocation count larger than will occur.
284      */
285     @Test
286     @ExpectedException(MissingInvocation.class)
287     void recordInvocationsWithMinInvocationCountLargerThanWillOccur() {
288         new Expectations() {
289             {
290                 mock.save();
291                 minTimes = 2;
292             }
293         };
294 
295         mock.save();
296     }
297 
298     /**
299      * Record with argument matcher and individual invocation counts.
300      */
301     @Test
302     void recordWithArgumentMatcherAndIndividualInvocationCounts() {
303         new Expectations() {
304             {
305                 mock.prepare();
306                 maxTimes = 1;
307                 mock.setSomething(anyInt);
308                 minTimes = 2;
309                 mock.editABunchMoreStuff();
310                 maxTimes = 5;
311                 mock.save();
312                 times = 1;
313             }
314         };
315 
316         exerciseCodeUnderTest();
317     }
318 
319     /**
320      * Record with max invocation count followed by return value.
321      */
322     @Test
323     void recordWithMaxInvocationCountFollowedByReturnValue() {
324         new Expectations() {
325             {
326                 Dependency.staticMethod(any, null);
327                 maxTimes = 1;
328                 result = 1;
329             }
330         };
331 
332         assertEquals(1, Dependency.staticMethod(new Object(), new Exception()));
333     }
334 
335     /**
336      * Record with max invocation count followed by return value but replay one time beyond max.
337      */
338     @Test
339     @ExpectedException(UnexpectedInvocation.class)
340     void recordWithMaxInvocationCountFollowedByReturnValueButReplayOneTimeBeyondMax() {
341         new Expectations() {
342             {
343                 Dependency.staticMethod(any, null);
344                 maxTimes = 1;
345                 result = 1;
346             }
347         };
348 
349         Dependency.staticMethod(null, null);
350         Dependency.staticMethod(null, null);
351     }
352 
353     /**
354      * Record with return value followed by expected invocation count.
355      */
356     @Test
357     void recordWithReturnValueFollowedByExpectedInvocationCount() {
358         new Expectations() {
359             {
360                 Dependency.staticMethod(any, null);
361                 result = 1;
362                 times = 1;
363             }
364         };
365 
366         assertEquals(1, Dependency.staticMethod(null, null));
367     }
368 
369     /**
370      * Record with min invocation count followed by return value using delegate.
371      */
372     @Test
373     void recordWithMinInvocationCountFollowedByReturnValueUsingDelegate() {
374         new Expectations() {
375             {
376                 Dependency.staticMethod(any, null);
377                 minTimes = 1;
378                 result = new Delegate<Object>() {
379                     int staticMethod(Object o, Exception e) {
380                         return 1;
381                     }
382                 };
383             }
384         };
385 
386         assertEquals(1, Dependency.staticMethod(null, null));
387     }
388 
389     /**
390      * Mocked class with annotated elements.
391      *
392      * @throws Exception
393      *             the exception
394      */
395     @Test
396     void mockedClassWithAnnotatedElements() throws Exception {
397         Class<?> mockedClass = mock.getClass();
398         assertTrue(mockedClass.isAnnotationPresent(Deprecated.class));
399         assertTrue(mockedClass.getDeclaredField("value").isAnnotationPresent(Deprecated.class));
400         assertTrue(mockedClass.getDeclaredConstructor().isAnnotationPresent(Deprecated.class));
401 
402         Method mockedMethod = mockedClass.getDeclaredMethod("setSomething", int.class);
403         Disabled disabled = mockedMethod.getAnnotation(Disabled.class);
404         assertNotNull(disabled);
405         assertEquals("test", disabled.value());
406         assertTrue(mockedMethod.getParameterAnnotations()[0][0] instanceof Deprecated);
407     }
408 
409     /**
410      * The Class Collaborator.
411      */
412     static class Collaborator {
413 
414         /** The value. */
415         private int value;
416 
417         /**
418          * Gets the value.
419          *
420          * @return the value
421          */
422         int getValue() {
423             return value;
424         }
425 
426         /**
427          * Sets the value.
428          *
429          * @param value
430          *            the new value
431          */
432         void setValue(int value) {
433             this.value = value;
434         }
435 
436         /**
437          * Provide some service.
438          */
439         void provideSomeService() {
440         }
441 
442         /**
443          * Do something.
444          *
445          * @param s
446          *            the s
447          *
448          * @return the string
449          */
450         String doSomething(String s) {
451             return s.toLowerCase();
452         }
453 
454         /**
455          * Do internal.
456          *
457          * @return the string
458          */
459         static String doInternal() {
460             return "123";
461         }
462     }
463 
464     /**
465      * Expect only one invocation but exercise others during replay.
466      *
467      * @param mock
468      *            the mock
469      */
470     @Test
471     @ExpectedException(UnexpectedInvocation.class)
472     void expectOnlyOneInvocationButExerciseOthersDuringReplay(@Mocked final Collaborator mock) {
473         new Expectations() {
474             {
475                 mock.provideSomeService();
476             }
477         };
478 
479         mock.provideSomeService();
480         mock.setValue(1);
481 
482         new FullVerifications() {
483         };
484     }
485 
486     /**
487      * Expect nothing on mocked type but exercise it during replay.
488      *
489      * @param mock
490      *            the mock
491      */
492     @Test
493     @ExpectedException(UnexpectedInvocation.class)
494     void expectNothingOnMockedTypeButExerciseItDuringReplay(@Mocked final Collaborator mock) {
495         new Expectations() {
496             {
497                 mock.setValue(anyInt);
498                 times = 0;
499             }
500         };
501 
502         mock.setValue(2);
503 
504         new FullVerifications() {
505         };
506     }
507 
508     /**
509      * Replay with unexpected static method invocation.
510      *
511      * @param mock
512      *            the mock
513      */
514     @Test
515     @ExpectedException(MissingInvocation.class)
516     void replayWithUnexpectedStaticMethodInvocation(@Mocked final Collaborator mock) {
517         new Expectations() {
518             {
519                 mock.getValue();
520             }
521         };
522 
523         Collaborator.doInternal();
524 
525         Assertions.assertThrows(UnexpectedInvocation.class, () -> {
526             new FullVerifications() {
527             };
528         });
529     }
530 
531     /**
532      * Failure from unexpected invocation in another thread.
533      *
534      * @param mock
535      *            the mock
536      *
537      * @throws Exception
538      *             the exception
539      */
540     @Test
541     @ExpectedException(UnexpectedInvocation.class)
542     void failureFromUnexpectedInvocationInAnotherThread(@Mocked final Collaborator mock) throws Exception {
543         Thread t = new Thread() {
544             @Override
545             public void run() {
546                 mock.provideSomeService();
547             }
548         };
549 
550         new Expectations() {
551             {
552                 mock.getValue();
553             }
554         };
555 
556         mock.getValue();
557         t.start();
558         t.join();
559 
560         new FullVerifications() {
561         };
562     }
563 
564     /**
565      * Recording expectation on method with one argument but replaying with another should produce useful error message.
566      *
567      * @param mock
568      *            the mock
569      */
570     @Test
571     @ExpectedException(value = UnexpectedInvocation.class, expectedMessages = "another")
572     void recordingExpectationOnMethodWithOneArgumentButReplayingWithAnotherShouldProduceUsefulErrorMessage(
573             @Mocked final Collaborator mock) {
574         final String expected = "expected";
575         new Expectations() {
576             {
577                 mock.doSomething(expected);
578             }
579         };
580 
581         mock.doSomething(expected);
582 
583         String another = "another";
584         mock.doSomething(another);
585 
586         new FullVerifications() {
587         };
588     }
589 }