InlineByteBuddyMockMakerTest.java revision 2637d96c202372854a7c71466ddcc6e90fc4fc53
1package org.mockito.internal.creation.bytebuddy; 2 3import net.bytebuddy.ByteBuddy; 4import net.bytebuddy.ClassFileVersion; 5import net.bytebuddy.description.modifier.Visibility; 6import net.bytebuddy.description.type.TypeDescription; 7import net.bytebuddy.implementation.StubMethod; 8import org.junit.Test; 9import org.mockito.exceptions.base.MockitoException; 10import org.mockito.internal.creation.MockSettingsImpl; 11import org.mockito.internal.creation.settings.CreationSettings; 12import org.mockito.internal.handler.MockHandlerImpl; 13import org.mockito.internal.stubbing.answers.Returns; 14import org.mockito.internal.util.collections.Sets; 15import org.mockito.mock.MockCreationSettings; 16import org.mockito.mock.SerializableMode; 17import org.mockito.plugins.MockMaker; 18 19import java.util.HashMap; 20import java.util.List; 21import java.util.Observable; 22import java.util.Observer; 23import java.util.regex.Pattern; 24 25import static net.bytebuddy.ClassFileVersion.JAVA_V8; 26import static net.bytebuddy.ClassFileVersion.JAVA_V9; 27import static net.bytebuddy.matcher.ElementMatchers.named; 28import static org.assertj.core.api.Assertions.assertThat; 29import static org.assertj.core.api.Assertions.fail; 30import static org.junit.Assume.assumeTrue; 31 32public class InlineByteBuddyMockMakerTest extends AbstractByteBuddyMockMakerTest<InlineByteBuddyMockMaker> { 33 34 public InlineByteBuddyMockMakerTest() { 35 super(new InlineByteBuddyMockMaker()); 36 } 37 38 @Override 39 protected Class<?> mockTypeOf(Class<?> type) { 40 return type; 41 } 42 43 @Test 44 public void should_create_mock_from_final_class() throws Exception { 45 MockCreationSettings<FinalClass> settings = settingsFor(FinalClass.class); 46 FinalClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings)); 47 assertThat(proxy.foo()).isEqualTo("bar"); 48 } 49 50 @Test 51 public void should_create_mock_from_final_class_in_the_JDK() throws Exception { 52 assumeTrue(ClassFileVersion.ofThisVm().isLessThan(JAVA_V9)); // Change when ByteBuddy has ASM6 - see #788 53 54 MockCreationSettings<Pattern> settings = settingsFor(Pattern.class); 55 Pattern proxy = mockMaker.createMock(settings, new MockHandlerImpl<Pattern>(settings)); 56 assertThat(proxy.pattern()).isEqualTo("bar"); 57 } 58 59 @Test 60 public void should_create_mock_from_abstract_class_with_final_method() throws Exception { 61 MockCreationSettings<FinalMethodAbstractType> settings = settingsFor(FinalMethodAbstractType.class); 62 FinalMethodAbstractType proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalMethodAbstractType>(settings)); 63 assertThat(proxy.foo()).isEqualTo("bar"); 64 assertThat(proxy.bar()).isEqualTo("bar"); 65 } 66 67 @Test 68 public void should_create_mock_from_final_class_with_interface_methods() throws Exception { 69 MockCreationSettings<FinalMethod> settings = settingsFor(FinalMethod.class, SampleInterface.class); 70 FinalMethod proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalMethod>(settings)); 71 assertThat(proxy.foo()).isEqualTo("bar"); 72 assertThat(((SampleInterface) proxy).bar()).isEqualTo("bar"); 73 } 74 75 @Test 76 public void should_create_mock_from_hashmap() throws Exception { 77 MockCreationSettings<HashMap> settings = settingsFor(HashMap.class); 78 HashMap proxy = mockMaker.createMock(settings, new MockHandlerImpl<HashMap>(settings)); 79 assertThat(proxy.get(null)).isEqualTo("bar"); 80 } 81 82 @Test 83 @SuppressWarnings("unchecked") 84 public void should_throw_exception_redefining_unmodifiable_class() { 85 MockCreationSettings settings = settingsFor(int.class); 86 try { 87 mockMaker.createMock(settings, new MockHandlerImpl(settings)); 88 fail("Expected a MockitoException"); 89 } catch (MockitoException e) { 90 e.printStackTrace(); 91 assertThat(e).hasMessageContaining("Could not modify all classes"); 92 } 93 } 94 95 @Test 96 @SuppressWarnings("unchecked") 97 public void should_throw_exception_redefining_array() { 98 int[] array = new int[5]; 99 MockCreationSettings<? extends int[]> settings = settingsFor(array.getClass()); 100 try { 101 mockMaker.createMock(settings, new MockHandlerImpl(settings)); 102 fail("Expected a MockitoException"); 103 } catch (MockitoException e) { 104 assertThat(e).hasMessageContaining("Arrays cannot be mocked"); 105 } 106 } 107 108 @Test 109 public void should_create_mock_from_enum() throws Exception { 110 MockCreationSettings<EnumClass> settings = settingsFor(EnumClass.class); 111 EnumClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<EnumClass>(settings)); 112 assertThat(proxy.foo()).isEqualTo("bar"); 113 } 114 115 @Test 116 public void should_fail_at_creating_a_mock_of_a_final_class_with_explicit_serialization() throws Exception { 117 MockCreationSettings<FinalClass> settings = new CreationSettings<FinalClass>() 118 .setTypeToMock(FinalClass.class) 119 .setSerializableMode(SerializableMode.BASIC); 120 121 try { 122 mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings)); 123 fail("Expected a MockitoException"); 124 } catch (MockitoException e) { 125 assertThat(e) 126 .hasMessageContaining("Unsupported settings") 127 .hasMessageContaining("serialization") 128 .hasMessageContaining("extra interfaces"); 129 } 130 } 131 132 @Test 133 public void should_fail_at_creating_a_mock_of_a_final_class_with_extra_interfaces() throws Exception { 134 MockCreationSettings<FinalClass> settings = new CreationSettings<FinalClass>() 135 .setTypeToMock(FinalClass.class) 136 .setExtraInterfaces(Sets.<Class<?>>newSet(List.class)); 137 138 try { 139 mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings)); 140 fail("Expected a MockitoException"); 141 } catch (MockitoException e) { 142 assertThat(e) 143 .hasMessageContaining("Unsupported settings") 144 .hasMessageContaining("serialization") 145 .hasMessageContaining("extra interfaces"); 146 } 147 } 148 149 @Test 150 public void should_remove_recursive_self_call_from_stack_trace() throws Exception { 151 StackTraceElement[] stack = new StackTraceElement[]{ 152 new StackTraceElement("foo", "", "", -1), 153 new StackTraceElement(SampleInterface.class.getName(), "", "", -1), 154 new StackTraceElement("qux", "", "", -1), 155 new StackTraceElement("bar", "", "", -1), 156 new StackTraceElement("baz", "", "", -1) 157 }; 158 159 Throwable throwable = new Throwable(); 160 throwable.setStackTrace(stack); 161 throwable = InlineByteBuddyMockMaker.hideRecursiveCall(throwable, 2, SampleInterface.class); 162 163 assertThat(throwable.getStackTrace()).isEqualTo(new StackTraceElement[]{ 164 new StackTraceElement("foo", "", "", -1), 165 new StackTraceElement("bar", "", "", -1), 166 new StackTraceElement("baz", "", "", -1) 167 }); 168 } 169 170 @Test 171 public void should_handle_missing_or_inconsistent_stack_trace() throws Exception { 172 Throwable throwable = new Throwable(); 173 throwable.setStackTrace(new StackTraceElement[0]); 174 assertThat(InlineByteBuddyMockMaker.hideRecursiveCall(throwable, 0, SampleInterface.class)).isSameAs(throwable); 175 } 176 177 @Test 178 public void should_provide_reason_for_wrapper_class() { 179 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Integer.class); 180 assertThat(mockable.nonMockableReason()).isEqualTo("Cannot mock wrapper types, String.class or Class.class"); 181 } 182 183 @Test 184 public void should_provide_reason_for_vm_unsupported() { 185 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(int[].class); 186 assertThat(mockable.nonMockableReason()).isEqualTo("VM does not not support modification of given type"); 187 } 188 189 @Test 190 public void is_type_mockable_excludes_String() { 191 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(String.class); 192 assertThat(mockable.mockable()).isFalse(); 193 assertThat(mockable.nonMockableReason()).contains("Cannot mock wrapper types, String.class or Class.class"); 194 } 195 196 @Test 197 public void is_type_mockable_excludes_Class() { 198 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Class.class); 199 assertThat(mockable.mockable()).isFalse(); 200 assertThat(mockable.nonMockableReason()).contains("Cannot mock wrapper types, String.class or Class.class"); 201 } 202 203 @Test 204 public void is_type_mockable_excludes_primitive_classes() { 205 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(int.class); 206 assertThat(mockable.mockable()).isFalse(); 207 assertThat(mockable.nonMockableReason()).contains("primitive"); 208 } 209 210 @Test 211 public void is_type_mockable_allows_anonymous() { 212 Observer anonymous = new Observer() { 213 @Override 214 public void update(Observable o, Object arg) { 215 } 216 }; 217 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(anonymous.getClass()); 218 assertThat(mockable.mockable()).isTrue(); 219 assertThat(mockable.nonMockableReason()).contains(""); 220 } 221 222 @Test 223 public void is_type_mockable_give_empty_reason_if_type_is_mockable() { 224 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(SomeClass.class); 225 assertThat(mockable.mockable()).isTrue(); 226 assertThat(mockable.nonMockableReason()).isEqualTo(""); 227 } 228 229 @Test 230 public void is_type_mockable_give_allow_final_mockable_from_JDK() { 231 MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Pattern.class); 232 assertThat(mockable.mockable()).isTrue(); 233 assertThat(mockable.nonMockableReason()).isEqualTo(""); 234 } 235 236 @Test 237 public void test_parameters_retention() throws Exception { 238 assumeTrue(ClassFileVersion.ofThisVm().isAtLeast(JAVA_V8)); 239 assumeTrue(ClassFileVersion.ofThisVm().isLessThan(JAVA_V9)); // Change when ByteBuddy has ASM6 - see #788 240 241 Class<?> typeWithParameters = new ByteBuddy() 242 .subclass(Object.class) 243 .defineMethod("foo", void.class, Visibility.PUBLIC) 244 .withParameter(String.class, "bar") 245 .intercept(StubMethod.INSTANCE) 246 .make() 247 .load(null) 248 .getLoaded(); 249 250 MockCreationSettings<?> settings = settingsFor(typeWithParameters); 251 @SuppressWarnings("unchecked") 252 Object proxy = mockMaker.createMock(settings, new MockHandlerImpl(settings)); 253 254 assertThat(proxy.getClass()).isEqualTo(typeWithParameters); 255 assertThat(new TypeDescription.ForLoadedType(typeWithParameters).getDeclaredMethods().filter(named("foo")) 256 .getOnly().getParameters().getOnly().getName()).isEqualTo("bar"); 257 } 258 259 private static <T> MockCreationSettings<T> settingsFor(Class<T> type, Class<?>... extraInterfaces) { 260 MockSettingsImpl<T> mockSettings = new MockSettingsImpl<T>(); 261 mockSettings.setTypeToMock(type); 262 mockSettings.defaultAnswer(new Returns("bar")); 263 if (extraInterfaces.length > 0) mockSettings.extraInterfaces(extraInterfaces); 264 return mockSettings; 265 } 266 267 @Test 268 public void testMockDispatcherIsRelocated() throws Exception { 269 assertThat(InlineByteBuddyMockMaker.class.getClassLoader().getResource("org/mockito/internal/creation/bytebuddy/MockMethodDispatcher.raw")).isNotNull(); 270 } 271 272 private static final class FinalClass { 273 274 public String foo() { 275 return "foo"; 276 } 277 } 278 279 private enum EnumClass { 280 281 INSTANCE; 282 283 public String foo() { 284 return "foo"; 285 } 286 } 287 288 private abstract static class FinalMethodAbstractType { 289 290 public final String foo() { 291 return "foo"; 292 } 293 294 public abstract String bar(); 295 } 296 297 private static class FinalMethod { 298 299 public final String foo() { 300 return "foo"; 301 } 302 } 303 304 private static class NonFinalMethod { 305 306 public String foo() { 307 return "foo"; 308 } 309 } 310 311 private interface SampleInterface { 312 313 String bar(); 314 } 315} 316