All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.mockito.internal.creation.bytebuddy;

import com.fitbur.mockito.exceptions.base.MockitoException;
import com.fitbur.mockito.internal.InternalMockHandler;
import com.fitbur.mockito.internal.configuration.plugins.Plugins;
import com.fitbur.mockito.internal.creation.bytebuddy.MockMethodInterceptor.MockAccess;
import com.fitbur.mockito.internal.creation.instance.Instantiator;
import com.fitbur.mockito.invocation.MockHandler;
import com.fitbur.mockito.mock.MockCreationSettings;
import com.fitbur.mockito.mock.SerializableMode;
import com.fitbur.mockito.plugins.MockMaker;

import java.lang.reflect.Modifier;

import static com.fitbur.mockito.internal.util.StringJoiner.join;

public class ByteBuddyMockMaker implements MockMaker {

    private final CachingMockBytecodeGenerator cachingMockBytecodeGenerator;

    public ByteBuddyMockMaker() {
        cachingMockBytecodeGenerator = new CachingMockBytecodeGenerator();
    }

    @Override
    public  T createMock(MockCreationSettings settings, MockHandler handler) {
        Class mockedProxyType = createProxyClass(mockWithFeaturesFrom(settings));

        Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
        T mockInstance = null;
        try {
            mockInstance = instantiator.newInstance(mockedProxyType);
            MockAccess mockAccess = (MockAccess) mockInstance;
            mockAccess.setMockitoInterceptor(new MockMethodInterceptor(asInternalMockHandler(handler), settings));

            return ensureMockIsAssignableToMockedType(settings, mockInstance);
        } catch (ClassCastException cce) {
            throw new MockitoException(join(
                    "ClassCastException occurred while creating the mockito mock :",
                    "  class to mock : " + describeClass(mockedProxyType),
                    "  created class : " + describeClass(settings.getTypeToMock()),
                    "  proxy instance class : " + describeClass(mockInstance),
                    "  instance creation by : " + instantiator.getClass().getSimpleName(),
                    "",
                    "You might experience classloading issues, please ask the mockito mailing-list.",
                    ""
            ),cce);
        } catch (com.fitbur.mockito.internal.creation.instance.InstantiationException e) {
            throw new MockitoException("Unable to create mock instance of type '" + mockedProxyType.getSuperclass().getSimpleName() + "'", e);
        }
    }

     Class createProxyClass(MockFeatures mockFeatures) {
        return cachingMockBytecodeGenerator.get(mockFeatures);
    }


    private  MockFeatures mockWithFeaturesFrom(MockCreationSettings settings) {
        return MockFeatures.withMockFeatures(
                settings.getTypeToMock(),
                settings.getExtraInterfaces(),
                settings.getSerializableMode() == SerializableMode.ACROSS_CLASSLOADERS
        );
    }

    private  T ensureMockIsAssignableToMockedType(MockCreationSettings settings, T mock) {
        // Force explicit cast to mocked type here, instead of
        // relying on the JVM to implicitly cast on the client call site.
        // This allows us to catch earlier the ClassCastException earlier
        Class typeToMock = settings.getTypeToMock();
        return typeToMock.cast(mock);
    }

    private static String describeClass(Class type) {
        return type == null ? "null" : "'" + type.getCanonicalName() + "', loaded by classloader : '" + type.getClassLoader() + "'";
    }

    private static String describeClass(Object instance) {
        return instance == null ? "null" : describeClass(instance.getClass());
    }

    @Override
    public MockHandler getHandler(Object mock) {
        if (!(mock instanceof MockAccess)) {
            return null;
        }
        return ((MockAccess) mock).getMockitoInterceptor().getMockHandler();
    }

    @Override
    public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) {
        ((MockAccess) mock).setMockitoInterceptor(
                new MockMethodInterceptor(asInternalMockHandler(newHandler), settings)
        );
    }

    @Override
    public TypeMockability isTypeMockable(final Class type) {
        return new TypeMockability() {
            @Override
            public boolean mockable() {
                return !type.isPrimitive() && !Modifier.isFinal(type.getModifiers());
            }

            @Override
            public String nonMockableReason() {
                //TODO SF does not seem to have test coverage. What is the expected value when type mockable
                if(type.isPrimitive()) {
                    return "primitive type";
                }
                if(Modifier.isFinal(type.getModifiers())) {
                    return "final or anonymous class";
                }
                return join("not handled type");
            }
        };
    }

    private static InternalMockHandler asInternalMockHandler(MockHandler handler) {
        if (!(handler instanceof InternalMockHandler)) {
            throw new MockitoException(join(
                    "At the moment you cannot provide own implementations of MockHandler.",
                    "Please see the javadocs for the MockMaker interface.",
                    ""
            ));
        }
        return (InternalMockHandler) handler;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy