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