I'm encountering the following error while running my EmployeeControllerTest
class. I've been instructed not to modify the controller class. Could anyone please help me resolve this issue?
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '....controllers.EmployeeController': Unsatisfied dependency expressed through field 'testService1': No qualifying bean of type '...services.TestService1' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
I have attempted the following approaches:
Using reflection to set private fields, Constructor injection in the test class, Creating a Test Configuration class to add the necessary beans, Setting up dependencies manually, I’m still unable to resolve the issue.
@ContextConfiguration(classes = {TestConfiguration.class})
@WebFluxTest(controllers = EmployeeController.class)
@Profile("local")
public class EmployeeControllerTest {
@MockBean
private TestService1 testService1;
@MockBean
private TestService2 testService2;
private WebTestClient webTestClient; /* If I use @Autowired for this field, I'm getting an UnsatisfiedDependencyException: Error creating bean due to an unsatisfied dependency expressed through the field 'webTestClient'.*/
@InjectMocks
private EmployeeController employeeController;
private JwtAuthenticationToken jwtAuthenticationToken;
int pageIndex=0;
int pageSize=10;
@BeforeEach
void setup(){
MockitoAnnotations.openMocks(this);
jwtAuthenticationToken = mock(JwtAuthenticationToken.class);
when(jwtAuthenticationToken.getName()).thenReturn("testUser");
/*If I don't use the code below, I get the error: Cannot invoke "org.springframework.test.web.reactive.server.WebTestClient.post()" because "this.webTestClient" is null.*/
webTestClient = WebTestClient.bindToController(employeeController).webFilter(new SecurityContextServerWebExchangeWebFilter()).build();
}
@Test
void testEmployee(){
Page<EmployeeDto> mockPage = new PageImpl<>(Collections.emptyList());
when(testService1.list(eq(jwtAuthenticationToken), eq(pageIndex), eq(pageSize))).thenReturn(Mono.just(mockPage));
webTestClient.post() .uri("/test/employee/{index}/{size}",pageIndex,pageSize).exchange().expectStatus().isOk().expectBodyList(EmployeeDto.class).hasSize(0);
}}
/*Controller Code:I've been instructed not to modify the controller class*/
@RestController
@RequestMapping("/test")
public class EmployeeController {
@Autowired //Field injection is not recommended
private TestService1 testService1;
@Autowired //Field injection is not recommended
private TestService2 testService2;
@PostMapping("/employee/{index}/{size}")
public Mono<Page<EmployeeDto>> users(@NonNull JwtAuthenticationToken principal, @PathVariable int index, @PathVariable int size) {
return testService1.list(principal, index, size);
}
}
The test runs successfully, as follows. Note that openMocks
is not needed, neither is @ExtendWith(MockitoExtension.class)
that I had mentioned in an earlier version.
@Autowired
to the declaration of WebTestClientprivate EmployeeController employeeController;
.Complete setup of test below, test cases omitted:
@ContextConfiguration(classes = {TestConfiguration.class})
@WebFluxTest(controllers = EmployeeController.class)
public class EmployeeControllerTest {
@MockBean
private TestService1 testService1;
@MockBean
EmployeeController employeeController;
@Autowired
private WebTestClient webTestClient;
private JwtAuthenticationToken jwtAuthenticationToken;
int pageIndex=0;
int pageSize=10;
@BeforeEach
void setup(){
jwtAuthenticationToken = mock(JwtAuthenticationToken.class);
when(jwtAuthenticationToken.getName()).thenReturn("testUser");
//do not use: WebTestClient.bindToController
}
@Test
void testEmployee(){
Page<EmployeeDto> mockPage = new PageImpl<>(Collections.emptyList());
when(testService1.list(eq(jwtAuthenticationToken), eq(pageIndex), eq(pageSize))).thenReturn(mockPage);
webTestClient
.post()
.uri("/test/employee/{index}/{size}",pageIndex,pageSize)
.exchange()
.expectStatus().isOk().expectBodyList(EmployeeDto.class).hasSize(0);
}
}
With this solution you cannot use SecurityContextServerWebExchangeWebFilter
; it would be tested separately.
Regarding the pom.xml, with this configuration the above test works, including the assertions, as is:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>xa.xa.xa</groupId>
<artifactId>plain</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>plain</name>
<description>plain</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>