Skip to content

Commit

Permalink
Find repeatable @⁠ExtendWith meta-annotations on fields again
Browse files Browse the repository at this point in the history
JUnit Jupiter 5.11 introduced a regression regarding extension
registration via fields. Specifically, repeated @⁠ExtendWith
annotations on composed annotations were no longer found.

This commit fixes that regression by reintroducing support for finding
@⁠ExtendWith on custom composed annotations when @⁠ExtendWith is used
as a repeatable annotation.

Fixes #4054

(cherry picked from commit d274794)
  • Loading branch information
sbrannen committed Oct 8, 2024
1 parent b451122 commit 8f45eea
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ on GitHub.
[[release-notes-5.11.3-junit-jupiter-bug-fixes]]
==== Bug Fixes

* ❓
* Extensions can once again be registered via multiple `@ExtendWith`
meta-annotations on the same composed annotation on a field within a
test class.


[[release-notes-5.11.3-junit-jupiter-deprecations-and-breaking-changes]]
==== Deprecations and Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,7 @@ static void registerExtensionsFromExecutableParameters(ExtensionRegistrar regist
* @since 5.11
*/
private static Stream<Field> streamExtensionRegisteringFields(Class<?> clazz, Predicate<Field> predicate) {
Predicate<Field> composedPredicate = predicate.and(
field -> isAnnotated(field, ExtendWith.class) || isAnnotated(field, RegisterExtension.class));
return streamFields(clazz, composedPredicate, TOP_DOWN)//
return streamFields(clazz, predicate.and(registersExtension), TOP_DOWN)//
.sorted(orderComparator);
}

Expand Down Expand Up @@ -236,4 +234,14 @@ private static int getOrder(Field field) {
return findAnnotation(field, Order.class).map(Order::value).orElse(Order.DEFAULT);
}

/**
* {@link Predicate} which determines if a {@link Field} registers an extension via
* {@link RegisterExtension @RegisterExtension} or {@link ExtendWith @ExtendWith}.
*
* @since 5.11.3
*/
private static final Predicate<Field> registersExtension = //
field -> isAnnotated(field, RegisterExtension.class)
|| !findRepeatableAnnotations(field, ExtendWith.class).isEmpty();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.engine.descriptor;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.util.function.Function;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.engine.extension.ExtensionRegistrar;

/**
* Tests for {@link ExtensionUtils}.
*
* @since 5.11.3
*/
class ExtensionsUtilsTests {

@Test
void registerExtensionsViaStaticFields() throws Exception {
Field field = TestCase.class.getDeclaredField("staticField");
ExtensionRegistrar registrar = mock();
ExtensionUtils.registerExtensionsFromStaticFields(registrar, TestCase.class);
verify(registrar).registerExtension(Extension1.class);
verify(registrar).registerExtension(Extension2.class);
verify(registrar).registerExtension(TestCase.staticField, field);
}

@Test
@SuppressWarnings("unchecked")
void registerExtensionsViaInstanceFields() throws Exception {
Class<TestCase> testClass = TestCase.class;
Field field = testClass.getDeclaredField("instanceField");
ExtensionRegistrar registrar = mock();
ExtensionUtils.registerExtensionsFromInstanceFields(registrar, testClass);
verify(registrar).registerExtension(Extension1.class);
verify(registrar).registerExtension(Extension2.class);
verify(registrar).registerUninitializedExtension(eq(testClass), eq(field), any(Function.class));
}

static class Extension1 implements Extension {
}

static class Extension2 implements Extension {
}

static class Extension3 implements Extension {
}

static class Extension4 implements Extension {
}

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(Extension1.class)
@ExtendWith(Extension2.class)
@interface UseCustomExtensions {
}

static class TestCase {

@UseCustomExtensions
@RegisterExtension
static Extension3 staticField = new Extension3();

@UseCustomExtensions
@RegisterExtension
Extension4 instanceField = new Extension4();

}

}

0 comments on commit 8f45eea

Please # to comment.