1 /* 2 * Copyright (c) 2017 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockitousage.constructor; 6 7 import java.util.List; 8 import org.junit.Test; 9 import org.mockito.exceptions.base.MockitoException; 10 import org.mockito.mock.SerializableMode; 11 import org.mockitousage.IMethods; 12 import org.mockitoutil.TestBase; 13 14 import static org.junit.Assert.assertEquals; 15 import static org.junit.Assert.assertNull; 16 import static org.junit.Assert.fail; 17 import static org.assertj.core.api.Assertions.assertThat; 18 import static org.mockito.Mockito.CALLS_REAL_METHODS; 19 import static org.mockito.Mockito.mock; 20 import static org.mockito.Mockito.spy; 21 import static org.mockito.Mockito.when; 22 import static org.mockito.Mockito.withSettings; 23 24 public class CreatingMocksWithConstructorTest extends TestBase { 25 26 static abstract class AbstractMessage { 27 private final String message; 28 AbstractMessage() { 29 this.message = "hey!"; 30 } 31 AbstractMessage(String message) { 32 this.message = message; 33 } 34 AbstractMessage(int i) { 35 this.message = String.valueOf(i); 36 } 37 String getMessage() { 38 return message; 39 } 40 } 41 42 static class Message extends AbstractMessage {} 43 class InnerClass extends AbstractMessage {} 44 45 @Test 46 public void can_create_mock_with_constructor() { 47 Message mock = mock(Message.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)); 48 //the message is a part of state of the mocked type that gets initialized in constructor 49 assertEquals("hey!", mock.getMessage()); 50 } 51 52 @Test 53 public void can_mock_abstract_classes() { 54 AbstractMessage mock = mock(AbstractMessage.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)); 55 assertEquals("hey!", mock.getMessage()); 56 } 57 58 @Test 59 public void can_spy_abstract_classes() { 60 AbstractMessage mock = spy(AbstractMessage.class); 61 assertEquals("hey!", mock.getMessage()); 62 } 63 64 @Test 65 public void can_spy_abstract_classes_with_constructor_args() { 66 AbstractMessage mock = mock(AbstractMessage.class, withSettings().useConstructor("hello!").defaultAnswer(CALLS_REAL_METHODS)); 67 assertEquals("hello!", mock.getMessage()); 68 } 69 70 @Test 71 public void can_spy_abstract_classes_with_constructor_primitive_args() { 72 AbstractMessage mock = mock(AbstractMessage.class, withSettings().useConstructor(7).defaultAnswer(CALLS_REAL_METHODS)); 73 assertEquals("7", mock.getMessage()); 74 } 75 76 @Test 77 public void can_spy_abstract_classes_with_constructor_array_of_nulls() { 78 AbstractMessage mock = mock(AbstractMessage.class, withSettings().useConstructor(new Object[]{null}).defaultAnswer(CALLS_REAL_METHODS)); 79 assertNull(mock.getMessage()); 80 } 81 82 @Test 83 public void can_spy_abstract_classes_with_casted_null() { 84 AbstractMessage mock = mock(AbstractMessage.class, withSettings().useConstructor((String) null).defaultAnswer(CALLS_REAL_METHODS)); 85 assertNull(mock.getMessage()); 86 } 87 88 @Test 89 public void can_spy_abstract_classes_with_null_varargs() { 90 try { 91 mock(AbstractMessage.class, withSettings().useConstructor(null).defaultAnswer(CALLS_REAL_METHODS)); 92 fail(); 93 } catch (IllegalArgumentException e) { 94 assertThat(e).hasMessageContaining("constructorArgs should not be null. " + 95 "If you need to pass null, please cast it to the right type, e.g.: useConstructor((String) null)"); 96 } 97 } 98 99 @Test 100 public void can_mock_inner_classes() { 101 InnerClass mock = mock(InnerClass.class, withSettings().useConstructor().outerInstance(this).defaultAnswer(CALLS_REAL_METHODS)); 102 assertEquals("hey!", mock.getMessage()); 103 } 104 105 public static class ThrowingConstructorClass{ 106 public ThrowingConstructorClass() { 107 throw new RuntimeException(); 108 } 109 } 110 111 @Test 112 public void explains_constructor_exceptions() { 113 try { 114 mock(ThrowingConstructorClass.class, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)); 115 fail(); 116 } catch (MockitoException e) { 117 assertThat(e).hasRootCauseInstanceOf(RuntimeException.class); 118 assertThat(e.getCause()).hasMessageContaining("Please ensure the target class has a 0-arg constructor and executes cleanly."); 119 } 120 } 121 122 static class HasConstructor { 123 HasConstructor(String x) {} 124 } 125 126 @Test 127 public void exception_message_when_constructor_not_found() { 128 try { 129 //when 130 spy(HasConstructor.class); 131 //then 132 fail(); 133 } catch (MockitoException e) { 134 assertThat(e).hasMessage("Unable to create mock instance of type 'HasConstructor'"); 135 assertThat(e.getCause()).hasMessageContaining("Please ensure that the target class has a 0-arg constructor."); 136 } 137 } 138 139 static class Base {} 140 static class ExtendsBase extends Base {} 141 static class ExtendsExtendsBase extends ExtendsBase {} 142 143 static class UsesBase { 144 public UsesBase(Base b) { 145 constructorUsed = "Base"; 146 } 147 public UsesBase(ExtendsBase b) { 148 constructorUsed = "ExtendsBase"; 149 } 150 151 private String constructorUsed = null; 152 String getConstructorUsed() { 153 return constructorUsed; 154 } 155 } 156 157 @Test 158 public void can_mock_unambigous_constructor_with_inheritance_base_class_exact_match() { 159 UsesBase u = mock(UsesBase.class, withSettings().useConstructor(new Base()).defaultAnswer(CALLS_REAL_METHODS)); 160 assertEquals("Base", u.getConstructorUsed()); 161 } 162 163 @Test 164 public void can_mock_unambigous_constructor_with_inheritance_extending_class_exact_match() { 165 UsesBase u = mock(UsesBase.class, withSettings().useConstructor(new ExtendsBase()).defaultAnswer(CALLS_REAL_METHODS)); 166 assertEquals("ExtendsBase", u.getConstructorUsed()); 167 } 168 169 @Test 170 public void can_mock_unambigous_constructor_with_inheritance_non_exact_match() { 171 UsesBase u = mock(UsesBase.class, withSettings().useConstructor(new ExtendsExtendsBase()).defaultAnswer(CALLS_REAL_METHODS)); 172 assertEquals("ExtendsBase", u.getConstructorUsed()); 173 } 174 175 static class UsesTwoBases { 176 public UsesTwoBases(Base b1, Base b2) { 177 constructorUsed = "Base,Base"; 178 } 179 public UsesTwoBases(ExtendsBase b1, Base b2) { 180 constructorUsed = "ExtendsBase,Base"; 181 } 182 public UsesTwoBases(Base b1, ExtendsBase b2) { 183 constructorUsed = "Base,ExtendsBase"; 184 } 185 186 private String constructorUsed = null; 187 String getConstructorUsed() { 188 return constructorUsed; 189 } 190 } 191 192 @Test 193 public void can_mock_unambigous_constructor_with_inheritance_multiple_base_class_exact_match() { 194 UsesTwoBases u = 195 mock(UsesTwoBases.class, withSettings().useConstructor(new Base(), new Base()).defaultAnswer(CALLS_REAL_METHODS)); 196 assertEquals("Base,Base", u.getConstructorUsed()); 197 } 198 199 @Test 200 public void can_mock_unambigous_constructor_with_inheritance_first_extending_class_exact_match() { 201 UsesTwoBases u = 202 mock(UsesTwoBases.class, withSettings().useConstructor(new ExtendsBase(), new Base()).defaultAnswer(CALLS_REAL_METHODS)); 203 assertEquals("ExtendsBase,Base", u.getConstructorUsed()); 204 } 205 206 @Test 207 public void can_mock_unambigous_constructor_with_inheritance_second_extending_class_exact_match() { 208 UsesTwoBases u = 209 mock(UsesTwoBases.class, withSettings().useConstructor(new Base(), new ExtendsBase()).defaultAnswer(CALLS_REAL_METHODS)); 210 assertEquals("Base,ExtendsBase", u.getConstructorUsed()); 211 } 212 213 @Test 214 public void fail_when_multiple_matching_constructors_with_inheritence() { 215 try { 216 //when 217 mock(UsesTwoBases.class, withSettings().useConstructor(new ExtendsBase(), new ExtendsBase())); 218 //then 219 fail(); 220 } catch (MockitoException e) { 221 //TODO the exception message includes Mockito internals like the name of the generated class name. 222 //I suspect that we could make this exception message nicer. 223 assertThat(e).hasMessage("Unable to create mock instance of type 'UsesTwoBases'"); 224 assertThat(e.getCause()) 225 .hasMessageContaining("Multiple constructors could be matched to arguments of types " 226 + "[org.mockitousage.constructor.CreatingMocksWithConstructorTest$ExtendsBase, " 227 + "org.mockitousage.constructor.CreatingMocksWithConstructorTest$ExtendsBase]") 228 .hasMessageContaining("If you believe that Mockito could do a better job deciding on which constructor to use, please let us know.\n" + 229 "Ticket 685 contains the discussion and a workaround for ambiguous constructors using inner class.\n" + 230 "See https://github.com/mockito/mockito/issues/685"); 231 } 232 } 233 234 @Test 235 public void mocking_inner_classes_with_wrong_outer_instance() { 236 try { 237 //when 238 mock(InnerClass.class, withSettings().useConstructor().outerInstance(123).defaultAnswer(CALLS_REAL_METHODS)); 239 //then 240 fail(); 241 } catch (MockitoException e) { 242 assertThat(e).hasMessage("Unable to create mock instance of type 'InnerClass'"); 243 //TODO it would be nice if all useful information was in the top level exception, instead of in the exception's cause 244 //also applies to other scenarios in this test 245 assertThat(e.getCause()).hasMessageContaining( 246 "Please ensure that the target class has a 0-arg constructor" 247 + " and provided outer instance is correct."); 248 } 249 } 250 251 @Test 252 public void mocking_interfaces_with_constructor() { 253 //at the moment this is allowed however we can be more strict if needed 254 //there is not much sense in creating a spy of an interface 255 mock(IMethods.class, withSettings().useConstructor()); 256 spy(IMethods.class); 257 } 258 259 @Test 260 public void prevents_across_jvm_serialization_with_constructor() { 261 try { 262 //when 263 mock(AbstractMessage.class, withSettings().useConstructor().serializable(SerializableMode.ACROSS_CLASSLOADERS)); 264 //then 265 fail(); 266 } catch (MockitoException e) { 267 assertEquals("Mocks instantiated with constructor cannot be combined with " + SerializableMode.ACROSS_CLASSLOADERS + " serialization mode.", e.getMessage()); 268 } 269 } 270 271 static abstract class AbstractThing { 272 abstract String name(); 273 String fullName() { 274 return "abstract " + name(); 275 } 276 } 277 278 @Test 279 public void abstract_method_returns_default() { 280 AbstractThing thing = spy(AbstractThing.class); 281 assertEquals("abstract null", thing.fullName()); 282 } 283 284 @Test 285 public void abstract_method_stubbed() { 286 AbstractThing thing = spy(AbstractThing.class); 287 when(thing.name()).thenReturn("me"); 288 assertEquals("abstract me", thing.fullName()); 289 } 290 291 @Test 292 public void interface_method_stubbed() { 293 List<?> list = spy(List.class); 294 when(list.size()).thenReturn(12); 295 assertEquals(12, list.size()); 296 } 297 298 @Test 299 public void calls_real_interface_method() { 300 List list = mock(List.class, withSettings().defaultAnswer(CALLS_REAL_METHODS)); 301 assertNull(list.get(1)); 302 } 303 304 @Test 305 public void handles_bridge_methods_correctly() { 306 SomeConcreteClass<Integer> testBug = spy(new SomeConcreteClass<Integer>()); 307 assertEquals("value", testBug.getValue(0)); 308 } 309 310 public abstract class SomeAbstractClass<T> { 311 312 protected abstract String getRealValue(T value); 313 314 public String getValue(T value) { 315 return getRealValue(value); 316 } 317 } 318 319 public class SomeConcreteClass<T extends Number> extends SomeAbstractClass<T> { 320 321 @Override 322 protected String getRealValue(T value) { 323 return "value"; 324 } 325 } 326 327 private static class AmbiguousWithPrimitive { 328 public AmbiguousWithPrimitive(String s, int i) { 329 data = s; 330 } 331 public AmbiguousWithPrimitive(Object o, int i) { 332 data = "just an object"; 333 } 334 335 private String data; 336 public String getData() { 337 return data; 338 } 339 } 340 341 @Test 342 public void can_spy_ambiguius_constructor_with_primitive() { 343 AmbiguousWithPrimitive mock = mock(AmbiguousWithPrimitive.class, withSettings().useConstructor("String", 7).defaultAnswer(CALLS_REAL_METHODS)); 344 assertEquals("String", mock.getData()); 345 } 346 } 347