Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.lang.reflect.*;
import java.util.*;
import javax.annotation.*;
import static java.lang.reflect.Modifier.*;
import mockit.internal.classGeneration.*;
import mockit.internal.faking.*;
import mockit.internal.reflection.*;
import mockit.internal.startup.*;
import mockit.internal.faking.FakeClasses.*;
import mockit.internal.state.*;
import static mockit.internal.util.GeneratedClasses.*;
/**
* 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:
*
* 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 {@code @Mock} method should have a matching method or constructor in the faked class/interface.
* At runtime, the execution of a faked method/constructor will get redirected to the corresponding fake method.
*
* When the faked type is an interface, an implementation class is generated where all methods are empty, with non-void
* methods returning a default value according to the return type: {@code 0} for {@code int}, {@code null} for a
* reference type, and so on.
* In this case, an instance of the generated implementation class should be obtained by calling
* {@link #getMockInstance()}.
*
* When the type to be faked is specified indirectly through a {@linkplain TypeVariable type variable}, there are two
* other possible outcomes:
*
*
If the type variable "extends" two or more interfaces, a fake proxy class that implements all
* interfaces is created, with the proxy instance made available through a call to {@link #getMockInstance()}.
* Example:
*
If the type variable extends a single type (either an interface or a non-final class), then
* that type is taken as a base type whose concrete implementation classes should also get faked.
* Example:
*
* @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 (class, interface, etc.) to be faked; multiple interfaces can be faked by defining a
* type variable in the test class or test method, and using it as the type argument;
* if a type variable is used and it extends a single type, 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 fakedClass;
@Nullable private Set> classesToRestore;
@Nullable private T fakeInstance;
@SuppressWarnings("unused") @Nullable private T invokedInstance; // set through Reflection elsewhere
/**
* 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 previousFake = findPreviouslyFakedClassIfFakeAlreadyApplied();
if (previousFake != null) {
targetType = previousFake.targetType;
fakedClass = previousFake.fakedClass;
return;
}
targetType = getTypeToFake();
Class classToFake = null;
if (targetType instanceof Class) {
//noinspection unchecked
classToFake = (Class) targetType;
}
else if (targetType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) targetType;
//noinspection unchecked
classToFake = (Class) parameterizedType.getRawType();
}
if (classToFake != null) {
fakedClass = redefineClassOrImplementInterface(classToFake);
}
else {
Type[] typesToFake = ((TypeVariable) targetType).getBounds();
fakedClass = typesToFake.length > 1 ?
new FakedImplementationClass(this).createImplementation(typesToFake) :
new CaptureOfFakedImplementations(this, typesToFake[0]).apply();
}
}
@Nullable
private MockUp findPreviouslyFakedClassIfFakeAlreadyApplied()
{
FakeClasses fakeClasses = TestRun.getFakeClasses();
MockUpInstances fakeInstances = fakeClasses.findPreviouslyAppliedFakes(this);
if (fakeInstances != null && fakeInstances.hasMockUpsForSingleInstances()) {
return fakeInstances.initialMockUp;
}
return null;
}
@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 classToFake)
{
if (classToFake.isInterface()) {
return createInstanceOfFakedImplementationClass(classToFake, targetType);
}
Class realClass = classToFake;
if (isAbstract(classToFake.getModifiers())) {
classToFake = new ConcreteSubclass(classToFake).generateClass();
}
classesToRestore = redefineMethods(realClass, classToFake, targetType);
return classToFake;
}
@Nonnull
private Class createInstanceOfFakedImplementationClass(@Nonnull Class classToFake, @Nullable Type typeToFake)
{
FakedImplementationClass fakedImplementationClass = new FakedImplementationClass(this);
return fakedImplementationClass.createImplementation(classToFake, typeToFake);
}
@Nonnull
private Set> redefineMethods(
@Nonnull Class realClass, @Nonnull Class classToFake, @Nullable Type genericFakedType)
{
FakeClassSetup fakeSetup = new FakeClassSetup(realClass, classToFake, genericFakedType, this);
return fakeSetup.redefineMethods();
}
/**
* Applies the {@linkplain Mock fake methods} defined in the fake class 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.
*
* @see #MockUp()
* @see #MockUp(Object)
*/
protected MockUp(@SuppressWarnings("NullableProblems") Class targetClass)
{
targetType = targetClass;
MockUp previousFake = findPreviouslyFakedClassIfFakeAlreadyApplied();
if (previousFake != null) {
fakedClass = previousFake.fakedClass;
return;
}
if (targetClass.isInterface()) {
//noinspection unchecked
fakedClass = createInstanceOfFakedImplementationClass((Class) targetClass, targetClass);
}
else {
fakedClass = targetClass;
//noinspection unchecked
Class realClass = (Class) targetClass;
classesToRestore = redefineMethods(realClass, realClass, null);
fakeInstance = null;
}
}
/**
* Applies the {@linkplain Mock fake methods} defined in the fake class 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 fake data or behavior is desired only for a particular instance, with other
* instances remaining unaffected; or when multiple fake objects carrying different states are desired, with one
* fake instance per real instance.
*
* If {@link #getMockInstance()} later gets called on this fake 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 fake instance
*
* @see #MockUp()
* @see #MockUp(Class)
* @deprecated This is a rarely used feature without demonstrable use cases; real instances should be used instead.
*/
@Deprecated
protected MockUp(T targetInstance)
{
MockUp previousFake = findPreviouslyFakedClassIfFakeAlreadyApplied();
if (previousFake != null) {
targetType = previousFake.targetType;
fakedClass = previousFake.fakedClass;
setFakeInstance(targetInstance);
return;
}
@SuppressWarnings("unchecked") Class classToFake = (Class) targetInstance.getClass();
targetType = classToFake;
fakedClass = classToFake;
classesToRestore = redefineMethods(classToFake, classToFake, classToFake);
setFakeInstance(targetInstance);
}
private void setFakeInstance(@Nonnull T fakeInstance)
{
TestRun.getFakeClasses().addFake(this, fakeInstance);
this.fakeInstance = fakeInstance;
}
/**
* Returns the mock instance exclusively associated with this fake instance.
* If the faked type was an interface, then said instance is the one that was automatically created when the fake was
* applied.
* If it was a class, and no such instance is currently associated with this (stateful) fake object, then a new
* uninitialized instance of the faked class is created and returned, becoming associated with the fake.
* If a regular initialized instance was desired, then the {@link #MockUp(Object)} constructor should have
* been used instead.
*
* In any case, for a given fake instance this method will always return the same fake instance.
*
* @see Tutorial
*/
public final T getMockInstance()
{
if (invokedInstance == Void.class) {
return null;
}
if (invokedInstance != null) {
return invokedInstance;
}
if (fakeInstance == null && fakedClass != null) {
@SuppressWarnings("unchecked") T newInstance = (T) createFakeInstance(fakedClass);
setFakeInstance(newInstance);
}
//noinspection ConstantConditions
return fakeInstance;
}
@Nonnull
private Object createFakeInstance(@Nonnull Class fakedClass)
{
String fakedClassName = fakedClass.getName();
if (isGeneratedImplementationClass(fakedClassName)) {
return ConstructorReflection.newInstanceUsingPublicDefaultConstructor(fakedClass);
}
if (Proxy.isProxyClass(fakedClass)) {
return MockInvocationHandler.newMockedInstance(fakedClass);
}
return ConstructorReflection.newUninitializedInstance(fakedClass);
}
/**
* An empty method that can be overridden in a fake class that wants to be notified whenever the fake is
* automatically torn down.
* Tear down happens when the fake 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
* "fakes" system property.
*
* By default, this method does nothing.
*/
protected void onTearDown() {}
}