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.MockitoAnnotations; 17 import org.mockito.Spy; 18 import org.mockito.exceptions.base.MockitoException; 19 import org.mockitoutil.TestBase; 20 21 import static junit.framework.TestCase.assertEquals; 22 import static junit.framework.TestCase.assertNotNull; 23 import static junit.framework.TestCase.assertTrue; 24 import static junit.framework.TestCase.fail; 25 import static org.assertj.core.api.Assertions.assertThat; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.when; 30 31 @SuppressWarnings("unused") 32 public class SpyAnnotationTest extends TestBase { 33 34 @Spy 35 final List<String> spiedList = new ArrayList<String>(); 36 37 @Spy 38 InnerStaticClassWithNoArgConstructor staticTypeWithNoArgConstructor; 39 40 @Spy 41 InnerStaticClassWithoutDefinedConstructor staticTypeWithoutDefinedConstructor; 42 43 @Rule 44 public final ExpectedException shouldThrow = ExpectedException.none(); 45 46 @Test should_init_spy_by_instance()47 public void should_init_spy_by_instance() throws Exception { 48 doReturn("foo").when(spiedList).get(10); 49 assertEquals("foo", spiedList.get(10)); 50 assertTrue(spiedList.isEmpty()); 51 } 52 53 @Test should_init_spy_and_automatically_create_instance()54 public void should_init_spy_and_automatically_create_instance() throws Exception { 55 when(staticTypeWithNoArgConstructor.toString()).thenReturn("x"); 56 when(staticTypeWithoutDefinedConstructor.toString()).thenReturn("y"); 57 assertEquals("x", staticTypeWithNoArgConstructor.toString()); 58 assertEquals("y", staticTypeWithoutDefinedConstructor.toString()); 59 } 60 61 @Test should_allow_spying_on_interfaces()62 public void should_allow_spying_on_interfaces() throws Exception { 63 class WithSpy { 64 @Spy 65 List<String> list; 66 } 67 68 WithSpy withSpy = new WithSpy(); 69 MockitoAnnotations.initMocks(withSpy); 70 when(withSpy.list.size()).thenReturn(3); 71 assertEquals(3, withSpy.list.size()); 72 } 73 74 @Test should_allow_spying_on_interfaces_when_instance_is_concrete()75 public void should_allow_spying_on_interfaces_when_instance_is_concrete() throws Exception { 76 class WithSpy { 77 @Spy 78 List<String> list = new LinkedList<String>(); 79 } 80 WithSpy withSpy = new WithSpy(); 81 82 //when 83 MockitoAnnotations.initMocks(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 93 NoValidConstructor noValidConstructor; 94 } 95 96 try { 97 MockitoAnnotations.initMocks(new FailingSpy()); 98 fail(); 99 } catch (MockitoException e) { 100 assertThat(e.getMessage()).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 110 ThrowingConstructor throwingConstructor; 111 } 112 113 try { 114 MockitoAnnotations.initMocks(new FailingSpy()); 115 fail(); 116 } catch (MockitoException e) { 117 assertThat(e.getMessage()).contains("Unable to create mock instance"); 118 } 119 } 120 121 @Test should_spy_abstract_class()122 public void should_spy_abstract_class() throws Exception { 123 class SpyAbstractClass { 124 @Spy 125 AbstractList<String> list; 126 127 List<String> asSingletonList(String s) { 128 when(list.size()).thenReturn(1); 129 when(list.get(0)).thenReturn(s); 130 return list; 131 } 132 } 133 SpyAbstractClass withSpy = new SpyAbstractClass(); 134 MockitoAnnotations.initMocks(withSpy); 135 assertEquals(Arrays.asList("a"), withSpy.asSingletonList("a")); 136 } 137 138 @Test should_spy_inner_class()139 public void should_spy_inner_class() throws Exception { 140 141 class WithMockAndSpy { 142 @Spy 143 private InnerStrength strength; 144 @Mock 145 private List<String> list; 146 147 abstract class InnerStrength { 148 private final String name; 149 150 InnerStrength() { 151 // Make sure that @Mock fields are always injected before @Spy fields. 152 assertNotNull(list); 153 // Make sure constructor is indeed called. 154 this.name = "inner"; 155 } 156 157 abstract String strength(); 158 159 String fullStrength() { 160 return name + " " + strength(); 161 } 162 } 163 } 164 WithMockAndSpy outer = new WithMockAndSpy(); 165 MockitoAnnotations.initMocks(outer); 166 when(outer.strength.strength()).thenReturn("strength"); 167 assertEquals("inner strength", outer.strength.fullStrength()); 168 } 169 170 @Test(expected = IndexOutOfBoundsException.class) should_reset_spy()171 public void should_reset_spy() throws Exception { 172 spiedList.get(10); // see shouldInitSpy 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 } 181 class WithSpy { 182 @Spy 183 private Outer.Inner inner; 184 } 185 try { 186 MockitoAnnotations.initMocks(new WithSpy()); 187 fail(); 188 } catch (MockitoException e) { 189 assertThat(e).hasMessageContaining("@Spy annotation can only initialize inner classes"); 190 } 191 } 192 193 @Test should_report_private_inner_not_supported()194 public void should_report_private_inner_not_supported() throws Exception { 195 try { 196 MockitoAnnotations.initMocks(new WithInnerPrivate()); 197 fail(); 198 } catch (MockitoException e) { 199 // Currently fails at instantiation time, because the mock subclass don't have the 200 // 1-arg constructor expected for the outerclass. 201 // org.mockito.internal.creation.instance.ConstructorInstantiator.withParams() 202 assertThat(e).hasMessageContaining("Unable to initialize @Spy annotated field 'spy_field'") 203 .hasMessageContaining(WithInnerPrivate.InnerPrivate.class.getSimpleName()); 204 } 205 } 206 207 @Test should_report_private_abstract_inner_not_supported()208 public void should_report_private_abstract_inner_not_supported() throws Exception { 209 try { 210 MockitoAnnotations.initMocks(new WithInnerPrivateAbstract()); 211 fail(); 212 } catch (MockitoException e) { 213 assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes") 214 .hasMessageContaining(WithInnerPrivateAbstract.class.getSimpleName()) 215 .hasMessageContaining(WithInnerPrivateAbstract.InnerPrivateAbstract.class.getSimpleName()) 216 .hasMessageContaining("You should augment the visibility of this inner class"); 217 } 218 } 219 220 @Test should_report_private_static_abstract_inner_not_supported()221 public void should_report_private_static_abstract_inner_not_supported() throws Exception { 222 try { 223 MockitoAnnotations.initMocks(new WithInnerPrivateStaticAbstract()); 224 fail(); 225 } catch (MockitoException e) { 226 assertThat(e).hasMessageContaining("@Spy annotation can't initialize private abstract inner classes") 227 .hasMessageContaining(WithInnerPrivateStaticAbstract.class.getSimpleName()) 228 .hasMessageContaining(WithInnerPrivateStaticAbstract.InnerPrivateStaticAbstract.class.getSimpleName()) 229 .hasMessageContaining("You should augment the visibility of this inner class"); 230 } 231 } 232 233 static class WithInnerPrivateStaticAbstract { 234 @Spy 235 private InnerPrivateStaticAbstract spy_field; 236 237 private static abstract class InnerPrivateStaticAbstract { 238 } 239 } 240 static class WithInnerPrivateAbstract { 241 @Spy 242 private InnerPrivateAbstract spy_field; 243 some_method()244 public void some_method() { 245 new InnerPrivateConcrete(); 246 } 247 248 private abstract class InnerPrivateAbstract { 249 } 250 251 private class InnerPrivateConcrete extends InnerPrivateAbstract { 252 253 } 254 } 255 256 static class WithInnerPrivate { 257 @Spy 258 private InnerPrivate spy_field; 259 260 private class InnerPrivate { 261 } 262 263 private class InnerPrivateSub extends InnerPrivate {} 264 } 265 266 static class InnerStaticClassWithoutDefinedConstructor { 267 } 268 269 static class InnerStaticClassWithNoArgConstructor { InnerStaticClassWithNoArgConstructor()270 InnerStaticClassWithNoArgConstructor() { 271 } 272 InnerStaticClassWithNoArgConstructor(String f)273 InnerStaticClassWithNoArgConstructor(String f) { 274 } 275 } 276 277 static class NoValidConstructor { NoValidConstructor(String f)278 NoValidConstructor(String f) { 279 } 280 } 281 282 static class ThrowingConstructor { ThrowingConstructor()283 ThrowingConstructor() { 284 throw new RuntimeException("boo!"); 285 } 286 } 287 } 288