mockit.MockUp Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.lang.reflect.*;
import javax.annotation.*;
import mockit.internal.faking.*;
import mockit.internal.startup.*;
/**
* 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 @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:
*
* @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 #onTearDown()
* @see #targetType
* @see Tutorial
*/
public class MockUp
{
static { Startup.verifyInitialization(); }
/**
* Holds the class or generic type targeted by this fake instance.
*/
@Nonnull protected final Type targetType;
/**
* Applies the {@linkplain Mock fake methods} defined in the concrete subclass to the class specified through the type parameter.
*/
protected MockUp() {
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) {
redefineClass(classToFake);
}
else {
Type[] typesToFake = ((TypeVariable>) targetType).getBounds();
if (typesToFake.length == 1) {
new CaptureOfFakedImplementations(this, typesToFake[0]).apply();
}
else {
throw new UnsupportedOperationException("Unable to capture more than one base type at once");
}
}
}
@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);
}
private void redefineClass(@Nonnull Class> classToFake) {
if (!classToFake.isInterface()) {
new FakeClassSetup(classToFake, this, targetType).redefineMethods();
}
}
/**
* Applies the {@linkplain Mock fake methods} defined in the fake class to the given class.
*
* In most cases, the {@linkplain #MockUp() constructor with no parameters} can be used.
* This variation is useful when the type to be faked is not known at compile time.
* For example, it can be used with an {@linkplain Mock $advice} method and the fakes system property in order to have an
* aspect-like fake implementation applicable to any class; it can then be applied at the beginning of the test run with the desired
* target class being specified in the test run configuration.
*/
protected MockUp(@Nonnull Class> targetClass) {
targetType = targetClass;
redefineClass(targetClass);
}
/**
* 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() {}
}