junit-team/junit5 · error · TestInstantiationException

TestInstanceFactory [ ] failed to return an instance of [ ]…

Error message

TestInstanceFactory [%s] failed to return an instance of [%s] and instead returned an instance of [%s].

What it means

Thrown when a TestInstanceFactory returns an object that is not null but is not assignable to the test class. The check uses getTestClass().isInstance(instance); when FQNs collide due to ClassLoader differences, identity hash codes are appended to the names so you can tell the two definitions apart.

Solutions

  1. Make the factory return ctx.getTestClass().getDeclaredConstructor().newInstance() (or equivalent) so the instance is literally of the test class.
  2. Resolve ClassLoader conflicts so the test class is loaded exactly once - align the test runtime classpath and remove duplicate classpath entries.
  3. If the factory must proxy, return a subclass that genuinely extends the test class via ByteBuddy/cglib.

Example fix

// before
@Override
public Object createTestInstance(TestInstanceFactoryContext ctx, ExtensionContext ec) {
    return mock(ctx.getTestClass()); // Mockito mock is NOT a test-class instance
}
// after
@Override
public Object createTestInstance(TestInstanceFactoryContext ctx, ExtensionContext ec) {
    return ctx.getTestClass().getDeclaredConstructor().newInstance();
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object instance = factory.createTestInstance(ctx, extensionContext);
if (instance == null || !ctx.getTestClass().isInstance(instance)) {
    throw new IllegalStateException("Factory returned wrong type: " + (instance == null ? "null" : instance.getClass()));
}

Type guard

Class<?> testClass = ctx.getTestClass();
// in factory
Object inst = buildInstance();
if (!testClass.isInstance(inst)) {
    throw new TestInstantiationException("not a " + testClass.getName() + ": " + inst.getClass());
}
return inst;

Prevention

When it happens

Trigger: Factory returns an instance whose Class differs from getTestClass() - returns a subclass loaded by a different loader, returns an unrelated type, or returns a Mockito proxy class whose superclass is not the test class.

Common situations: Test class loaded twice by different ClassLoaders (OSGi, build tools with isolated test classpaths), factory mistakenly returns a wrapper/mock instead of the real test class, or Kotlin companion-object misuse where the factory returns the companion instead of an instance.

Related errors


AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11). Data as JSON: /api/errors/787d6ec285f255f4. Report an issue: GitHub.

Appendix: source

Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java:400

			throw new TestInstantiationException(message, throwable);
		}

		if (!getTestClass().isInstance(instance)) {
			String testClassName = getTestClass().getName();
			Class<?> instanceClass = (instance == null ? null : instance.getClass());
			String instanceClassName = (instanceClass == null ? "null" : instanceClass.getName());

			// If the test instance was loaded via a different ClassLoader, append
			// the identity hash codes to the type names to help users disambiguate
			// between otherwise identical "fully qualified class names".
			if (testClassName.equals(instanceClassName)) {
				testClassName += "@" + Integer.toHexString(System.identityHashCode(getTestClass()));
				instanceClassName += "@" + Integer.toHexString(System.identityHashCode(instanceClass));
			}
			String message = "TestInstanceFactory [%s] failed to return an instance of [%s] and instead returned an instance of [%s].".formatted(
				testInstanceFactory.getClass().getName(), testClassName, instanceClassName);

			throw new TestInstantiationException(message);
		}

		return instance;
	}

	private Object invokeTestClassConstructor(@Nullable Object outerInstance, ExtensionRegistry registry,
			ExtensionContextSupplier extensionContext) {

		Constructor<?> constructor = ReflectionUtils.getDeclaredConstructor(getTestClass());
		return executableInvoker.invoke(constructor, outerInstance, extensionContext, registry,
			InvocationInterceptor::interceptTestClassConstructor);
	}

	private void invokeTestInstancePreConstructCallbacks(TestInstanceFactoryContext factoryContext,
			ExtensionRegistry registry, ExtensionContextSupplier context) {
		registry.stream(TestInstancePreConstructCallback.class).forEach(extension -> executeAndMaskThrowable(
			() -> extension.preConstructTestInstance(factoryContext, context.get(extension))));
	}

View on GitHub (pinned to f070c699a0)