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