blob: 51c321d47180663be5a27de35e90c795174c1871 [file] [log] [blame]
/*
* Copyright (c) 2017 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.creation.bytebuddy;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.StubMethod;
import org.junit.Test;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.creation.MockSettingsImpl;
import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.internal.handler.MockHandlerImpl;
import org.mockito.internal.stubbing.answers.Returns;
import org.mockito.internal.util.collections.Sets;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.SerializableMode;
import org.mockito.plugins.MockMaker;
import java.util.*;
import java.util.regex.Pattern;
import static net.bytebuddy.ClassFileVersion.JAVA_V8;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.Assume.assumeTrue;
public class InlineByteBuddyMockMakerTest extends AbstractByteBuddyMockMakerTest<InlineByteBuddyMockMaker> {
public InlineByteBuddyMockMakerTest() {
super(new InlineByteBuddyMockMaker());
}
@Override
protected Class<?> mockTypeOf(Class<?> type) {
return type;
}
@Test
public void should_create_mock_from_final_class() throws Exception {
MockCreationSettings<FinalClass> settings = settingsFor(FinalClass.class);
FinalClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings));
assertThat(proxy.foo()).isEqualTo("bar");
}
@Test
public void should_create_mock_from_final_class_in_the_JDK() throws Exception {
MockCreationSettings<Pattern> settings = settingsFor(Pattern.class);
Pattern proxy = mockMaker.createMock(settings, new MockHandlerImpl<Pattern>(settings));
assertThat(proxy.pattern()).isEqualTo("bar");
}
@Test
public void should_create_mock_from_abstract_class_with_final_method() throws Exception {
MockCreationSettings<FinalMethodAbstractType> settings = settingsFor(FinalMethodAbstractType.class);
FinalMethodAbstractType proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalMethodAbstractType>(settings));
assertThat(proxy.foo()).isEqualTo("bar");
assertThat(proxy.bar()).isEqualTo("bar");
}
@Test
public void should_create_mock_from_final_class_with_interface_methods() throws Exception {
MockCreationSettings<FinalMethod> settings = settingsFor(FinalMethod.class, SampleInterface.class);
FinalMethod proxy = mockMaker.createMock(settings, new MockHandlerImpl<FinalMethod>(settings));
assertThat(proxy.foo()).isEqualTo("bar");
assertThat(((SampleInterface) proxy).bar()).isEqualTo("bar");
}
@Test
public void should_detect_non_overridden_generic_method_of_supertype() throws Exception {
MockCreationSettings<GenericSubClass> settings = settingsFor(GenericSubClass.class);
GenericSubClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<GenericSubClass>(settings));
assertThat(proxy.value()).isEqualTo("bar");
}
@Test
public void should_create_mock_from_hashmap() throws Exception {
MockCreationSettings<HashMap> settings = settingsFor(HashMap.class);
HashMap proxy = mockMaker.createMock(settings, new MockHandlerImpl<HashMap>(settings));
assertThat(proxy.get(null)).isEqualTo("bar");
}
@Test
@SuppressWarnings("unchecked")
public void should_throw_exception_redefining_unmodifiable_class() {
MockCreationSettings settings = settingsFor(int.class);
try {
mockMaker.createMock(settings, new MockHandlerImpl(settings));
fail("Expected a MockitoException");
} catch (MockitoException e) {
e.printStackTrace();
assertThat(e).hasMessageContaining("Could not modify all classes");
}
}
@Test
@SuppressWarnings("unchecked")
public void should_throw_exception_redefining_array() {
int[] array = new int[5];
MockCreationSettings<? extends int[]> settings = settingsFor(array.getClass());
try {
mockMaker.createMock(settings, new MockHandlerImpl(settings));
fail("Expected a MockitoException");
} catch (MockitoException e) {
assertThat(e).hasMessageContaining("Arrays cannot be mocked");
}
}
@Test
public void should_create_mock_from_enum() throws Exception {
MockCreationSettings<EnumClass> settings = settingsFor(EnumClass.class);
EnumClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<EnumClass>(settings));
assertThat(proxy.foo()).isEqualTo("bar");
}
@Test
public void should_fail_at_creating_a_mock_of_a_final_class_with_explicit_serialization() throws Exception {
MockCreationSettings<FinalClass> settings = new CreationSettings<FinalClass>()
.setTypeToMock(FinalClass.class)
.setSerializableMode(SerializableMode.BASIC);
try {
mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings));
fail("Expected a MockitoException");
} catch (MockitoException e) {
assertThat(e)
.hasMessageContaining("Unsupported settings")
.hasMessageContaining("serialization")
.hasMessageContaining("extra interfaces");
}
}
@Test
public void should_fail_at_creating_a_mock_of_a_final_class_with_extra_interfaces() throws Exception {
MockCreationSettings<FinalClass> settings = new CreationSettings<FinalClass>()
.setTypeToMock(FinalClass.class)
.setExtraInterfaces(Sets.<Class<?>>newSet(List.class));
try {
mockMaker.createMock(settings, new MockHandlerImpl<FinalClass>(settings));
fail("Expected a MockitoException");
} catch (MockitoException e) {
assertThat(e)
.hasMessageContaining("Unsupported settings")
.hasMessageContaining("serialization")
.hasMessageContaining("extra interfaces");
}
}
@Test
public void should_mock_interface() {
MockSettingsImpl<Set> mockSettings = new MockSettingsImpl<Set>();
mockSettings.setTypeToMock(Set.class);
mockSettings.defaultAnswer(new Returns(10));
Set<?> proxy = mockMaker.createMock(mockSettings, new MockHandlerImpl<Set>(mockSettings));
assertThat(proxy.size()).isEqualTo(10);
}
@Test
public void should_mock_interface_to_string() {
MockSettingsImpl<Set> mockSettings = new MockSettingsImpl<Set>();
mockSettings.setTypeToMock(Set.class);
mockSettings.defaultAnswer(new Returns("foo"));
Set<?> proxy = mockMaker.createMock(mockSettings, new MockHandlerImpl<Set>(mockSettings));
assertThat(proxy.toString()).isEqualTo("foo");
}
@Test
public void should_remove_recursive_self_call_from_stack_trace() throws Exception {
StackTraceElement[] stack = new StackTraceElement[]{
new StackTraceElement("foo", "", "", -1),
new StackTraceElement(SampleInterface.class.getName(), "", "", -1),
new StackTraceElement("qux", "", "", -1),
new StackTraceElement("bar", "", "", -1),
new StackTraceElement("baz", "", "", -1)
};
Throwable throwable = new Throwable();
throwable.setStackTrace(stack);
throwable = MockMethodAdvice.hideRecursiveCall(throwable, 2, SampleInterface.class);
assertThat(throwable.getStackTrace()).isEqualTo(new StackTraceElement[]{
new StackTraceElement("foo", "", "", -1),
new StackTraceElement("bar", "", "", -1),
new StackTraceElement("baz", "", "", -1)
});
}
@Test
public void should_handle_missing_or_inconsistent_stack_trace() throws Exception {
Throwable throwable = new Throwable();
throwable.setStackTrace(new StackTraceElement[0]);
assertThat(MockMethodAdvice.hideRecursiveCall(throwable, 0, SampleInterface.class)).isSameAs(throwable);
}
@Test
public void should_provide_reason_for_wrapper_class() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Integer.class);
assertThat(mockable.nonMockableReason()).isEqualTo("Cannot mock wrapper types, String.class or Class.class");
}
@Test
public void should_provide_reason_for_vm_unsupported() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(int[].class);
assertThat(mockable.nonMockableReason()).isEqualTo("VM does not not support modification of given type");
}
@Test
public void should_mock_method_of_package_private_class() throws Exception {
MockCreationSettings<NonPackagePrivateSubClass> settings = settingsFor(NonPackagePrivateSubClass.class);
NonPackagePrivateSubClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<NonPackagePrivateSubClass>(settings));
assertThat(proxy.value()).isEqualTo("bar");
}
@Test
public void is_type_mockable_excludes_String() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(String.class);
assertThat(mockable.mockable()).isFalse();
assertThat(mockable.nonMockableReason()).contains("Cannot mock wrapper types, String.class or Class.class");
}
@Test
public void is_type_mockable_excludes_Class() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Class.class);
assertThat(mockable.mockable()).isFalse();
assertThat(mockable.nonMockableReason()).contains("Cannot mock wrapper types, String.class or Class.class");
}
@Test
public void is_type_mockable_excludes_primitive_classes() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(int.class);
assertThat(mockable.mockable()).isFalse();
assertThat(mockable.nonMockableReason()).contains("primitive");
}
@Test
public void is_type_mockable_allows_anonymous() {
Observer anonymous = new Observer() {
@Override
public void update(Observable o, Object arg) {
}
};
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(anonymous.getClass());
assertThat(mockable.mockable()).isTrue();
assertThat(mockable.nonMockableReason()).contains("");
}
@Test
public void is_type_mockable_give_empty_reason_if_type_is_mockable() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(SomeClass.class);
assertThat(mockable.mockable()).isTrue();
assertThat(mockable.nonMockableReason()).isEqualTo("");
}
@Test
public void is_type_mockable_give_allow_final_mockable_from_JDK() {
MockMaker.TypeMockability mockable = mockMaker.isTypeMockable(Pattern.class);
assertThat(mockable.mockable()).isTrue();
assertThat(mockable.nonMockableReason()).isEqualTo("");
}
@Test
public void test_parameters_retention() throws Exception {
assumeTrue(ClassFileVersion.ofThisVm().isAtLeast(JAVA_V8));
Class<?> typeWithParameters = new ByteBuddy()
.subclass(Object.class)
.defineMethod("foo", void.class, Visibility.PUBLIC)
.withParameter(String.class, "bar")
.intercept(StubMethod.INSTANCE)
.make()
.load(null)
.getLoaded();
MockCreationSettings<?> settings = settingsFor(typeWithParameters);
@SuppressWarnings("unchecked")
Object proxy = mockMaker.createMock(settings, new MockHandlerImpl(settings));
assertThat(proxy.getClass()).isEqualTo(typeWithParameters);
assertThat(new TypeDescription.ForLoadedType(typeWithParameters).getDeclaredMethods().filter(named("foo"))
.getOnly().getParameters().getOnly().getName()).isEqualTo("bar");
}
private static <T> MockCreationSettings<T> settingsFor(Class<T> type, Class<?>... extraInterfaces) {
MockSettingsImpl<T> mockSettings = new MockSettingsImpl<T>();
mockSettings.setTypeToMock(type);
mockSettings.defaultAnswer(new Returns("bar"));
if (extraInterfaces.length > 0) mockSettings.extraInterfaces(extraInterfaces);
return mockSettings;
}
@Test
public void testMockDispatcherIsRelocated() throws Exception {
assertThat(InlineByteBuddyMockMaker.class.getClassLoader().getResource("org/mockito/internal/creation/bytebuddy/MockMethodDispatcher.raw")).isNotNull();
}
private static final class FinalClass {
public String foo() {
return "foo";
}
}
private enum EnumClass {
INSTANCE;
public String foo() {
return "foo";
}
}
private abstract static class FinalMethodAbstractType {
public final String foo() {
return "foo";
}
public abstract String bar();
}
private static class FinalMethod {
public final String foo() {
return "foo";
}
}
private interface SampleInterface {
String bar();
}
/*package-private*/ abstract class PackagePrivateSuperClass {
public abstract String indirect();
public String value() {
return indirect() + "qux";
}
}
public class NonPackagePrivateSubClass extends PackagePrivateSuperClass {
@Override
public String indirect() {
return "foo";
}
}
public static class GenericClass<T> {
public T value() {
return null;
}
}
public static class GenericSubClass extends GenericClass<String> {
}
}