Why is brave.Tracer not autowired?
Asked Answered
H

4

7

We have a lot of services using Spring Boot 2.0.x and io.zipkin.brave.Tracer is used and it works properly. Tracer is used in a class annotated with @Component and it has a constructor with Tracer as its parameter. Here's an example snippet:

@Component
public class CrmMessagePublisher {

    private static final Logger LOGGER = LoggerFactory.getLogger(CrmMessagePublisher.class);

    private static final String EVENT_NAME_HEADER = "service.eventName";

    private static final String EXCHANGE_EVENT = "service.event";

    private static String applicationName;

    private RabbitTemplate rabbitTemplate;

    @Autowired
    private Tracer tracer;

    @Autowired
    public CrmMessagePublisher(
            RabbitTemplate rabbitTemplate,
            @Value("${spring.application.name}") final String applicationName,
            Tracer tracer
    ) {
        this.rabbitTemplate = rabbitTemplate;
        CrmMessagePublisher.applicationName = applicationName;
        this.tracer = tracer;
    }
...

Now I want to write a junit test but I always get

Test ignored.

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
    at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:97)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$invokeTestInstancePostProcessors$5(ClassTestDescriptor.java:349)
    at org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.executeAndMaskThrowable(JupiterTestDescriptor.java:215)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassTestDescriptor.java:349)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1621)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
    at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeTestInstancePostProcessors(ClassTestDescriptor.java:348)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateAndPostProcessTestInstance(ClassTestDescriptor.java:270)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$2(ClassTestDescriptor.java:259)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$3(ClassTestDescriptor.java:263)
    at java.base/java.util.Optional.orElseGet(Optional.java:362)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$4(ClassTestDescriptor.java:262)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$before$0(ClassTestDescriptor.java:192)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.before(ClassTestDescriptor.java:191)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.before(ClassTestDescriptor.java:74)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:105)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'crmMessagePublisher': Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'brave.Tracer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:847)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
    ... 48 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'brave.Tracer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1662)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1221)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
    ... 66 more

Here's the test class:

//@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RabbitMqTest2.RabbitTestConfig.class, CrmMessagePublisher.class, Tracer.class})
////@EnableRabbit
@SpringBootTest
@TestPropertySource("classpath:application.properties")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class RabbitMqTest2 {

    private final String QUEUE_NAME = "crm-test-service";

    @Autowired
    CachingConnectionFactory connectionFactory;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    private RabbitAdmin rabbitAdmin;

    private Binding binding;

    private Queue queue;

    @Autowired
    private CrmMessagePublisher publisher;
//    private Tracer tracer;

    @BeforeAll
    void beforeAll() {
        rabbitAdmin = new RabbitAdmin(this.connectionFactory);
    }

    @BeforeEach
    void beforeEachTestCase() {
        // TODO: Get values from test configuration
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);

        rabbitTemplate = new RabbitTemplate(connectionFactory);

        rabbitTemplate.setDefaultReceiveQueue(QUEUE_NAME);  // for receiving messages
        rabbitTemplate.setRoutingKey(QUEUE_NAME); // for sending messages

        Properties queueProps = rabbitAdmin.getQueueProperties(QUEUE_NAME);
        if( queueProps == null ) {
            queue = new Queue(QUEUE_NAME, false, false, true);
            rabbitAdmin.declareQueue(queue);

            binding = BindingBuilder.bind(queue).to(new FanoutExchange("service.event"));
            rabbitAdmin.declareBinding(binding);
        }

        queueProps = rabbitAdmin.getQueueProperties(QUEUE_NAME);
        Assert.assertEquals("More messages than expected are in the queue.", 0,
                Integer.parseInt(queueProps.getProperty("QUEUE_MESSAGE_COUNT") == null ? "0" : queueProps.getProperty("QUEUE_MESSAGE_COUNT")));
    }

    @AfterEach
    void afterEachTestCase() {
        rabbitAdmin.removeBinding(binding);
        rabbitAdmin.deleteQueue(QUEUE_NAME);
    }

    @Test
    void sendMessageToQueue() throws JsonProcessingException {
        final CrmMessageModel message = new CrmMessageModel();
        message.setCustomerId(1L);
        final AmqpAdmin rabbitAdmin = new RabbitAdmin(this.rabbitTemplate.getConnectionFactory());
        final ObjectMapper om = new ObjectMapper();

        rabbitTemplate.convertAndSend("service.event", "contract-service", om.writeValueAsString(message));

        final Properties queueProps = rabbitAdmin.getQueueProperties(QUEUE_NAME);
        Assert.assertEquals("Not exactly ONE message is in the queue.", 1,
                Integer.parseInt(queueProps.get("QUEUE_MESSAGE_COUNT").toString()));
    }

    @Configuration
    public static class RabbitTestConfig extends ResourceServerConfigurerAdapter {

        @Bean
        public CachingConnectionFactory connectionFactory() {
            return new CachingConnectionFactory();
        }

        @Bean
        public RabbitTemplate crmRabbitTemplate() {
            return new RabbitTemplate(connectionFactory());
        }

        @Bean
        public RestTemplate crmRestTemplate() {
            return new RestTemplate();
        }

    }
}

So: What do I have to do to run the test successful?

Housecoat answered 31/10, 2019 at 17:42 Comment(2)
is brave.Tracer a bean? Where do you configure Tracer?Ruling
I don't configure it at all. It's a third party class and it should work as with CachingConnectionFactory which is a third party class as well.Housecoat
K
3

I hit the same problem; fixed it by ensuring that spring.sleuth.enabled=true when running tests.

Keenakeenan answered 3/11, 2020 at 11:17 Comment(2)
Thank you for reminding me to check the most obvious thing!Stepheniestephens
This one is not applicable anymore because as of SB 3.1, the spring sleuth has been replaced with micrometer tracing.Dippy
U
1

The problem is that the tracer Tracer.class in your @ContextConfiguration does actually nothing:

  • it's not a @Configuration class
  • it does not have a constructor with arguments, which spring could somehow autowire

You should use org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration.class instead, which provides a working instance of Tracer, which could be autowired.

Underpants answered 10/6, 2020 at 9:53 Comment(0)
H
1

Please try change your testing class line from:

@ContextConfiguration(classes = {RabbitMqTest2.RabbitTestConfig.class, CrmMessagePublisher.class, Tracer.class})

to

@ContextConfiguration(classes = {RabbitMqTest2.RabbitTestConfig.class, CrmMessagePublisher.class, TraceAutoConfiguration.class})

Haematogenous answered 17/2, 2021 at 19:36 Comment(2)
whats the fqcn for TraceAutoConfiguration.class?Wisecrack
are you looking for javadoc.io/doc/org.springframework.cloud/… ?Haematogenous
M
0

I solved the issue by adding a configuration class for mock beans in src/test/java:

    @Configuration
    public class MockBeanConfig {
    
        @Bean
        Tracer tracer() {
            return Mockito.mock(Tracer.class);
        }
    
    }
Moiety answered 15/5 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.