#WebFluxTest Integration test not finding beans - java

My test class looks like:
#WebFluxTest(controllers = IndexContoller.class)
#Import(UserServiceImpl.class)
class IndexContollerTest {
private static final String PATH = "/";
private static final String ADD_USER = "/signup";
#MockBean
private UserRepo userRepo;
#Autowired
private WebTestClient webClient;
#Autowired
private ReactiveUserDetailsService userService;
#Test
void saveUser() throws Exception {
Mockito.when(userRepo.save(any())).thenReturn(Mono.just(new User()));
webClient.post()
.uri(ADD_USER)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(new User()))
.exchange()
.expectStatus()
.is2xxSuccessful();}
}
and after i ran it it falls with
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'skillGroupRepository' defined in
com.gmail.qwertygoog.roadmap.repository.SkillGroupRepository defined
in #EnableR2dbcRepositories declared on RoadmapApplication: Cannot
resolve reference to bean 'r2dbcEntityTemplate' while setting bean
property 'entityOperations'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'r2dbcEntityTemplate' available
Basically it searches for different service bean's dependency and falls after cant instantiate it.I also tried to add #SpringExtension withought any difference in output. Any suggestions?

Related

Issues with test execution with #ConfigurationProperties usage in java sbring-boot application

Context is getting passed as null, hence getting NullPointerException in context.getBean("abcRestTemplate", RestTemplate.class). What can be the issue for context to be null.
AuthnConfig.java
#Getter
#ConfigurationProperties(prefix = "authn")
#Configuration
public class AuthnConfig {
#Value("${endpoint}")
private String endpoint;
#Value("${client-svc.name}")
private String serviceClientId;
}
RestTemplateConfig.java
#Configuration
public class RestTemplateConfig {
private final AuthnConfig authnConfig;
#Autowired
public RestTemplateConfig(final AuthnConfig authnConfig) {
this.authnConfig = authnConfig;
}
#Bean(name = "abcRestTemplate"){
public RestTemplate abcRestTemplate() {
//authConfig.getEndpoint() etc....
}
}
}
RestTemplateConfigTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class RestTemplateConfigTest {
#Autowired
private RestTemplateConfig restTemplateConfig;
#Autowired
private ApplicationContext context;
#Mock
private AuthnConfig authnConfig;
#Before
public void initializeConfig() {
Mockito.when(authnConfig.getEndpoint()).thenReturn("https://localhost");
}
#Test
public void testForBeanCreation() {
RestTemplate abcRestTemplate = context.getBean("abcRestTemplate", RestTemplate.class);
assertNotNull(abcRestTemplate);
}
}
Errors If #RunWith(MockitoJUnitRunner.class) is used in RestTemplateConfigTest.class:
java.lang.NullPointerException, context is getting passed as NULL in context.getBean("abcRestTemplate", RestTemplate.class);
Errors If #RunWith(SpringRunner.class) is used in RestTemplateConfigTest.class
[ERROR] testForBeanCreation Time elapsed: 0.028 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authnConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'endpoint' in value "${endpoint}"
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'endpoint' in value "${endpoint}"
You need to add the your SpringBooltApplication.class to this #SpringBootTest Annotation, just like this: #SpringBootTest(classes = SpringBootApplicationDemo.class)

Unresolved dependency in UnitTesting Spring Boot

