How to inject Spring beans into a JUnit Jupiter ExecutionCondition

237 Views Asked by At

I have custom Interface "Platform" with @ExtendWith annotation and Class which implements ExecutionCondition.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@ExtendWith(PlatformExecutionCondition.class)
public @interface Platform {

    MobilePlatform[] value();

@Component
@RequiredArgsConstructor
public class PlatformExecutionCondition implements ExecutionCondition {

    private final EmulatorConfigProperties emulatorConfigProperties;

    private final PlatformAnnotationManager platformAnnotationManager;

    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext   extensionContext) {
        MobilePlatform platform =       MobilePlatform.getByName(emulatorConfigProperties.getPlatformName());
        if (platform == null) {
            return disabled("Mobile platform is not specified");
        }
        if (extensionContext.getTestMethod().isEmpty()) {
            return enabled("Test class execution enabled");
        }

        Set<MobilePlatform> mobilePlatforms = platformAnnotationManager.getTestMobilePlatforms(extensionContext);
        if (mobilePlatforms.contains(platform)) {
            return enabled(format("{0} mobile platform is available for testing", platform.getName()));
        } else {
            return disabled(format("{0} mobile platform is not available for testing",   platform.getName()));
        }
    }
 }

While running the test, I catch an error java.lang.NoSuchMethodException: com.mob.market3b.platform.execution.PlatformExecutionCondition.<init>() If I remove the annotation, the test runs without errors, but then the evaluateExecutionCondition method is not in use. How can I use @ExtendWith annotation with Spring?

1

There are 1 best solutions below

0
Sam Brannen On

As @slaw mentioned, your PlatformExecutionCondition must have a default no-args constructor.

Consequently, the fields in PlatformExecutionCondition cannot be final.

Although you could create an instance of PlatformExecutionCondition as a bean in your ApplicationContext and have that injected into a field in the test class annotated with @Autowired and @RegisterExtension (see https://stackoverflow.com/a/50251190/388980), the simplest way to access beans from the ApplicationContext within a custom JUnit Jupiter extension is to retrieve them manually from the ApplicationContext -- for example via getBean(<bean type>).

You can access the application context within your PlatformExecutionCondition via org.springframework.test.context.junit.jupiter.SpringExtension.getApplicationContext(ExtensionContext).

See also: https://stackoverflow.com/a/56922657/388980

WARNING

Please note that accessing the ApplicationContext (or beans within the application context) in an ExecutionCondition implementation is generally not recommended since your ExecutionCondition may then effectively force the creation of an ApplicationContext that would otherwise not be used (if the ExecutionCondition ends up disabling the test). See the documentation for @DisabledIf(loadContext) for details.

Side Note

Spring's @EnabledIf and @DisabledIf annotations may be an easier alternative to implementing your own ExecutionCondition.