英文:
Exclude EurekaClient Bean from Application Context in SpringBootApplicationTest
问题
这是我的测试类
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@Transactional
class FooAPITest {
  
  @Test
  void contextLoads() {
  }
}
然而我有一个组件,它注入了EurekaClient以从中获取服务实例
@Component
public class ServiceClient {
  @Autowired
  public ServiceClient(@Qualifier("eurekaClient") EurekaClient eurekaClient) {
    URI serviceUri = URI.create(eurekaClient.getNextServerFromEureka("service", false).getHomePageUrl());
  }
}
因此,由于这个服务,我的应用程序无法加载ApplicationContext。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.netflix.discovery.EurekaClient' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value="eurekaClient")}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
	... 81 more
我迄今为止尝试过的
我考虑过设置自定义的ContextConfiguration来排除ServiceClient,因为在测试类中不需要它。然而,我需要包含一个配置文件,该文件自动装配了EntityManager,但当我使用@SpringBootApplication(classes = {Configuration.class})时,无法注入EntityManager。这个配置看起来像这样:
@Configuration
class Configuration {
  @Autowired
  EntityManager entityManager;
}
这会产生与EntityManager Bean相同的错误:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	... 81 more
当前的解决方法
目前,我通过模拟ServiceClient来避免这个问题,但我想摆脱这个代码异味。
@MockBean
ServiceClient serviceClient;
@BeforeEach
void setUp() {
  MockitoAnnotations.initMocks(FooAPITest.class);
}
另一个解决方法是将注入的Bean标记为不需要,但我认为这不太实用,只是为了使测试工作。
解决这个问题的适当方法是什么?
英文:
I'm trying to write a integration test against my Spring Data API with the following test configuration.
eureka:
  client:
    enabled: false
[..] # No other configuration part that affects discovery/eureka client
This is my test class
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@Transactional
class FooAPITest {
  
  @Test
  void contextLoads() {
  }
}
However I have a component which injects the EurekaClient to get a service instance from it
@Component
public class ServiceClient {
  @Autowired
  public ServiceClient(@Qualifier("eurekaClient") EurekaClient eurekaClient) {
    URI serviceUri = URI.create(eurekaClient.getNextServerFromEureka("service", false).getHomePageUrl());
  }
}
So as of this service my application is not able to load the ApplicationContext.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.netflix.discovery.EurekaClient' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value="eurekaClient")}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
	... 81 more
What I've tried so far
I thought about setting up a custom ContextConfiguration to exclude the ServiceClient as it is not needed in the test class. However I need to include a Configuration File which autowires the EntityManager but when I use @SpringBootApplication(classes = {Configuration.class}) the EntityManager can not be injected. This Configuration looks like that:
@Configuration
class Configuration {
  @Autowired
  EntityManager entityManager;
}
This produces the same error but with EntityManager Bean:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	... 81 more
Current Workaround
Currently I'm avoiding the problem by mocking the ServiceClient but I want to get rid of that code smell.
@MockBean
ServiceClient serviceClient;
@BeforeEach
void setUp() {
  MockitoAnnotations.initMocks(FooAPITest.class);
}
Another workaround would be to mark the Injected Beans as not required but I don't find that practicable only to make the tests work.
What is a proper way to solve this problem?
答案1
得分: 1
你可以尝试在测试中模拟EurekaClient:
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@Transactional
class FooAPITest {
  @MockBean
  private EurekaClient eurekaClient;
  @Test
  void contextLoads() {
  }
}
这将在ApplicationContext中将EurekaClient创建为一个模拟的bean,以便注入到你的服务中。
如果你有其他初始化Spring ApplicationContext的测试,你可以在应用程序包中创建一个单独的配置类,以便被扫描(使用@ConditionalOnMissingBean注解覆盖所有情况):
@Configuration
public class MockEurekaConfiguration {
  @Bean
  @ConditionalOnMissingBean 
  public EurekaClient eurekaClient() {
    return Mockito.mock(EurekaClient.class);
  }
}
英文:
You can try mocking the EurekaClient in your test:
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@Transactional
class FooAPITest {
  
  @MockBean
  private EurekaClient eurekaClient;
  
  @Test
  void contextLoads() {
  }
}
This will create the EurekaClient as a mocked bean in the ApplicationContext to be injected into your service.
If you have other tests that initialize the Spring ApplicationContext, you can create a separate configuration class within the application package to be scanned (using @ConditionalOnMissingBean annotation to cover all bases):
@Configuration
public class MockEurekaConfiguration {
  @Bean
  @ConditionalOnMissingBean 
  public EurekaClient eurekaClient() {
    return Mockito.mock(EurekaClient.class);
  }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论