Я работаю над простым проектом, который использует Spring Boot 2 с Spring WebFlux с использованием Kotlin. Я написал тест для своей функции-обработчика (в которой я смоделирую зависимости, используя Mockito).
Однако, похоже, что моя функция маршрута не запускает обработчик, так как все мои запросы возвращают HTTP 404 NOT FOUND
(даже если маршрут правильный).
Я смотрел на различные другиепроекты, чтобы выяснить, как эти тесты должны быть написаны ( здесь , здесь ), но проблема сохраняется.
Код выглядит следующим образом (и можеттакже можно найти на GitHub ):
UserRouterTest
@ExtendWith(SpringExtension::class, MockitoExtension::class)
@Import(UserHandler::class)
@WebFluxTest
class UserRouterTest {
@MockBean
private lateinit var userService: UserService
@Autowired
private lateinit var userHandler: UserHandler
@Test
fun givenExistingCustomer_whenGetCustomerByID_thenCustomerFound() {
val expectedCustomer = User("test", "test")
val id = expectedCustomer.userID
`when`(userService.getUserByID(id)).thenReturn(Optional.ofNullable(expectedCustomer))
val router = UserRouter().userRoutes(userHandler)
val client = WebTestClient.bindToRouterFunction(router).build()
client.get()
.uri("/users/$id")
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody(User::class.java)
}
}
Пользователь
@Entity
class User(var username : String, var password: String) {
@Id
val userID = UUID.randomUUID()
}
UserRepository
@Repository
interface UserRepository : JpaRepository<User, UUID>{
}
UserService
@Service
class UserService(
private val userRepository: UserRepository
) {
fun getUserByID(id: UUID): Optional<User> {
return Optional.of(
try {
userRepository.getOne(id)
} catch (e: EntityNotFoundException) {
User("test", "test")
}
)
}
fun addUser(user: User) {
userRepository.save(user)
}
}
UserHandler
@Component
class UserHandler(
private val userService: UserService
) {
fun getUserWithID(request: ServerRequest): Mono<ServerResponse> {
val id = try {
UUID.fromString(request.pathVariable("userID"))
} catch (e: IllegalArgumentException) {
return ServerResponse.badRequest().syncBody("Invalid user id")
}
val user = userService.getUserByID(id).get()
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(user))
}
}
UserRouter
@Configuration
class UserRouter {
@Bean
fun userRoutes(userHandler: UserHandler) = router {
contentType(MediaType.APPLICATION_JSON_UTF8).nest {
GET("/users/{userID}", userHandler::getUserWithID)
GET("") { ServerResponse.ok().build() }
}
}
}
РЕДАКТИРОВАТЬ
Для маршрутизации на основе наличия одного или нескольких параметров запроса (независимо от их значений) мы можем сделатьследующее: UserRouter
@Configuration
class UserRouter {
@Bean
fun userRoutes(userHandler: UserHandler) = router {
GET("/users/{userID}", userHandler::getUserWithID)
(GET("/users/")
and queryParam("username") { true }
and queryParam("password") { true }
)
.invoke(userHandler::getUsers)
}
}
Обратите внимание, что GET("/users/?username={username}", userHandler::getUsersWithUsername)
не работает.