1 /*
2  * Copyright (c) 2007 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockitousage.annotation;
6 
7 import java.util.AbstractList;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.LinkedList;
11 import java.util.List;
12 import org.junit.Rule;
13 import org.junit.Test;
14 import org.junit.rules.ExpectedException;
15 import org.mockito.Mock;
16 import org.mockito.Mockito;
17 import org.mockito.MockitoAnnotations;
18 import org.mockito.Spy;
19 import org.mockito.exceptions.base.MockitoException;
20 import org.mockitoutil.TestBase;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.assertj.core.api.Assertions.assertThat;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.eq;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 @SuppressWarnings("unused")
34 public class SpyAnnotationTest extends TestBase {
35 
36     @Spy
37     final List<String> spiedList = new ArrayList<String>();
38 
39     @Spy
40     InnerStaticClassWithNoArgConstructor staticTypeWithNoArgConstructor;
41 
42     @Spy
43     InnerStaticClassWithoutDefinedConstructor staticTypeWithoutDefinedConstructor;
44 
45     @Spy
46     MockTranslator translator;
47 
48     @Rule
49     public final ExpectedException shouldThrow = ExpectedException.none();
50 
51     @Test
should_init_spy_by_instance()52     public void should_init_spy_by_instance() throws Exception {
53         doReturn("foo").when(spiedList).get(10);
54         assertEquals("foo", spiedList.get(10));
55         assertTrue(spiedList.isEmpty());
56     }
57 
58     @Test
should_init_spy_and_automatically_create_instance()59     public void should_init_spy_and_automatically_create_instance() throws Exception {
60         when(staticTypeWithNoArgConstructor.toString()).thenReturn("x");
61         when(staticTypeWithoutDefinedConstructor.toString()).thenReturn("y");
62         assertEquals("x", staticTypeWithNoArgConstructor.toString());
63         assertEquals("y", staticTypeWithoutDefinedConstructor.toString());
64     }
65 
66     @Test
should_allow_spying_on_interfaces()67     public void should_allow_spying_on_interfaces() throws Exception {
68         class WithSpy {
69             @Spy
70             List<String> list;
71         }
72 
73         WithSpy withSpy = new WithSpy();
74         MockitoAnnotations.initMocks(withSpy);
75         when(withSpy.list.size()).thenReturn(3);
76         assertEquals(3, withSpy.list.size());
77     }
78 
79     @Test
should_allow_spying_on_interfaces_when_instance_is_concrete()80     public void should_allow_spying_on_interfaces_when_instance_is_concrete() throws Exception {
81         class WithSpy {
82             @Spy
83             List<String> list = new LinkedList<String>();
84         }
85         WithSpy withSpy = new WithSpy();
86 
87         //when
88         MockitoAnnotations.initMocks(withSpy);
89 
90         //then
91         verify(withSpy.list, never()).clear();
92     }
93 
94     @Test
should_report_when_no_arg_less_constructor()95     public void should_report_when_no_arg_less_constructor() throws Exception {
96         class FailingSpy {
97             @Spy
98             NoValidConstructor noValidConstructor;
99         }
100 
101         try {
102             MockitoAnnotations.initMocks(new FailingSpy());
103             fail();
104         } catch (MockitoException e) {
105             assertThat(e.getMessage()).contains("Please ensure that the type")
106                     .contains(NoValidConstructor.class.getSimpleName())
107                     .contains("has a no-arg constructor");
108         }
109     }
110 
111     @Test
should_report_when_constructor_is_explosive()112     public void should_report_when_constructor_is_explosive() throws Exception {
113         class FailingSpy {
114             @Spy
115             ThrowingConstructor throwingConstructor;
116         }
117 
118         try {
119             MockitoAnnotations.initMocks(new FailingSpy());
120             fail();
121         } catch (MockitoException e) {
122             assertThat(e.getMessage()).contains("Unable to create mock instance");
123         }
124     }
125 
126     @Test
should_spy_abstract_class()127     public void should_spy_abstract_class() throws Exception {
128         class SpyAbstractClass {
129             @Spy
130             AbstractList<String> list;
131 
132             List<String> asSingletonList(String s) {
133                 when(list.size()).thenReturn(1);
134                 when(list.get(0)).thenReturn(s);
135                 return list;
136             }
137         }
138         SpyAbstractClass withSpy = new SpyAbstractClass();
139         MockitoAnnotations.initMocks(withSpy);
140         assertEquals(Arrays.asList("a"), withSpy.asSingletonList("a"));
141     }
142 
143     @Test
should_spy_inner_class()144     public void should_spy_inner_class() throws Exception {
145 
146         class WithMockAndSpy {
147             @Spy
148             private InnerStrength strength;
149             @Mock
150             private List<String> list;
151 
152             abstract class InnerStrength {
153                 private final String name;
154 
155                 InnerStrength() {
156                     // Make sure that @Mock fields are always injected before @Spy fields.
157                     assertNotNull(list);
158                     // Make sure constructor is indeed called.
159                     this.name = "inner";
160                 }
161 
162                 abstract String strength();
163 
164                 String fullStrength() {
165                     return name + " " + strength();
166                 }
167             }
168         }
169         WithMockAndSpy outer = new WithMockAndSpy();
170         MockitoAnnotations.initMocks(outer);
171         when(outer.strength.strength()).thenReturn("strength");
172         assertEquals("inner strength", outer.strength.fullStrength());
173     }
174 
175     @Test(expected = IndexOutOfBoundsException.class)
should_reset_spy()176     public void should_reset_spy() throws Exception {
177         spiedList.get(10); // see shouldInitSpy
178     }
179 
180     @Test
should_report_when_enclosing_instance_is_needed()181     public void should_report_when_enclosing_instance_is_needed() throws Exception {
182         class Outer {
183             class Inner {
184             }
185         }
186         class WithSpy {
187             @Spy
188             private Outer.Inner inner;
189         }
190         try {
191             MockitoAnnotations.initMocks(new WithSpy());
192             fail();
193         } catch (MockitoException e) {
194             assertThat(e).hasMessageContaining("@Spy annotation can only initialize inner classes");
195         }
196     }
197 
198     @Test
should_report_private_inner_not_supported()199     public void should_report_private_inner_not_supported() throws Exception {
200         try {
201             MockitoAnnotations.initMocks(new WithInnerPrivate());
202             fail();
203         } catch (MockitoException e) {
204             // Currently fails at instantiation time, because the mock subclass don't have the
205             // 1-arg constructor expected for the outerclass.
206             // org.mockito.internal.creation.instance.ConstructorInstantiator.withParams()
207             assertThat(e).hasMessageContaining("Unable to initialize @Spy annotated field 'spy_field'")
208                     .hasMessageContaining(WithInnerPrivate.InnerPrivate.class.getSimpleName());
209         }
210     }
211 
212     @Test
should_report_private_abstract_inner_not_supported()213     public void should_report_private_abstract_inner_not_supported() throws Exception {
214         try {
215             MockitoAnnotations.initMocks(new WithInnerPrivateAbstract());
216             fail();
217         } catch (MockitoException e) {
218             assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
219                     .hasMessageContaining(WithInnerPrivateAbstract.class.getSimpleName())
220                     .hasMessageContaining(WithInnerPrivateAbstract.InnerPrivateAbstract.class.getSimpleName())
221                     .hasMessageContaining("You should augment the visibility of this inner class");
222         }
223     }
224 
225     @Test
should_report_private_static_abstract_inner_not_supported()226     public void should_report_private_static_abstract_inner_not_supported() throws Exception {
227         try {
228             MockitoAnnotations.initMocks(new WithInnerPrivateStaticAbstract());
229             fail();
230         } catch (MockitoException e) {
231             assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes")
232                     .hasMessageContaining(WithInnerPrivateStaticAbstract.class.getSimpleName())
233                     .hasMessageContaining(WithInnerPrivateStaticAbstract.InnerPrivateStaticAbstract.class.getSimpleName())
234                     .hasMessageContaining("You should augment the visibility of this inner class");
235         }
236     }
237 
238     @Test
should_be_able_to_stub_and_verify_via_varargs_for_list_params()239     public void should_be_able_to_stub_and_verify_via_varargs_for_list_params() throws Exception {
240       // You can stub with varargs.
241       when(translator.translate("hello", "mockito")).thenReturn(Arrays.asList("you", "too"));
242 
243       // Pretend the prod code will call translate(List<String>) with these elements.
244       assertThat(translator.translate(Arrays.asList("hello", "mockito"))).containsExactly("you", "too");
245       assertThat(translator.translate(Arrays.asList("not stubbed"))).isEmpty();
246 
247       // You can verify with varargs.
248       verify(translator).translate("hello", "mockito");
249     }
250 
251     @Test
should_be_able_to_stub_and_verify_via_varargs_of_matchers_for_list_params()252     public void should_be_able_to_stub_and_verify_via_varargs_of_matchers_for_list_params() throws Exception {
253       // You can stub with varargs of matchers.
254       when(translator.translate(Mockito.anyString())).thenReturn(Arrays.asList("huh?"));
255       when(translator.translate(eq("hello"))).thenReturn(Arrays.asList("hi"));
256 
257       // Pretend the prod code will call translate(List<String>) with these elements.
258       assertThat(translator.translate(Arrays.asList("hello"))).containsExactly("hi");
259       assertThat(translator.translate(Arrays.asList("not explicitly stubbed"))).containsExactly("huh?");
260 
261       // You can verify with varargs of matchers.
262       verify(translator).translate(eq("hello"));
263     }
264 
265     static class WithInnerPrivateStaticAbstract {
266         @Spy
267         private InnerPrivateStaticAbstract spy_field;
268 
269         private static abstract class InnerPrivateStaticAbstract {
270         }
271     }
272     static class WithInnerPrivateAbstract {
273         @Spy
274         private InnerPrivateAbstract spy_field;
275 
some_method()276         public void some_method() {
277             new InnerPrivateConcrete();
278         }
279 
280         private abstract class InnerPrivateAbstract {
281         }
282 
283         private class InnerPrivateConcrete extends InnerPrivateAbstract {
284 
285         }
286     }
287 
288     static class WithInnerPrivate {
289         @Spy
290         private InnerPrivate spy_field;
291 
292         private class InnerPrivate {
293         }
294 
295         private class InnerPrivateSub extends InnerPrivate {}
296     }
297 
298     static class InnerStaticClassWithoutDefinedConstructor {
299     }
300 
301     static class InnerStaticClassWithNoArgConstructor {
InnerStaticClassWithNoArgConstructor()302         InnerStaticClassWithNoArgConstructor() {
303         }
304 
InnerStaticClassWithNoArgConstructor(String f)305         InnerStaticClassWithNoArgConstructor(String f) {
306         }
307     }
308 
309     static class NoValidConstructor {
NoValidConstructor(String f)310         NoValidConstructor(String f) {
311         }
312     }
313 
314     static class ThrowingConstructor {
ThrowingConstructor()315         ThrowingConstructor() {
316             throw new RuntimeException("boo!");
317         }
318     }
319 
320     interface Translator {
translate(List<String> messages)321       List<String> translate(List<String> messages);
322     }
323 
324     static abstract class MockTranslator implements Translator {
translate(List<String> messages)325       @Override public final List<String> translate(List<String> messages) {
326         return translate(messages.toArray(new String[0]));
327       }
328 
translate(String... messages)329       abstract List<String> translate(String... messages);
330     }
331 }
332