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 java.util.Arrays.asList;
9   import static java.util.Collections.singletonList;
10  
11  import static mockit.ExpectationsWithArgMatchersTest.Delegates.collectionElement;
12  import static org.hamcrest.CoreMatchers.equalTo;
13  import static org.hamcrest.CoreMatchers.hasItem;
14  import static org.hamcrest.Matchers.containsInAnyOrder;
15  import static org.junit.jupiter.api.Assertions.assertEquals;
16  import static org.junit.jupiter.api.Assertions.assertFalse;
17  import static org.junit.jupiter.api.Assertions.assertNull;
18  import static org.junit.jupiter.api.Assertions.assertSame;
19  import static org.junit.jupiter.api.Assertions.assertTrue;
20  
21  import java.security.cert.Certificate;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Date;
25  import java.util.List;
26  
27  import mockit.integration.junit5.ExpectedException;
28  import mockit.integration.junit5.JMockitExtension;
29  import mockit.internal.expectations.invocation.MissingInvocation;
30  
31  import org.junit.jupiter.api.Test;
32  import org.junit.jupiter.api.extension.ExtendWith;
33  
34  /**
35   * The Class ExpectationsWithArgMatchersTest.
36   */
37  @ExtendWith(JMockitExtension.class)
38  class ExpectationsWithArgMatchersTest {
39  
40      /**
41       * The Class Collaborator.
42       */
43      @SuppressWarnings("unused")
44      static class Collaborator {
45  
46          /**
47           * Sets the value.
48           *
49           * @param value
50           *            the new value
51           */
52          void setValue(int value) {
53          }
54  
55          /**
56           * Sets the value.
57           *
58           * @param value
59           *            the new value
60           */
61          void setValue(double value) {
62          }
63  
64          /**
65           * Sets the value.
66           *
67           * @param value
68           *            the value
69           *
70           * @return true, if successful
71           */
72          boolean setValue(float value) {
73              return false;
74          }
75  
76          /**
77           * Sets the value.
78           *
79           * @param value
80           *            the new value
81           */
82          void setValue(char value) {
83          }
84  
85          /**
86           * Sets the value.
87           *
88           * @param value
89           *            the new value
90           */
91          void setValue(String value) {
92          }
93  
94          /**
95           * Sets the values.
96           *
97           * @param c
98           *            the c
99           * @param b
100          *            the b
101          */
102         void setValues(char c, boolean b) {
103         }
104 
105         /**
106          * Sets the values.
107          *
108          * @param values
109          *            the new values
110          */
111         void setValues(String[] values) {
112         }
113 
114         /**
115          * Sets the textual values.
116          *
117          * @param values
118          *            the new textual values
119          */
120         void setTextualValues(Collection<String> values) {
121         }
122 
123         /**
124          * Do something.
125          *
126          * @param i
127          *            the i
128          */
129         void doSomething(Integer i) {
130         }
131 
132         /**
133          * Do something.
134          *
135          * @param s
136          *            the s
137          *
138          * @return true, if successful
139          */
140         boolean doSomething(String s) {
141             return false;
142         }
143 
144         /**
145          * Simple operation.
146          *
147          * @param a
148          *            the a
149          * @param b
150          *            the b
151          * @param c
152          *            the c
153          */
154         final void simpleOperation(int a, String b, Date c) {
155         }
156 
157         /**
158          * Sets the value.
159          *
160          * @param cert
161          *            the new value
162          */
163         void setValue(Certificate cert) {
164         }
165 
166         /**
167          * Sets the value.
168          *
169          * @param ex
170          *            the new value
171          */
172         void setValue(Exception ex) {
173         }
174 
175         void setIntValue(int i) {
176         }
177 
178         /**
179          * Use object.
180          *
181          * @param arg
182          *            the arg
183          *
184          * @return the string
185          */
186         String useObject(Object arg) {
187             return "";
188         }
189     }
190 
191     /** The mock. */
192     @Mocked
193     Collaborator mock;
194 
195     /**
196      * Verify expectation numeric equality matcher but fail to match on replay.
197      */
198     @Test
199     @ExpectedException(value = MissingInvocation.class, expectedMessages = { "setValue",
200             "a numeric value within 0.01 of 2.3", "instead got", "setValue(2.32)" })
201     void verifyExpectationNumericEqualityMatcherButFailToMatchOnReplay() {
202         mock.setValue(2.32);
203 
204         new Verifications() {
205             {
206                 mock.setValue(withEqual(2.3, 0.01));
207             }
208         };
209     }
210 
211     /**
212      * Verify expectation using numeric equality matcher but replay with non numeric parameter type.
213      */
214     @Test
215     @ExpectedException(MissingInvocation.class)
216     void verifyExpectationUsingNumericEqualityMatcherButReplayWithNonNumericParameterType() {
217         mock.useObject('2');
218 
219         new Verifications() {
220             {
221                 mock.useObject(withEqual(2.3, 0.01));
222             }
223         };
224     }
225 
226     /**
227      * Verify expectation using inequality matcher but fail to match on replay.
228      */
229     @Test
230     @ExpectedException(value = MissingInvocation.class, expectedMessages = { "(not 2)", "got", "(2)" })
231     void verifyExpectationUsingInequalityMatcherButFailToMatchOnReplay() {
232         mock.setValue(2);
233 
234         new Verifications() {
235             {
236                 mock.setValue(withNotEqual(2));
237             }
238         };
239     }
240 
241     /**
242      * Verify expectations using numeric equality matchers.
243      */
244     @Test
245     void verifyExpectationsUsingNumericEqualityMatchers() {
246         new Expectations() {
247             {
248                 mock.setValue(withEqual(2.0F, 0.01F));
249                 result = true;
250                 mock.setValue(withEqual(2.0F, 0.05F));
251                 result = false; // still overwrites the previous expectation, due to overlap in delta
252             }
253         };
254 
255         assertFalse(mock.setValue(2.0F));
256         assertFalse(mock.setValue(2.05F));
257     }
258 
259     /**
260      * Record expectation with delegate without the parameter type.
261      */
262     @Test
263     void recordExpectationWithDelegateWithoutTheParameterType() {
264         new Expectations() {
265             {
266                 mock.useObject(with(new Delegate() { // only compiles for a parameter of type Object
267                     @SuppressWarnings("unused")
268                     boolean delegate(Object arg) {
269                         return "test".equals(arg);
270                     }
271                 }));
272             }
273         };
274 
275         String result = mock.useObject("test");
276 
277         assertNull(result);
278     }
279 
280     /**
281      * The Class CollectionElementDelegate.
282      *
283      * @param <T>
284      *            the generic type
285      */
286     static final class CollectionElementDelegate<T> implements Delegate<Collection<T>> {
287 
288         /** The item. */
289         private final T item;
290 
291         /**
292          * Instantiates a new collection element delegate.
293          *
294          * @param item
295          *            the item
296          */
297         CollectionElementDelegate(T item) {
298             this.item = item;
299         }
300 
301         /**
302          * Checks for item.
303          *
304          * @param items
305          *            the items
306          *
307          * @return true, if successful
308          */
309         @SuppressWarnings("unused")
310         boolean hasItem(Collection<T> items) {
311             return items.contains(item);
312         }
313     }
314 
315     /**
316      * The Class Delegates.
317      */
318     @SuppressWarnings("unused")
319     static final class Delegates {
320 
321         /**
322          * Collection element.
323          *
324          * @param <T>
325          *            the generic type
326          * @param item
327          *            the item
328          *
329          * @return the delegate
330          */
331         static <T> Delegate<Collection<T>> collectionElement(T item) {
332             return new CollectionElementDelegate<>(item);
333         }
334     }
335 
336     /**
337      * Expect invocations with named delegate matcher.
338      */
339     @Test
340     void expectInvocationsWithNamedDelegateMatcher() {
341         new Expectations() {
342             {
343                 mock.setTextualValues(with(collectionElement("B")));
344             }
345         };
346 
347         List<String> values = asList("a", "B", "c");
348         mock.setTextualValues(values);
349     }
350 
351     @Test
352     void expectInvocationsWithHamcrestMatcher() {
353         new Expectations() {
354             {
355                 mock.setTextualValues(this.<Collection<String>> withArgThat(hasItem("B")));
356             }
357         };
358 
359         List<String> values = asList("a", "B", "c");
360         mock.setTextualValues(values);
361     }
362 
363     @Test
364     void expectInvocationsWithHamcrestMatcher2() {
365         new Expectations() {
366             {
367                 mock.setTextualValues(withArgThat(containsInAnyOrder("B", "c", "a")));
368             }
369         };
370 
371         List<String> values = asList("a", "B", "c");
372         mock.setTextualValues(values);
373     }
374 
375     @Test
376     void expectInvocationWithMatcherContainingAnotherMatcher() {
377         new Expectations() {
378             {
379                 mock.setIntValue(withArgThat(equalTo(3)));
380             }
381         };
382 
383         mock.setIntValue(3);
384     }
385 
386     /**
387      * Use mocked method before recording expectation with argument matcher.
388      */
389     @Test
390     void useMockedMethodBeforeRecordingExpectationWithArgumentMatcher() {
391         assertFalse(mock.doSomething("abc"));
392 
393         new Expectations() {
394             {
395                 mock.doSomething(anyString);
396                 result = true;
397             }
398         };
399 
400         assertTrue(mock.doSomething("xyz"));
401         assertTrue(mock.doSomething("abc"));
402     }
403 
404     /**
405      * Record expectations using the any fields for parameter of type object.
406      */
407     @Test
408     void recordExpectationsUsingTheAnyFieldsForParameterOfTypeObject() {
409         new Expectations() {
410             {
411                 mock.useObject(anyString);
412                 result = "String";
413                 mock.useObject(anyInt);
414                 result = "int";
415                 mock.useObject(anyByte);
416                 result = "byte";
417                 mock.useObject(anyShort);
418                 result = "short";
419                 mock.useObject(anyLong);
420                 result = "long";
421                 mock.useObject(anyBoolean);
422                 result = "boolean";
423                 mock.useObject(anyChar);
424                 result = "char";
425                 mock.useObject(anyFloat);
426                 result = "float";
427                 mock.useObject(anyDouble);
428                 result = "double";
429                 mock.useObject(any);
430                 result = "Object";
431             }
432         };
433 
434         assertInvocationsWithArgumentsOfDifferentTypesToMethodAcceptingAnyObject();
435     }
436 
437     /**
438      * Assert invocations with arguments of different types to method accepting any object.
439      */
440     void assertInvocationsWithArgumentsOfDifferentTypesToMethodAcceptingAnyObject() {
441         assertEquals("String", mock.useObject("test"));
442         assertEquals("String", mock.useObject(null)); // uses the first recorded expectation, since they all match null
443         assertEquals("int", mock.useObject(2));
444         assertEquals("Object", mock.useObject(new Object()));
445         assertEquals("byte", mock.useObject((byte) -3));
446         assertEquals("short", mock.useObject((short) 4));
447         assertEquals("long", mock.useObject(-5L));
448         assertEquals("boolean", mock.useObject(true));
449         assertEquals("boolean", mock.useObject(false));
450         assertEquals("char", mock.useObject('A'));
451         assertEquals("float", mock.useObject(-1.5F));
452         assertEquals("double", mock.useObject(23.456));
453     }
454 
455     /**
456      * Record expectations using the with any method for parameter of type object.
457      */
458     @Test
459     void recordExpectationsUsingTheWithAnyMethodForParameterOfTypeObject() {
460         new Expectations() {
461             {
462                 mock.useObject(withAny("a"));
463                 result = "String";
464                 mock.useObject(withAny(2));
465                 result = "int";
466                 mock.useObject(withAny((byte) 3));
467                 result = "byte";
468                 mock.useObject(withAny((short) 4));
469                 result = "short";
470                 mock.useObject(withAny(5L));
471                 result = "long";
472                 mock.useObject(withAny(true));
473                 result = "boolean";
474                 mock.useObject(withAny('\0'));
475                 result = "char";
476                 mock.useObject(withAny(0.41F));
477                 result = "float";
478                 mock.useObject(withAny(0.41));
479                 result = "double";
480                 mock.useObject(withAny(new Object()));
481                 result = "Object";
482             }
483         };
484 
485         assertInvocationsWithArgumentsOfDifferentTypesToMethodAcceptingAnyObject();
486     }
487 
488     /**
489      * Declare field in expectation block with name having same prefix as argument matching field.
490      */
491     @Test
492     void declareFieldInExpectationBlockWithNameHavingSamePrefixAsArgumentMatchingField() {
493         new Expectations() {
494             final Integer anyValue = 1;
495 
496             {
497                 mock.setValue(anyValue);
498             }
499         };
500 
501         mock.setValue(1);
502     }
503 
504     /**
505      * Declare method in expectation block with name having same prefix as argument matching method.
506      */
507     @Test
508     void declareMethodInExpectationBlockWithNameHavingSamePrefixAsArgumentMatchingMethod() {
509         final List<Integer> values = new ArrayList<>();
510 
511         new Expectations() {
512             {
513                 mock.setValues(withEqual('c'), anyBoolean);
514                 mock.setValue(withCapture(values));
515             }
516 
517             char withEqual(char c) {
518                 return c;
519             }
520         };
521 
522         mock.setValues('c', true);
523         final Collaborator col = new Collaborator();
524         col.setValue(1);
525 
526         assertEquals(singletonList(1), values);
527 
528         new Verifications() {
529             {
530                 int i;
531                 mock.setValue(i = withCapture());
532                 assertEquals(1, i);
533 
534                 List<Collaborator> collaborators = withCapture(new Collaborator());
535                 assertSame(col, collaborators.get(0));
536             }
537         };
538     }
539 
540     // "Missing invocations"
541     // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
542 
543     /**
544      * Expect invocation with same mock instance but replay with null.
545      *
546      * @param cert
547      *            the cert
548      */
549     @Test
550     @ExpectedException(MissingInvocation.class)
551     void expectInvocationWithSameMockInstanceButReplayWithNull(
552             // This class defines an abstract "toString" override, which initially was erroneously
553             // mocked, causing a new expectation to be created during replay:
554             @Mocked final Certificate cert) {
555         new Expectations() {
556             {
557                 mock.setValue(withSameInstance(cert));
558                 times = 1;
559             }
560         };
561 
562         mock.setValue((Certificate) null);
563     }
564 
565     /**
566      * Expect invocation with matcher which invokes mocked method.
567      */
568     @Test
569     @ExpectedException(MissingInvocation.class)
570     void expectInvocationWithMatcherWhichInvokesMockedMethod() {
571         new Expectations() {
572             {
573                 mock.setValue(with(new Delegate<Integer>() {
574                     @Mock
575                     boolean validateAsPositive(int value) {
576                         // Invoking mocked method caused ConcurrentModificationException (bug fixed):
577                         mock.simpleOperation(1, "b", null);
578                         return value > 0;
579                     }
580                 }));
581             }
582         };
583 
584         mock.setValue(-3);
585     }
586 
587     // Verifications
588     // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
589 
590     /**
591      * The Class ReusableMatcher.
592      */
593     static class ReusableMatcher implements Delegate<Integer> {
594 
595         /**
596          * Checks if is positive.
597          *
598          * @param i
599          *            the i
600          *
601          * @return true, if is positive
602          */
603         @Mock
604         final boolean isPositive(int i) {
605             return i > 0;
606         }
607     }
608 
609     /**
610      * Extending A reusable argument matcher.
611      */
612     @Test
613     void extendingAReusableArgumentMatcher() {
614         mock.setValue(5);
615         mock.setValue(123);
616 
617         new Verifications() {
618             {
619                 mock.setValue(with(new ReusableMatcher() {
620                 }));
621                 times = 2;
622             }
623         };
624     }
625 }