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