Skip to content

Support dependency injection for JUnit Jupiter ArgumentsProvider constructor #34643

@xenoterracide

Description

@xenoterracide

I apologize if this ends up being a question... if so could you please add an example to the reference documentation. Since constructor injection is a spring core feature and not a spring boot feature I think this is right place to raise this issue.

Problem

I'd like to inject an ArgumentsProvider's constructor, but it seems that the ArgumentsProvider does not have a parameter resolver registered; which is possible now.

import static org.assertj.core.api.Assertions.assertThat;

import com.myorg.dto.CreditCardAccount;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PerformanceTest {

  @ParameterizedTest
  @ArgumentsSource(Clients.class)
  void testClient(CommonCardAccountRepository repository) {
    var referenceId = "0";
    var cca = repository.getCreditCardAccount(referenceId);
    assertThat(cca)
      .isInstanceOf(CreditCardAccount.class)
      .hasFieldOrPropertyWithValue("accountReferenceId", referenceId);
  }

  static class Clients implements ArgumentsProvider {

    int port;

    @Autowired
    Clients(@LocalServerPort int port) {
      this.port = port;
    }

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
      return Stream.of(Arguments.arguments(new CommonCardAccountRepositoryWebClientDefault(port)));
    }
  }
}

Constructor injecting into the base test is working.

Solution

Spring should fully register the paremeter resolver such that this works in the same way that it does on the constructor for the base test. I don't really know what that means though, or why it doesn't work now.

extended solution

Simply give me a base argument source that can inject the stream of arguments safely with @LocalServerPort

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PerformanceTest {

  @ParameterizedTest
  @ArgumentsSource(Clients.class)
  void testClient(CommonCardAccountRepository repository) {
    var referenceId = "0";
    var cca = repository.getCreditCardAccount(referenceId);
    assertThat(cca)
      .isInstanceOf(CreditCardAccount.class)
      .hasFieldOrPropertyWithValue("accountReferenceId", referenceId);
  }

// assumes that LocalServerPort is somehow injected into CommonCardAccountRepository
  static class Clients extends BeanCollectionArgumentsProvider<CommonCardAcountRepository> {}

I'm not certain if this use case is common enough to be worth pursuing, but may be worthy of it's own ticket.

Env

Spring Boot 3.3.7
Java 21

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: testIssues in the test modulestatus: invalidAn issue that we don't feel is valid

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions