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

mockit.MockUp Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

The newest version!
/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit;

import static java.lang.reflect.Modifier.isAbstract;

import static mockit.internal.util.GeneratedClasses.isGeneratedImplementationClassName;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

import mockit.internal.classGeneration.ConcreteSubclass;
import mockit.internal.faking.CaptureOfFakedImplementations;
import mockit.internal.faking.FakeClassSetup;
import mockit.internal.faking.FakeClasses;
import mockit.internal.faking.FakedImplementationClass;
import mockit.internal.reflection.ConstructorReflection;
import mockit.internal.reflection.MockInvocationHandler;
import mockit.internal.startup.Startup;
import mockit.internal.state.TestRun;

/**
 * A base class used in the creation of a fake for an external type, which is usually a class from
 * some library or component used from the internal codebase of the system under test (SUT). Such fake classes
 * can be used as fake implementations for use in unit or integration tests. For example:
 *
 * 
{@code
 * public final class FakeSystem extends MockUp<System> {
 *    @Mock public static long nanoTime() { return 123L; }
 * }
 * }
* * One or more fake methods annotated {@linkplain Mock as such} must be defined in the concrete subclass. Each * @Mock method should have a matching method or constructor in the faked class. At runtime, the execution * of a faked method/constructor will get redirected to the corresponding fake method. *

* When the type to be faked is specified indirectly through a {@linkplain TypeVariable type variable}, then that type * is taken as a base type whose concrete implementation classes should also get faked. Example: * *

{@code
 * @Test
 * public <BC extends SomeBaseClass> void someTest() {
 *     new MockUp<BC>() {
 *        @Mock int someMethod(int i) { return i + 1; }
 *     };
 *
 *     int i = new AConcreteSubclass().someMethod(1);
 *     assertEquals(2, i);
 * }
 * }
* * @param * specifies the type to be faked; if a type variable is used, then all implementation classes extending or * implementing that base type are also faked; if the type argument itself is a parameterized type, then only * its raw type is considered * * @see #MockUp() * @see #MockUp(Class) * @see #MockUp(Object) * @see #getMockInstance() * @see #onTearDown() * @see #targetType * @see Tutorial */ public abstract class MockUp { static { Startup.verifyInitialization(); } /** * Holds the class or generic type targeted by this fake instance. */ protected final Type targetType; @Nullable private final Class mockedClass; @Nullable private T mockInstance; @Nullable private T invokedInstance; /** * Applies the {@linkplain Mock fake methods} defined in the concrete subclass to the class or interface specified * through the type parameter. * * @see #MockUp(Class) * @see #MockUp(Object) */ protected MockUp() { MockUp previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); if (previousMockUp != null) { targetType = previousMockUp.targetType; mockedClass = previousMockUp.mockedClass; return; } targetType = getTypeToFake(); Class classToMock = null; if (targetType instanceof Class) { // noinspection unchecked classToMock = (Class) targetType; } else if (targetType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) targetType; // noinspection unchecked classToMock = (Class) parameterizedType.getRawType(); } if (classToMock != null) { mockedClass = redefineClassOrImplementInterface(classToMock); } else { Type[] typesToMock = ((TypeVariable) targetType).getBounds(); mockedClass = typesToMock.length > 1 ? new FakedImplementationClass(this).createImplementation(typesToMock) : new CaptureOfFakedImplementations(this, typesToMock[0]).apply(); } } @Nullable private MockUp findPreviouslyFakedClassIfMockUpAlreadyApplied() { FakeClasses mockClasses = TestRun.getFakeClasses(); FakeClasses.MockUpInstances mockUpInstances = mockClasses.findPreviouslyAppliedMockUps(this); if (mockUpInstances != null && mockUpInstances.hasMockUpsForSingleInstances()) { return mockUpInstances.initialMockUp; } return null; } /** * Gets the type to fake. * * @return the type to fake */ @NonNull private Type getTypeToFake() { Class currentClass = getClass(); do { Type superclass = currentClass.getGenericSuperclass(); if (superclass instanceof ParameterizedType) { return ((ParameterizedType) superclass).getActualTypeArguments()[0]; } if (superclass == MockUp.class) { throw new IllegalArgumentException("No target type"); } currentClass = (Class) superclass; } while (true); } @NonNull private Class redefineClassOrImplementInterface(@NonNull Class classToMock) { if (classToMock.isInterface()) { return createInstanceOfMockedImplementationClass(classToMock, targetType); } Class realClass = classToMock; if (isAbstract(classToMock.getModifiers())) { classToMock = new ConcreteSubclass(classToMock).generateClass(); } redefineMethods(realClass, classToMock, targetType); return classToMock; } @NonNull private Class createInstanceOfMockedImplementationClass(@NonNull Class classToMock, @Nullable Type typeToMock) { return new FakedImplementationClass(this).createImplementation(classToMock, typeToMock); } private void redefineMethods(@NonNull Class realClass, @NonNull Class classToMock, @Nullable Type genericMockedType) { new FakeClassSetup(realClass, classToMock, genericMockedType, this).redefineMethods(); } /** * Applies the {@linkplain Mock mock methods} defined in the mock-up subclass to the given class/interface. *

* In most cases, the constructor with no parameters can be used. This variation should be used only when the type * to be faked is not accessible or known from the test code. * * @param targetClass * the target class * * @see #MockUp() * @see #MockUp(Object) */ protected MockUp(Class targetClass) { targetType = targetClass; MockUp previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); if (previousMockUp != null) { mockedClass = previousMockUp.mockedClass; return; } if (targetClass.isInterface()) { // noinspection unchecked mockedClass = createInstanceOfMockedImplementationClass((Class) targetClass, targetClass); } else { mockedClass = targetClass; // noinspection unchecked Class realClass = (Class) targetClass; redefineMethods(realClass, realClass, null); mockInstance = null; } } /** * Applies the {@linkplain Mock mock methods} defined in the mock-up subclass to the type specified through the type * parameter, but only affecting the given instance. *

* In most cases, the constructor with no parameters should be adequate. This variation can be used when mock data * or behavior is desired only for a particular instance, with other instances remaining unaffected; or when * multiple mock-up objects carrying different states are desired, with one mock-up instance per real instance. *

* If {@link #getMockInstance()} later gets called on this mock-up instance, it will return the instance that was * given here. * * @param targetInstance * a real instance of the type to be faked, meant to be the only one of that type that should be affected * by this mock-up instance * * @see #MockUp() * @see #MockUp(Class) */ protected MockUp(T targetInstance) { MockUp previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); if (previousMockUp != null) { targetType = previousMockUp.targetType; mockedClass = previousMockUp.mockedClass; setMockInstance(targetInstance); return; } @SuppressWarnings("unchecked") Class classToMock = (Class) targetInstance.getClass(); targetType = classToMock; mockedClass = classToMock; redefineMethods(classToMock, classToMock, classToMock); setMockInstance(targetInstance); } private void setMockInstance(@NonNull T mockInstance) { TestRun.getFakeClasses().addFake(this, mockInstance); this.mockInstance = mockInstance; } /** * Returns the mock instance exclusively associated with this mock-up instance. If the mocked type was an interface, * then said instance is the one that was automatically created when the mock-up was applied. If it was a class, and * no such instance is currently associated with this (stateful) mock-up object, then a new uninitialized * instance of the faked class is created and returned, becoming associated with the mock-up. If a regular * initialized instance was desired, then the {@link #MockUp(Object)} constructor should have been used * instead. *

* In any case, for a given mock-up instance this method will always return the same mock instance. * * @return the mock instance * * @see Tutorial */ public final T getMockInstance() { if (invokedInstance == Void.class) { return null; } if (invokedInstance != null) { return invokedInstance; } if (mockInstance == null && mockedClass != null) { @SuppressWarnings("unchecked") T newInstance = (T) createMockInstance(mockedClass); setMockInstance(newInstance); } return mockInstance; } @NonNull private Object createMockInstance(@NonNull Class mockedClass) { String mockedClassName = mockedClass.getName(); if (isGeneratedImplementationClassName(mockedClassName)) { return ConstructorReflection.newInstanceUsingPublicDefaultConstructor(mockedClass); } if (Proxy.isProxyClass(mockedClass)) { return MockInvocationHandler.newMockedInstance(mockedClass); } return ConstructorReflection.newUninitializedInstance(mockedClass); } /** * An empty method that can be overridden in a mock-up subclass that wants to be notified whenever the mock-up is * automatically torn down. Tear down happens when the mock-up goes out of scope: at the end of the test when * applied inside a test, at the end of the test class when applied before the test class, or at the end of the test * run when applied through the "mockups" system property. *

* By default, this method does nothing. */ protected void onTearDown() { } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy