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