Implemented a WebClientStub to simplify the unit-test
authorKai Moritz <kai@juplo.de>
Sat, 11 Jan 2020 21:00:41 +0000 (22:00 +0100)
committerKai Moritz <kai@juplo.de>
Wed, 15 Jan 2020 09:15:45 +0000 (10:15 +0100)
src/test/java/de/juplo/demo/RemoteContentServiceTest.java
src/test/java/de/juplo/demo/WebClientStub.java [new file with mode: 0644]

index ac1488e..a396d6c 100644 (file)
@@ -1,27 +1,11 @@
 package de.juplo.demo;
 
 
-import java.net.URI;
-import java.nio.charset.Charset;
-import java.time.ZonedDateTime;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.function.Function;
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.MediaType;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
-import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
-import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;
 import reactor.core.publisher.Mono;
 
 
@@ -34,22 +18,11 @@ public class RemoteContentServiceTest
 {
   RemoteContentService service;
 
-  @MockBean
-  WebClient webClient;
-  @MockBean
-  RequestHeadersUriSpec headersSpec;
-  @MockBean
-  RequestBodyUriSpec bodySpec;
-  @MockBean
-  ResponseSpec responseSpec;
-  @MockBean
-  ClientResponse clientResponse;
-
 
   @BeforeEach
   void setUp()
   {
-    service = new RemoteContentService(webClient);
+    service = new RemoteContentService(WebClientStub.WEB_CLIENT);
   }
 
 
@@ -57,47 +30,7 @@ public class RemoteContentServiceTest
   void test()
   {
     Mono<String> mono = Mono.just("bar");
-
-    when(webClient.options()).thenReturn(headersSpec);
-    when(webClient.head()).thenReturn(headersSpec);
-    when(webClient.get()).thenReturn(headersSpec);
-    when(webClient.method(any(HttpMethod.class))).thenReturn(bodySpec);
-    when(headersSpec.uri(any(URI.class))).thenReturn(headersSpec);
-    when(headersSpec.uri(any(String.class), any(Function.class))).thenReturn(headersSpec);
-    when(headersSpec.uri(any(String.class), any(Map.class))).thenReturn(headersSpec);
-    when(headersSpec.uri(any(String.class), (Object[])any())).thenReturn(headersSpec);
-    when(headersSpec.uri(any(Function.class))).thenReturn(headersSpec);
-    when(headersSpec.accept((MediaType[])any())).thenReturn(headersSpec);
-    when(headersSpec.acceptCharset((Charset[])any())).thenReturn(headersSpec);
-    when(headersSpec.attribute(any(String.class), any(Object.class))).thenReturn(headersSpec);
-    when(headersSpec.cookie(any(String.class), any(String.class))).thenReturn(headersSpec);
-    when(headersSpec.cookies(any(Consumer.class))).thenReturn(headersSpec);
-    when(headersSpec.exchange()).thenReturn(mono);
-    when(headersSpec.header(any(String.class), (String[])any())).thenReturn(headersSpec);
-    when(headersSpec.headers(any(Consumer.class))).thenReturn(headersSpec);
-    when(headersSpec.ifModifiedSince(any(ZonedDateTime.class))).thenReturn(headersSpec);
-    when(headersSpec.ifNoneMatch((String[])any())).thenReturn(headersSpec);
-    when(headersSpec.retrieve()).thenReturn(responseSpec);
-    when(bodySpec.uri(any(URI.class))).thenReturn(bodySpec);
-    when(bodySpec.uri(any(String.class), any(Function.class))).thenReturn(bodySpec);
-    when(bodySpec.uri(any(String.class), any(Map.class))).thenReturn(bodySpec);
-    when(bodySpec.uri(any(String.class), (Object[])any())).thenReturn(bodySpec);
-    when(bodySpec.uri(any(Function.class))).thenReturn(bodySpec);
-    when(bodySpec.accept((MediaType[])any())).thenReturn(bodySpec);
-    when(bodySpec.acceptCharset((Charset[])any())).thenReturn(bodySpec);
-    when(bodySpec.attribute(any(String.class), any(Object.class))).thenReturn(bodySpec);
-    when(bodySpec.cookie(any(String.class), any(String.class))).thenReturn(bodySpec);
-    when(bodySpec.cookies(any(Consumer.class))).thenReturn(bodySpec);
-    when(bodySpec.exchange()).thenReturn(Mono.just(clientResponse));
-    when(bodySpec.header(any(String.class), (String[])any())).thenReturn(bodySpec);
-    when(bodySpec.headers(any(Consumer.class))).thenReturn(bodySpec);
-    when(bodySpec.ifModifiedSince(any(ZonedDateTime.class))).thenReturn(bodySpec);
-    when(bodySpec.ifNoneMatch((String[])any())).thenReturn(bodySpec);
-    when(bodySpec.contentLength(any(Long.class))).thenReturn(bodySpec);
-    when(bodySpec.contentType(any(MediaType.class))).thenReturn(bodySpec);
-    when(bodySpec.retrieve()).thenReturn(responseSpec);
-    when(responseSpec.bodyToMono(String.class)).thenReturn(mono);
-    when(clientResponse.bodyToMono(String.class)).thenReturn(mono);
+    WebClientStub.expect(mono);
 
     Mono<String> result = service.getRemoteText("/foo");
 
diff --git a/src/test/java/de/juplo/demo/WebClientStub.java b/src/test/java/de/juplo/demo/WebClientStub.java
new file mode 100644 (file)
index 0000000..328029c
--- /dev/null
@@ -0,0 +1,449 @@
+package de.juplo.demo;
+
+
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntPredicate;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import org.reactivestreams.Publisher;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.reactive.ClientHttpRequest;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriBuilder;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+
+/**
+ * A stub for {@link WebClient}, that can be used for mocking.
+ * @author Kai Moritz
+ */
+public class WebClientStub implements WebClient
+{
+  public final static WebClientStub WEB_CLIENT = new WebClientStub();
+  public final static RequestHeadersUriSpecStub HEADERS_SPEC = new RequestHeadersUriSpecStub();
+  public final static RequestBodySpecStub REQUEST_BODY_SPEC = new RequestBodySpecStub();
+  public final static ResponseSpecStub RESPONSE_SPEC = new ResponseSpecStub();
+
+  private static Supplier<Mono<?>> TO_MONO = () -> null;
+  private static Supplier<Flux<?>> TO_FLUX = () -> null;
+
+
+  public static void expect(Mono<?> mono) { TO_MONO = () -> mono; }
+  public static void expect(Flux<?> flux) { TO_FLUX = () -> flux; }
+
+
+  private WebClientStub() {}
+
+
+  @Override
+  public WebClient.RequestHeadersUriSpec<?> get()
+  {
+    return HEADERS_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestHeadersUriSpec<?> head()
+  {
+    return HEADERS_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestBodyUriSpec post()
+  {
+    return REQUEST_BODY_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestBodyUriSpec put()
+  {
+    return REQUEST_BODY_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestBodyUriSpec patch()
+  {
+    return REQUEST_BODY_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestHeadersUriSpec<?> delete()
+  {
+    return HEADERS_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestHeadersUriSpec<?> options()
+  {
+    return HEADERS_SPEC;
+  }
+
+  @Override
+  public WebClient.RequestBodyUriSpec method(HttpMethod hm)
+  {
+    return REQUEST_BODY_SPEC;
+  }
+
+  @Override
+  public WebClient.Builder mutate()
+  {
+    throw new UnsupportedOperationException("Stub is immutable!");
+  }
+
+
+  public static class RequestHeadersUriSpecStub implements WebClient.RequestHeadersUriSpec
+  {
+    private RequestHeadersUriSpecStub() {}
+
+
+    @Override
+    public WebClient.RequestHeadersSpec uri(URI uri)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec uri(String string, Object... os)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec uri(String string, Map map)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec uri(String string, Function fnctn)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec uri(Function fnctn)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec accept(MediaType... mts)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec acceptCharset(Charset... chrsts)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec cookie(String string, String string1)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec cookies(Consumer cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec ifModifiedSince(ZonedDateTime zdt)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec ifNoneMatch(String... strings)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec header(String string, String... strings)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec headers(Consumer cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec attribute(String string, Object o)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec attributes(Consumer cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.ResponseSpec retrieve()
+    {
+      return RESPONSE_SPEC;
+    }
+
+    @Override
+    public Mono exchange()
+    {
+      return WebClientStub.TO_MONO.get();
+    }
+  }
+
+  public static class RequestBodySpecStub implements WebClient.RequestBodyUriSpec
+  {
+    private RequestBodySpecStub() {}
+
+
+    @Override
+    public WebClient.RequestBodySpec contentLength(long l)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec contentType(MediaType mt)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec<?> bodyValue(Object o)
+    {
+      return this;
+    }
+
+    @Override
+    public <T, P extends Publisher<T>> WebClient.RequestHeadersSpec<?> body(P p, Class<T> type)
+    {
+      return this;
+    }
+
+    @Override
+    public <T, P extends Publisher<T>> WebClient.RequestHeadersSpec<?> body(P p, ParameterizedTypeReference<T> ptr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec<?> body(Object o, Class<?> type)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec<?> body(Object o, ParameterizedTypeReference<?> ptr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec<?> body( BodyInserter<?, ? super ClientHttpRequest> bi)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestHeadersSpec<?> syncBody(Object o)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec accept(MediaType... mts)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec acceptCharset(Charset... chrsts)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec cookie(String string, String string1)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec cookies(Consumer<MultiValueMap<String, String>> cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec ifModifiedSince(ZonedDateTime zdt)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec ifNoneMatch(String... strings)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec header(String string, String... strings)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec headers(Consumer<HttpHeaders> cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec attribute(String string, Object o)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec attributes(Consumer<Map<String, Object>> cnsmr)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.ResponseSpec retrieve()
+    {
+      throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public Mono<ClientResponse> exchange()
+    {
+      throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public WebClient.RequestBodySpec uri(URI uri)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec uri(String string, Object... os)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec uri(String string, Map<String, ?> map)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec uri(String string, Function<UriBuilder, URI> fnctn)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.RequestBodySpec uri(Function<UriBuilder, URI> fnctn)
+    {
+      return this;
+    }
+  }
+
+  public static class ResponseSpecStub implements WebClient.ResponseSpec
+  {
+    private ResponseSpecStub() {}
+
+
+    @Override
+    public WebClient.ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction)
+    {
+      return this;
+    }
+
+    @Override
+    public WebClient.ResponseSpec onRawStatus(IntPredicate statusCodePredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction)
+    {
+      return this;
+    }
+
+    @Override
+    public <T> Mono<T> bodyToMono(Class<T> elementClass)
+    {
+      return (Mono<T>)TO_MONO.get();
+    }
+
+    @Override
+    public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef)
+    {
+      return (Mono<T>)TO_MONO.get();
+    }
+
+    @Override
+    public <T> Flux<T> bodyToFlux(Class<T> elementClass)
+    {
+      return (Flux<T>)TO_FLUX;
+    }
+
+    @Override
+    public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef)
+    {
+      return (Flux<T>)TO_FLUX;
+    }
+
+    @Override
+    public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyClass)
+    {
+      return (Mono<ResponseEntity<T>>)TO_MONO.get();
+    }
+
+    @Override
+    public <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeReference)
+    {
+      return (Mono<ResponseEntity<T>>)TO_MONO.get();
+    }
+
+    @Override
+    public <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementClass)
+    {
+      return (Mono<ResponseEntity<List<T>>>)TO_MONO.get();
+    }
+
+    @Override
+    public <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef)
+    {
+      return (Mono<ResponseEntity<List<T>>>)TO_MONO.get();
+    }
+
+    @Override
+    public Mono<ResponseEntity<Void>> toBodilessEntity()
+    {
+      return (Mono<ResponseEntity<Void>>)TO_MONO.get();
+    }
+  }
+}