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()
Related
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.
I am trying to unit test a Controller Class with Junit. However, when I try to autowire my PlayerRepository interface, which extends crudRepository, it gives this error:
2018-12-06 21:59:39.530 ERROR 8780 --- [ main]
o.s.test.context.TestContextManager : Caught exception while
allowing TestExecutionListener
[org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener#78e117e3]
to prepare test instance
[edu.ceng.gameproject.player.PlayerControllerTest#4f704591]
(I did not put the entire error since it is very long.)
and it also says:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'edu.ceng.gameproject.player.PlayerRepository'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
By the way, I can do the autowire in my controller to make changes on database. It just does not work in testing. Here is my code:
Controller Class:
#Controller // This means that this class is a Controller
#RequestMapping(path="/Player") // This means URL's start with /Player
(after Application path)
public class PlayerController {
#Autowired
private PlayerRepository playerRepository;
}
Here is the PlayerRepsitory interface:
#Repository
public interface PlayerRepository extends CrudRepository<Player, String> {
}
Abstract test class where I do the autowiring :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public abstract class GameProjectBackEndApplicationTests {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
#Autowired
PlayerRepository playerRepository;
protected void setUp() {
mvc =
MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
PlayerControllerTest class where I use the autowired playerRepository:
public class PlayerControllerTest extends GameProjectBackEndApplicationTests
{
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void test_getUsersList_withOk() throws Exception {
String uri = "/Player/all";
// Create user in the database
Player createdUser = playerRepository.save(new Player("testUser",
"testPassword"));
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
// check if status is 200 - OK
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
Player[] playerList = super.mapFromJson(content, Player[].class);
// check if list has actually any user
assertTrue(playerList.length > 0);
// check returned list has user that we created
boolean contains = false;
for (int i = 0; i < playerList.length; i++) {
if
(createdUser.getUsername().equals(playerList[i].getUsername())
&&
createdUser.getPasswd().equals(playerList[i].getPasswd())) {
contains = true;
}
}
// assert there is a user that we created
assertTrue(contains);
//delete created user
playerRepository.deleteById(createdUser.getUsername());
}
}
Thanks in advance.
As I said in the comments, use #MockBean to inject a mock for every dependency needed in your controller. Your test class will look like this.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public class GameProjectBackEndApplicationTests {
private MockMvc mvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private PlayerRepository playerRepository;
#Before
public void setUp() {
mvc =
MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
// Mock calls to PlayerRepository
// Mockito.when(playerRepository.getEntries(1)).thenReturn(myList);
}
#Test
public void myTest() {
....
}
}
Also, I don't really recommend using inheritance for your tests. It is better to have everything in one place.
Hi I have this project running on Spring boot. When I tried to run, it give me the following error:
Description:
Field clientCredentialsApi in com.mycompany.microservice.myproject.bot.controller.BotCommandController required a bean of type 'org.springframework.web.client.RestOperations' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.web.client.RestOperations' in your configuration.
Here is my Code:
Application.java
package com.mycompany.microservice.myproject
//some imports
#SpringBootApplication`
#ComponentScan(basePackages = "com.mycompany.*")
public class Application {
public static void main(String[] args) {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And here is the Controller:
BotCommandController.java
package com.mycompany.microservice.myproject.bot.controller;
//some imports
#RestController
#RequestMapping("/bot-command")
public class BotCommandController {
#Autowired
private RestOperations clientCredentialsApi;
#RequestMapping(value = "/sraix", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String sraixCommand(#RequestParam(name = "input", required = false) final String input,#RequestParam(name = "cs", required = false) final String cs) throws Exception {
final UserApiObject userApiObject = clientCredentialsApi.getForObject(env.getProperty("gms.location") + "/rest/user/" + userId, UserApiObject.class);
return userApiObject.getRole();
}
You are trying to autowire bean that you didn't define or implemented. RestOperations is an interface and has to be implemented.
#Autowired
private RestOperations clientCredentialsApi;
Spring is looking up for the classes annotated with #Bean or #Component or #Service to inject their references. In your case, you didn't define bean RestOperations and Spring can't inject it into BotCommandController.
Something like this:
#Bean
RestOperations rest(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.basicAuthorization("user", "password").build();
}
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.
I keep getting an error when compiling my web application. The error says: ' Could not autowire field: TaskService' etc.
I have the following setup:
The interface: TaskService
public interface TaskService {
String method1();
String method2();
}
The Implementation of the interface: TaskServiceImpl
#Primary
#Service
public class TaskServiceImpl implements TaskService {
#Inject MethodService methodService;
#Inject ProviderService providerService;
#Override
public String method1(){
return "X";
}
#Override
public String method2(){
return "Y";
}
}
Then the controller using the service TaskService: TaskController
#Controller
public class TaskController(){
#Inject TaskService taskService;
#RequestMapping(value = {"/task"}, method = RequestMethod.GET)
public String getTask(Model model){
model.addAttribute("task", taskService.method1());
return "task.html";
}
With the setup described above i get the error HTTP Status 500. Servlet.init()
Error creating bean with name 'TaskController': Injection of autowired dependencies failed; Could not autowire field: TaskService
If I remove the annotation #Controller from TaskController, then the program can start up. Does anybody have an idea why this is happening?