0

I developed a simple class that gets an access token from a REST api. Its not fully implemented, but the idea is for it to persist the token in a database. I also have a function to check if there is any available token and if there's not, it calls the function that gets the token for a new one. I also developed a unit test to check If i'm getting a token.

I'm getting a problem when running my test, a NoSuchBeanDefinitionException issue. I do not know what I'm doing wrong. I'm still pretty new to Spring boot, so all help is appreciated. Below is the code for both my testing and my class that communicates with the REST endpoint.

TokenIntegration class:

package com.foo.receivable.domain.token;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.Arrays;

public class TokenIntegration {

    static final String GET_TOKEN_URI = "";

    static final String API_CLIENT_ID = "";
    static final String API_CLIENT_SECRET = "";
    static final String API_AUDIENCE = "";
    static final String API_GRANT_TYPE = "";

    @Autowired
    private RestTemplate restTemplate;
    private TokenRepository repository;

    private Token API_RetrieveToken(){
        Token t = new Token();
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
            headers.set("client_id", API_CLIENT_ID);
            headers.set("client_secrect", API_CLIENT_SECRET);
            headers.set("audience", API_AUDIENCE);
            headers.set("grant_type", API_GRANT_TYPE);

            HttpEntity<String> entity = new HttpEntity<>(headers);
            ResponseEntity<String> response = restTemplate.exchange(GET_TOKEN_URI, HttpMethod.POST, entity, String.class);
//not implemented yet

        }catch(Exception e){
            e.printStackTrace();
        }
        return t;
    }

    public Token GetAccessToken(){
        Token token;
        try {
            token = repository.findByExpired(LocalDateTime.now());
            if(token == null){
                token = API_RetrieveToken();
            }
        }
        catch (Exception e){
            token = null;
            e.printStackTrace();
        }
        return token;
    }
}

TokenIntegrationTest class:

package com.foo.receivable.domain.token;

import com.foo.receivable.infra.AbstractTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TokenIntegrationTest  extends AbstractTest {

@Autowired
private TokenIntegration integration;

@Test
void ShouldReturnTokenObject(){
    final Token token = integration.GetAccessToken();
    if(token == null){
        //not implemented yet
    }
    assertEquals(Token.class, token.getClass());
}
}

And my exception tracing:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.foo.receivable.domain.token.TokenIntegrationTest': Unsatisfied dependency expressed through field 'integration'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.foo.receivable.domain.token.TokenIntegration' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

EDIT: The problem was the @Component annotation was missing, and so spring did not recognize my TokenIntegration class as a bean. After I changed it, I got yet another error about having a RestTemplate bean. Any help on this?

***************************
APPLICATION FAILED TO START
***************************

Description:

Field restTemplate in com.foo.receivable.domain.token.TokenIntegration required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.

2021-05-10 11:44:50.801 ERROR 19916 --- [           main] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@70ab2d48] to prepare test instance [com.foo.receivable.domain.token.TokenIntegrationTest@22a9170c]

java.lang.IllegalStateException: Failed to load ApplicationContext
Pelicer
  • 873
  • 2
  • 12
  • 36

5 Answers5

1

You have your TokenIntegration marked as @Autowired so you need to tell Spring that it should manage your TokenIntegration bean:

@Component
public class TokenIntegration

This will let Spring know to create one of these beans and put it into the application context, which will in turn make it so that Spring can provide a bean to your @Autowired field.

In other words:

If a bean is not in Spring's application context, it cannot be autowired by Spring. As far as Spring knows it does not exist.

To answer your edit:

Could not autowire field:RestTemplate in Spring boot application

mikeb
  • 9,707
  • 4
  • 48
  • 104
  • Thank you, Mikeb. This is what was missing. After that got out of the way, I got another issue related to beans. Can you check out the edit? – Pelicer May 10 '21 at 14:52
  • https://stackoverflow.com/questions/36151421/could-not-autowire-fieldresttemplate-in-spring-boot-application – mikeb May 10 '21 at 14:55
1

You need to define the class for spring to inject. You can do it either in xml or anotation. ps:

@Service
public class TokenIntegration {
}
Jswq
  • 732
  • 1
  • 6
  • 22
1

A class is identified to be a Bean within Spring in the following ways:

1> Using annotations like @Component or specialized one(s) @Service. This is usually implemented in the class which should be a bean. 2> It could also be implemented using a @Bean annotation on a public method and in a class annotated with @Configuration annotation.

For your case you may use the following options :

Option A>

@Component
public class TokenIntegration {
......
....

Option B>

@Configuration
public class AppConfig {

@Bean
public TokenIntegration tokenIntegration() {
   return new TokenIntegration();
}
}

Either of the options should work.

0

Probably it is because TokenIntegration has no annotation for Spring to indicate it is a bean, or it is in a package not scanned.

Pelicer
  • 873
  • 2
  • 12
  • 36
aled
  • 14,717
  • 2
  • 22
  • 28
0

in your snippet TokenIntegration class is named as TagIntegration, at least it could be the reason. By changing the name I successfully run the test (at least the problem with bean definition is gone). Here is a good answer to how Spring searches for beans: https://stackoverflow.com/a/62186653/5790043.