I getting the following error when trying to write a unit test for CircuitBreaker code example.
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'com.foosball.team.Service.TeamServiceUnitTest': Unsatisfied
dependency expressed through field 'registry'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
TeamService:
#Service
public class TeamService {
#Autowired
private ITeamRepository teamRepository;
#Autowired
private PlayerClient playerClient;
Logger logger = LoggerFactory.getLogger(TeamService.class);
.
.
.
TeamServiceUnitTest:
#SpringBootTest(classes = {
TeamService.class
})
#RunWith(SpringRunner.class)
#DirtiesContext
public class TeamServiceUnitTest extends AbstractCircuitBreakerTest {
#MockBean
private ITeamRepository teamRepository;
#MockBean
private PlayerClient playerClient;
#Autowired
private TeamService service;
private TeamEntity teamEntity;
private Logger logger = LoggerFactory.getLogger(TeamServiceUnitTest.class);
#Before
public void setUp(){
teamEntity = new TeamEntity();
teamEntity.setId(1L);
teamEntity.setPlayerOne("One");
teamEntity.setPlayerTwo("Two");
teamEntity.setPlayerThree("Three");
teamEntity.setPlayerFour("Four");
}
#Test
#DisplayName("when Player Client Fails 11 Times Then CircuitBreaker Should Be In OPEN State")
public void whenPlayerClientFailsElevenTimesThenCircuitBreakerShouldBeInOPENState(){
//Given
when(teamRepository.findAll()).thenReturn(new ArrayList<>());
when(playerClient.get(Mockito.anyString())).thenThrow(new RuntimeException());
//When
for(int i=0; i<11; i++){
try {
service.addTeam(teamEntity);
} catch (Exception ignore) {
logger.info(ignore.getClass().getName());
}
}
//Then
checkHealthStatus(BACKEND_B, CircuitBreaker.State.OPEN);
}
}
Ref Class:
public class AbstractCircuitBreakerTest {
protected static final String BACKEND_A = "backendA";
protected static final String BACKEND_B = "playerClientCircuitBreaker";
#Autowired
protected CircuitBreakerRegistry registry;
#Before
public void setup(){
transitionToClosedState(BACKEND_A);
transitionToClosedState(BACKEND_B);
}
protected void checkHealthStatus(String circuitBreakerName, CircuitBreaker.State state) {
CircuitBreaker circuitBreaker = registry.circuitBreaker(circuitBreakerName);
assertThat(circuitBreaker.getState()).isEqualTo(state);
}
protected void transitionToOpenState(String circuitBreakerName) {
CircuitBreaker circuitBreaker = registry.circuitBreaker(circuitBreakerName);
if(!circuitBreaker.getState().equals(CircuitBreaker.State.OPEN)){
circuitBreaker.transitionToOpenState();
}
}
protected void transitionToClosedState(String circuitBreakerName) {
CircuitBreaker circuitBreaker = registry.circuitBreaker(circuitBreakerName);
if(!circuitBreaker.getState().equals(CircuitBreaker.State.CLOSED)){
circuitBreaker.transitionToClosedState();
}
}
}
Ref Project: https://github.com/resilience4j/resilience4j-spring-boot2-demo
Proposed Soln Discussion: https://github.com/resilience4j/resilience4j-spring-boot2-demo/issues/33
Proposed Soln Project: https://github.com/shah-smit/resilience4j-spring-boot2-demo-maven
Looks like CircuitBreakerRegistry you're trying to autowire in you test is a factory class, not a bean. Instead of
#Autowired
protected CircuitBreakerRegistry registry;
Try:
protected CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults()
Here's an answer about not working tests.
Few things here:
1. Using CircuitBreakerRegistry.ofDefaults() creates new instance of object each time so this way the CircuitBreakerRegistry objects you're using in AbstractCircuitBreakerTest and HomeService are different instances. You should probably go back to #Autowired annotation but first you need to define bean of CircuitBreakerRegistry like this:
public class CircuitBreakerConfiguration {
#Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
return CircuitBreakerRegistry.ofDefaults();
}
}
You're using #RunWith(SpringRunner.class) but it's just Junit annotation and it does not initialize Spring Context in your test. You're autowiring a bean in your test so you need Spring Context. For this add also #SpringBootTest annotation.
You're expecting the registry BACKEND_A state to change to OPEN after 11 calls of service.getGreeting() method but I cannot see any usage of transitionToOpenState method.

Unable to autowire RestTemplate for unit test

