Как можно протестировать контроллер который обращается через Feign к 3 внешним сервисам?
Хотелось бы протестировать мой контролер на статус и возвращаемое значение. В ходе тестирования получаю ошибку. Как можно исправить данную ошибку или посоветуйте более лучшие способы решения проблемы.
Тест:
@RunWith(SpringRunner.class)
@WebMvcTest(CurrencyComporatorController.class)
@AutoConfigureMockMvc
public class CurrencyComporatorControllerTest {
@InjectMocks
private CurrencyComporatorController currencyComporatorController;
@Mock
private CurrencyComporatorServiceProxy currencyComporatorServiceProxy;
@Mock
private GifServiceProxy gifServiceProxy;
@Mock
private GiphyServiceProxy giphyServiceProxy;
@Autowired
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(currencyComporatorController).build();
}
@Test
public void testReturn200AndContentType() throws Exception {
String url = "/compare-currency-excgange/charcode/{USD}";
this.mockMvc.perform(get("/compare-currency-excgange/charcode/{charcode}","USD"))
.andExpect(content().contentType(MediaType.IMAGE_GIF));
}
}
Причина ошибки:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'currencyComporatorController': Unsatisfied dependency expressed through field 'currencyProxy'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.CurrencyComporator.service.CurrencyComporatorServiceProxy': Unexpected exception during bean creation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.openfeign.FeignContext' available
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:917)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:582)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 89 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.CurrencyComporator.service.CurrencyComporatorServiceProxy': Unexpected exception during bean creation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.openfeign.FeignContext' available
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
... 109 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.openfeign.FeignContext' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:331)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:322)
at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:224)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1231)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1173)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
... 117 more
Код контроллера:
@RestController
public class CurrencyComporatorController {
@Autowired
private CurrencyComporatorServiceProxy currencyProxy;
@Autowired
private GiphyServiceProxy giphyProxy;
@Autowired
private GifServiceProxy gifProxy;
@GetMapping(value = "/compare-currency-excgange/charcode/{name}")
public ResponseEntity<InputStreamResource> getGifByCurrencyChanges(@PathVariable String name) throws IOException {
String query = getQueryGifPath(name);
if (query.equals("")) {return null;}
Resource resource = gifProxy.retrieveGifImage(getGifIdFromUrl(url));
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_GIF)
.body(new InputStreamResource(resource.getInputStream()));
}
Ответы (1 шт):
Автор решения: Roman Konoval
→ Ссылка
Ключевая проблема в том, что WebMvcTest не создает всех бинов, а только контроллер. Про моки созданные в тесте с помощью @Mock контекст, который поднимается в тесте ничего не знает, нужно пользоваться @MockBean.
Правильный способ конфигурации mvc тестов такой:
@WebMvcTest(CurrencyComporatorController.class)
public class CurrencyComporatorControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private CurrencyComporatorServiceProxy currencyComporatorServiceProxy;
@MockBean
private GifServiceProxy gifServiceProxy;
@MockBean
private GiphyServiceProxy giphyServiceProxy;
@Test
public void test() {
...
}
}
То есть:
- Использовать автоматически сконфигурированный
MockMvc - Вместо
MockиспользоватьMockBean(у вас сейчас ошибка из-за этого). - Если в тесте вдруг нужен доступ к самому контроллеру, то можно его через
@Autowireдобавить, но это в таких тестах делать неправильно. Я так понимаю, что сейчас у вас он есть, как попытка внедрить зависимости. - Не нужно использовать 'SpringRunner.class'