spring-cloud-samples/spring-cloud-contract-samples

Add an example with WEBTESTCLIENT & RouterFunction

AndersClausen opened this issue · 1 comments

Would it be possible to add an example of how to set up spring-cloud-contracts for it to work with a RouterFunction in a WebFlux style (please see code below)? I'm particular interested in seeing how the BaseTest class looks like - by that I mean, how is RestAssuredWebTestClient set up, what annotations are used and how much mocking of objects are required.

@Configuration
public class LocationRouter {
    private ReactiveErrorHandler error = new ReactiveErrorHandler();

    @Bean
    public RouterFunction<ServerResponse> route(LocationHandler handler) {
        return RouterFunctions.route(GET("/locations"), handler::getLocations)
                .andRoute(GET("/locations/{geoId}"), handler::getLocationsByGeoId)
                .filter(error.notFound())
                .filter(error.badRequest())
                .filter(error.catchAll());
    }
}

The problem at hand

I cannot get past this error message

java.lang.IllegalStateException: Failed to load ApplicationContext

I have tried a set up like this in my BaseTest class:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public abstract class LocationBaseTest {

    @Autowired
    WebApplicationContext webAppContext;

    @Autowired
    ApplicationContext appContext;

    @Before
    public void setup() {

//        RestAssuredWebTestClient.applicationContextSetup(appContext);
//        RestAssuredWebTestClient.webAppContextSetup(webAppContext);
        RestAssuredWebTestClient.standaloneSetup(new LocationRouter());

    }
}

The commented out lines in the code above are the other options I've tried.
I've also tried to use

@MockBean LocationHandler handler

but that didn't get me anywhere.

Router function configuration

@Configuration
public class ProducerConfiguration {

	private final PersonCheckingService personCheckingService;

	public ProducerConfiguration(PersonCheckingService personCheckingService) {
		this.personCheckingService = personCheckingService;
	}

	// remove::start[]
    @Bean
    public RouterFunction<ServerResponse> route() {
		return RouterFunctions.route(RequestPredicates.POST("/check")
			.and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)), 
			request -> this.check(request));
    }

	private Mono<ServerResponse> check(ServerRequest request) {
		return request.bodyToMono(PersonToCheck.class)
		.flatMap(person -> {
			if (this.personCheckingService.shouldGetBeer(person)) {
				return Mono.just(new Response(BeerCheckStatus.OK));
			}
			return Mono.just(new Response(BeerCheckStatus.NOT_OK));
		})
		.flatMap(res -> ServerResponse.ok().bodyValue(res));
	}
	// remove::end[]

}

interface PersonCheckingService {
	Boolean shouldGetBeer(PersonToCheck personToCheck);
}

class PersonToCheck {
	public int age;

	public PersonToCheck(int age) {
		this.age = age;
	}

	public PersonToCheck() {
	}
}

class Response {
	public BeerCheckStatus status;

	Response(BeerCheckStatus status) {
		this.status = status;
	}
}

enum BeerCheckStatus {
	OK, NOT_OK
} 

Base class setup

package com.example;

import io.restassured.module.webtestclient.RestAssuredWebTestClient;
import org.junit.Before;
import org.springframework.test.web.reactive.server.WebTestClient;

public abstract class BeerRestBase {

	@Before
	public void setup() {
		//remove::start[]
		RestAssuredWebTestClient.webTestClient(WebTestClient.bindToRouterFunction(
			new ProducerConfiguration(personToCheck -> personToCheck.age >= 20).route()).build());
		// remove::end[]
	}
}