mercredi 24 février 2021

WebfluxTest and MockBean

I've got problem with mock. It's null. Could you help me?

  1. UsersService
public interface UsersService {
    Mono<GetUserDto> findById(String id);
    Mono<GetUserDto> findByUsername(String username);
    Mono<CreateUserResponseDto> create(Mono<CreateUserDto> createUserDtoMono);
}
  1. RoutingHandlers
package com.app.routing;

import com.app.dto.CreateUserDto;
import com.app.service.UsersService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
@RequiredArgsConstructor
public class RoutingHandlers {

    private final UsersService usersService;

    public Mono<ServerResponse> register(ServerRequest serverRequest) {
        Mono<CreateUserDto> createUserDtoMono = serverRequest.bodyToMono(CreateUserDto.class);
        var create = usersService.create(createUserDtoMono);
        return toServerResponse(create, HttpStatus.CREATED);

    }

    public Mono<ServerResponse> findByUsername(ServerRequest serverRequest) {
        String username = serverRequest.pathVariable("username");
        return toServerResponse(usersService.findByUsername(username), HttpStatus.OK);
    }

    public Mono<ServerResponse> findById(ServerRequest serverRequest) {
        String id = serverRequest.pathVariable("id");
        return toServerResponse(usersService.findById(id), HttpStatus.OK);
    }

    private static <T> Mono<ServerResponse> toServerResponse(Mono<T> mono, HttpStatus status) {

        return mono
                .flatMap(item -> ServerResponse
                        .status(status)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(item)))
                .doOnError(err -> {
                    System.out.println("---------------------------- ERROR -----------------------------");
                    System.out.println(err);
                    System.out.println("----------------------------------------------------------------");
                })
                .onErrorResume(e -> ServerResponse
                        .status(HttpStatus.INTERNAL_SERVER_ERROR)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(e.getMessage())));
    }
}

  1. Routing
package com.app.routing;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
public class Routing {

    @Bean
    public RouterFunction<ServerResponse> routerFunction(RoutingHandlers routingHandlers) {
        return nest(
                path("/users"),
                route(POST("").and(accept(MediaType.APPLICATION_JSON)), routingHandlers::register)
                        .andRoute(GET("/username/{username}").and(accept(MediaType.APPLICATION_JSON)), routingHandlers::findByUsername)
                        .andRoute(GET("/id/{id}").and(accept(MediaType.APPLICATION_JSON)), routingHandlers::findById)
        );
    }

}

  1. RoutingTest
package com.app;

import com.app.dto.CreateUserDto;
import com.app.dto.CreateUserResponseDto;
import com.app.routing.Routing;
import com.app.routing.RoutingHandlers;
import com.app.service.UsersService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Mono;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {RoutingHandlers.class, Routing.class})
@WebFluxTest
class RoutingTest {

    @Autowired
    private ApplicationContext applicationContext;

    @MockBean
    private UsersService usersService;

    private WebTestClient webTestClient;

    @BeforeEach
    public void setup() {
        webTestClient = WebTestClient.bindToApplicationContext(applicationContext).build();
    }

    @Test
    public void test1() {
        var userBody = CreateUserDto
                .builder()
                .username("u")
                .password("1234")
                .passwordConfirmation("1234")
                .build();

        var body = Mono.just(userBody);

        var userResponse = CreateUserResponseDto
                .builder()
                .id("1")
                .username("u")
                .build();

        var response = Mono.just(userResponse);

        Mockito
                .when(usersService.create(body))
                .thenReturn(response);

        webTestClient
                .post()
                .uri("/users")
                .accept(MediaType.APPLICATION_JSON)
                .body(body, CreateUserDto.class)
                .exchange()
                .expectStatus().isCreated()
                .expectBody(CreateUserResponseDto.class)
                .value(createUserResponseDto -> {
                    Assertions.assertThat(createUserResponseDto)
                            .hasFieldOrPropertyWithValue("username", "u")
                            .hasFieldOrPropertyWithValue("password", "1234");
                });
    }
}

  1. DTOs
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CreateUserDto {
    private String username;
    private String password;
    private String passwordConfirmation;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CreateUserResponseDto {
    private String id;
    private String username;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GetUserDto {
    private String id;
    private String username;
    private String password;
}

After I ran test I got error: Cannot invoke "reactor.core.publisher.Mono.flatMap(java.util.function.Function)" because "mono" is null. In RoutingHandlers in register method instance called create is null:

public Mono<ServerResponse> register(ServerRequest serverRequest) {
        Mono<CreateUserDto> createUserDtoMono = serverRequest.bodyToMono(CreateUserDto.class);
        var create = usersService.create(createUserDtoMono);
        return toServerResponse(create, HttpStatus.CREATED);

    }

Aucun commentaire:

Enregistrer un commentaire