I have a service which uses an autowired instance of RestTemplate like below
#Service
class SomeAPIService {
private RestTemplate restTemplate;
SomeAPIService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.restTemplate.setRequestFactory(HttpUtils.getRequestFactory());
}
}
Everything runs fine in non-test environment. But when I try to run following unit test in test profile, it starts complaining about unable to autowire rest template.
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(classes = MyApplication.class, webEnvironment = RANDOM_PORT, properties = "management.port:0")
#ActiveProfiles(profiles = "test")
#EmbeddedPostgresInstance(flywaySchema = "db/migration")
public abstract class BaseTest {
}
#SpringBootTest(classes = SomeAPIService.class)
public class SomeAPIServiceTest extends BaseTest {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
Following is the detailed exception -
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'someAPIService': Unsatisfied dependency
expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.springframework.web.client.RestTemplate'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
Any clues?
Following helped me get the correct dependencies autowired. The solution is to also include RestTemplate.class in list of classes given to SpringBootTest.
#SpringBootTest(classes = {RestTemplate.class, SomeAPIService.class})
class SomeAPIService {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
#Emre answer was helpful in guiding me towards the final solution.
You are trying to autowire SomeAPIService without satisfying its dependencies. You should inject Rest Template to SomeAPIService. But you are getting NoSuchBeanDefinitionException for Rest Template.
Take a look how to inject it :
How to autowire RestTemplate using annotations
Alternative answer would be - to use TestRestTemplate
From official docs >>>
TestRestTemplate can be instantiated directly in your integration tests, as shown in the following example:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
#Test
public void testRequest() throws Exception {
HttpHeaders headers = this.template.getForEntity(
"https://myhost.example.com/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
}
Alternatively, if you use the #SpringBootTest annotation with WebEnvironment.RANDOM_PORT or WebEnvironment.DEFINED_PORT, you can inject a fully configured TestRestTemplate and start using it. If necessary, additional customizations can be applied through the RestTemplateBuilder bean.

Creating beans when Autowiring with Spring

I am trying to write a test for my controller. When the web service is running everything works fine. However, when I run the test I get:
Error creating bean with name 'Controller': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.prov.Service' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
As you can see below, I believe have everything correctly Autowired and my project structure is correctly set up so that the component scanner can find the annotations properly, yet I still get this error.
Controller:
#RestController
#RequestMapping("/api")
public class Controller {
#Autowired
private Service service;
#JsonView(Views.All.class)
#RequestMapping(value = "/prov/users", method = RequestMethod.POST)
#ResponseBody
public CommonWebResponse<String> handleRequest(#RequestBody UserData userData) {
return service.prov(userData);
}
}
Service:
#Service
public class Service {
#Autowired
private Repo repo;
#Autowired
private OtherService otherService;
public CommonWebResponse<String> prov(UserData userData) {
// do stuff here
return new SuccessWebResponse<>("Status");
}
}
Controller Test:
#RunWith(SpringRunner.class)
#WebMvcTest(
controllers = Controller.class,
excludeFilters = {
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {CorsFilter.class, AuthenticationFilter.class}
)
}
)
#AutoConfigureMockMvc(secure = false)
public class ControllerTest {
public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Autowired
private MockMvc mvc;
#Test
public void connectToEndpoint_shouldReturnTrue() {
UserData userData = new UserData("a", "bunch", "of", "fields");
try {
mvc.perform(post("/api/prov/users").contentType(APPLICATION_JSON_UTF8)
.content(asJsonString(userData))
.accept(MediaType.ALL))
.andExpect(status().isOk());
} catch (Exception e) {
Assert.fail();
}
}
}
The Controller class autowires your Service class. Therefore, testing the Controller class requires existence of your Service class because Controller depends on creating a bean of type Service. This means you either have to #Autowired your service class into your test, or (preferably) mock it using something like Mockito.
(edit with code example):
#RunWith(SpringRunner.class)
#WebMvcTest(Controller.class)
public class ControllerTest {
#MockBean
private Service service
#Autowired
private MockMvc mvc;
#Test
public void foo() {
String somePayload = "Hello, World";
String myParams = "foo";
when(service.method(myParams)).thenReturn(somePayload);
mvc.perform(get("my/url/to/test").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", is(equalTo("Hello, World"))));
}
}
Do note that this example uses Hamcrest for things like is() and equalTo()

Why spring can't find my bean?

I created an interface and a class:
public interface UserService {
List<User> listAll();
}
#Transactional
public class DefaultUserService implements UserService {
private String tableName;
public List<User> listAll() { someDao.listAllFromTable(tableName); }
public void setTableName(String tableName) { this.tableName = tableName; }
}
Also in my application context xml file context.xml, I defined:
<bean id="userService" class="mypackage.DefaultUserService">
<property name="tableName" value="myusers" />
</bean>
Then I want to test the DefaultUserService:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:context-test.xml"})
#TransactionConfiguration(transactionManager = "testTransactionManager")
#Transactional
public class UserServiceTest {
#Autowired
private DefaultUserService userService;
#Before
public void setup() {
userService.setTableName("mytesttable");
}
#Test
public void test() {
// test with userService;
userService.listAll();
}
}
Notice it uses context-test.xml, which imported the original context.xml:
<import resource="classpath:context.xml"/>
Unfortunately, when the test starts, spring throws exception:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mypackage.UserServiceTest':
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field:
private mypackage.DefaultUserService mypackage.userService
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [mypackage.DefaultUserService] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I'm not sure where is wrong, why spring can't find the bean DefaultUserService I defined?
It's because #Transactional places the bean is behind a jdk proxy implementing UserService interface, after that the bean is only available as UserService and not DefaultUserService.
See https://stackoverflow.com/a/18875681/241986.
You can try setting the table name with a property placeholder #Value("${someprop}") and define that property in test context, or create another interface that will expose setTableName(), and autowire that helper interface into the test case.
I'm not sure there are any easy solutions of the problem, I think this task can be subsumed under the problem of bean redefinition in Spring test-context framework
Spring beans redefinition in unit test environment
Try to replace the class DefaultUserService to the interface UserService
public class UserServiceTest {
#Autowired
private UserService userService;
....
}
You have not defined the getter for your property tableName in your implementing class.Spring IOC container works on the POJO model

Categories