charlycou
charlycou

Reputation: 1988

Spring context failed to load in test class if @ComponentScan is from an Interface

I have this behaviour in a spring test that I don't witness running the application.

My test class fail to load OpenElevationClientImpl bean even though the OpenElevationClient interface is mentionned in the annotation @SpringBootTest(classes = {OpenElevationClient.class}).

The purpose of this post is that I cannot explain that the OpenElevationClientImpl bean is correctly loaded if I reference in @SpringBootTest annotation a class with the @ComponentScan annotation from the same package of the OpenElevationClient interface.

The test class

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {RestClientConfig.class, OpenElevationConfig.class})
public class EnrichmentUtilsTest {

    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    OpenElevationClient elevationClient;

    @Test
    public void printBeans() {
        for(String tmp:Arrays.asList(applicationContext.getBeanDefinitionNames())){
            System.out.println(tmp);
        }
    }
}

All the following classes are from the same package.

OpenElevationClient interface: bean is not found if this class is in @SpringbootTest

@ComponentScan
public interface OpenElevationClient {
    LocationElevationResultsDTO getElevation(String locations);
}

OpenElevationConfig class: bean is found if this class is in @SpringbootTest

@ComponentScan
public class OpenElevationConfig {
}

OpenElevationClientImpl bean

@Service
public class OpenElevationClientImpl implements OpenElevationClient {

    private OpenElevationLookupApi openElevationLookupApi;

    @Autowired
    public OpenElevationClientImpl(OpenElevationLookupApi openElevationLookupApi, @Value("${openelevation.api.url}") String basePath) {
        openElevationLookupApi.getApiClient().setBasePath(basePath);
        this.openElevationLookupApi = openElevationLookupApi;
    }

    @Override
    public LocationElevationResultsDTO getElevation(String locations) {
        return this.openElevationLookupApi.getLookup(locations);
    }
}

Again, this behaviour is only witnessed running the tests and the OpenElevationConfig.class is only used as a component class to use for loading the ApplicationContext in the test class. Anything I missed that could explained this strange behaviour to me?


EDIT 16/12/2021 - repo to reproduce https://github.com/charlycou/stackoverflow-70198445.

  1. Replace OpenElevationConfig.class with OpenElevationClient.class in the test (AppTest) and the test will fail.
  2. Remove OpenElevationConfig.class and run Main.class and the application is running fine

Upvotes: 2

Views: 1276

Answers (1)

xerx593
xerx593

Reputation: 13261

To the original question:

In Ref-doc I see @ComponentScan ONLY on @Configuration classes.

...


But what I could get out of your (git) repo, was:

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.262 s - in fr.stackoverflow.example.app.AppTest

Results:

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

with this test:

package fr.stackoverflow.example.app;

import fr.stackoverflow.example.api.openelevation.OpenElevationClient;
import fr.stackoverflow.example.api.openelevation.OpenElevationConfig;
import fr.stackoverflow.example.api.openelevation.model.LocationElevationResultsDTO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {
  OpenElevationConfig.class // c'est ca!
})
public class AppTest {

  @Autowired
  OpenElevationClient elevationClient;

  @Test
  public void testBad() {
    assertThrows(org.springframework.web.client.HttpClientErrorException.BadRequest.class, () -> {
      this.elevationClient.getElevation("dummy");
    });
  }

  @Test
  public void testGood() {
    LocationElevationResultsDTO elevation = elevationClient.getElevation("41.161758,-8.583933");
    assertNotNull(elevation);
    assertNotNull(elevation.getResults());
    assertThat(elevation.getResults().isEmpty()).isFalse();
    assertNotNull(elevation.getResults().get(0));
    assertNotNull(elevation.getResults().get(0).getElevation());
    assertThat(elevation.getResults().get(0).getElevation()).isEqualTo(117);
  }
}

Here is how (see code & git comments).

Upvotes: 1

Related Questions