1919import java .util .List ;
2020import java .util .Optional ;
2121
22+ import io .reactivex .rxjava3 .core .Completable ;
23+ import io .reactivex .rxjava3 .core .Flowable ;
24+ import io .reactivex .rxjava3 .core .Single ;
2225import org .junit .jupiter .api .Test ;
26+ import reactor .core .publisher .Flux ;
27+ import reactor .core .publisher .Mono ;
28+ import reactor .test .StepVerifier ;
2329
2430import org .springframework .core .ParameterizedTypeReference ;
31+ import org .springframework .http .HttpEntity ;
2532import org .springframework .http .HttpHeaders ;
2633import org .springframework .http .HttpMethod ;
2734import org .springframework .http .MediaType ;
3643import static org .springframework .http .MediaType .APPLICATION_JSON_VALUE ;
3744
3845/**
39- * Base class for testing {@link HttpServiceMethod} with a test {@link TestExchangeAdapter}
40- * and a test {@link TestExchangeAdapter} that stub the client invocations .
46+ * Tests for {@link HttpServiceMethod} with
47+ * {@link TestExchangeAdapter} and {@link TestReactorExchangeAdapter} .
4148 *
42- * <p>
43- * The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
44- * {@link HttpServiceProxyFactory} to create a service proxy in order to use a strongly
45- * typed interface without the need for class casts.
49+ * <p>The tests do not create or invoke {@code HttpServiceMethod} directly but
50+ * rather use {@link HttpServiceProxyFactory} to create a service proxy in order to
51+ * use a strongly typed interface without the need for class casts.
4652 *
4753 * @author Rossen Stoyanchev
4854 * @author Olga Maciaszek-Sharma
4955 */
50- abstract class HttpServiceMethodTests {
56+ class HttpServiceMethodTests {
5157
52- protected static final ParameterizedTypeReference <String > BODY_TYPE = new ParameterizedTypeReference <>() {
53- };
58+ private static final ParameterizedTypeReference <String > BODY_TYPE = new ParameterizedTypeReference <>() {};
5459
55- protected TestExchangeAdapter client ;
5660
57- protected HttpServiceProxyFactory proxyFactory ;
61+ private final TestExchangeAdapter client = new TestExchangeAdapter ();
62+
63+ private final TestReactorExchangeAdapter reactorClient = new TestReactorExchangeAdapter ();
64+
65+ private final HttpServiceProxyFactory proxyFactory =
66+ HttpServiceProxyFactory .builder ().exchangeAdapter (this .client ).build ();
67+
68+ private final HttpServiceProxyFactory reactorProxyFactory =
69+ HttpServiceProxyFactory .builder ().exchangeAdapter (this .reactorClient ).build ();
70+
5871
5972 @ Test
60- void blockingService () {
61- BlockingService service = this .proxyFactory .createClient (BlockingService .class );
73+ void service () {
74+ Service service = this .proxyFactory .createClient (Service .class );
6275
6376 service .execute ();
6477
6578 HttpHeaders headers = service .getHeaders ();
6679 assertThat (headers ).isNotNull ();
6780
6881 String body = service .getBody ();
69- assertThat (body ).isEqualTo (client .getInvokedMethodName ());
82+ assertThat (body ).isEqualTo (this . client .getInvokedMethodName ());
7083
7184 Optional <String > optional = service .getBodyOptional ();
72- assertThat (optional .get ()).startsWith ("exchangeForBody" );
85+ assertThat (optional .get ()).isEqualTo ("exchangeForBody" );
7386
7487 ResponseEntity <String > entity = service .getEntity ();
75- assertThat (entity .getBody ()).startsWith ("exchangeForEntity" );
88+ assertThat (entity .getBody ()).isEqualTo ("exchangeForEntity" );
7689
7790 ResponseEntity <Void > voidEntity = service .getVoidEntity ();
7891 assertThat (voidEntity .getBody ()).isNull ();
7992
8093 List <String > list = service .getList ();
81- assertThat (list .get (0 )).startsWith ("exchangeForBody" );
94+ assertThat (list .get (0 )).isEqualTo ("exchangeForBody" );
95+ }
96+
97+ @ Test
98+ void reactorService () {
99+ ReactorService service = this .reactorProxyFactory .createClient (ReactorService .class );
100+
101+ Mono <Void > voidMono = service .execute ();
102+ StepVerifier .create (voidMono ).verifyComplete ();
103+ verifyReactorClientInvocation ("exchangeForMono" , null );
104+
105+ Mono <HttpHeaders > headersMono = service .getHeaders ();
106+ StepVerifier .create (headersMono ).expectNextCount (1 ).verifyComplete ();
107+ verifyReactorClientInvocation ("exchangeForHeadersMono" , null );
108+
109+ Mono <String > body = service .getBody ();
110+ StepVerifier .create (body ).expectNext ("exchangeForBodyMono" ).verifyComplete ();
111+ verifyReactorClientInvocation ("exchangeForBodyMono" , BODY_TYPE );
112+
113+ Flux <String > fluxBody = service .getFluxBody ();
114+ StepVerifier .create (fluxBody ).expectNext ("exchange" , "For" , "Body" , "Flux" ).verifyComplete ();
115+ verifyReactorClientInvocation ("exchangeForBodyFlux" , BODY_TYPE );
116+
117+ Mono <ResponseEntity <Void >> voidEntity = service .getVoidEntity ();
118+ StepVerifier .create (voidEntity ).expectNext (ResponseEntity .ok ().build ()).verifyComplete ();
119+ verifyReactorClientInvocation ("exchangeForBodilessEntityMono" , null );
120+
121+ Mono <ResponseEntity <String >> entity = service .getEntity ();
122+ StepVerifier .create (entity ).expectNext (ResponseEntity .ok ("exchangeForEntityMono" ));
123+ verifyReactorClientInvocation ("exchangeForEntityMono" , BODY_TYPE );
124+
125+ Mono <ResponseEntity <Flux <String >>> fluxEntity = service .getFluxEntity ();
126+ StepVerifier .create (fluxEntity .flatMapMany (HttpEntity ::getBody ))
127+ .expectNext ("exchange" , "For" , "Entity" , "Flux" )
128+ .verifyComplete ();
129+ verifyReactorClientInvocation ("exchangeForEntityFlux" , BODY_TYPE );
130+
131+ assertThat (service .getDefaultMethodValue ()).isEqualTo ("default value" );
132+ }
133+
134+ @ Test
135+ void rxJavaService () {
136+ RxJavaService service = this .reactorProxyFactory .createClient (RxJavaService .class );
137+ Completable completable = service .execute ();
138+ assertThat (completable ).isNotNull ();
139+
140+ Single <HttpHeaders > headersSingle = service .getHeaders ();
141+ assertThat (headersSingle .blockingGet ()).isNotNull ();
142+
143+ Single <String > bodySingle = service .getBody ();
144+ assertThat (bodySingle .blockingGet ()).isEqualTo ("exchangeForBodyMono" );
145+
146+ Flowable <String > bodyFlow = service .getFlowableBody ();
147+ assertThat (bodyFlow .toList ().blockingGet ()).asList ().containsExactly ("exchange" , "For" , "Body" , "Flux" );
148+
149+ Single <ResponseEntity <Void >> voidEntity = service .getVoidEntity ();
150+ assertThat (voidEntity .blockingGet ().getBody ()).isNull ();
151+
152+ Single <ResponseEntity <String >> entitySingle = service .getEntity ();
153+ assertThat (entitySingle .blockingGet ().getBody ()).isEqualTo ("exchangeForEntityMono" );
154+
155+ Single <ResponseEntity <Flowable <String >>> entityFlow = service .getFlowableEntity ();
156+ Flowable <String > body = (entityFlow .blockingGet ()).getBody ();
157+ assertThat (body .toList ().blockingGet ()).containsExactly ("exchange" , "For" , "Entity" , "Flux" );
82158 }
83159
84160 @ Test
@@ -128,13 +204,14 @@ void typeAndMethodAnnotatedService() {
128204 assertThat (requestValues .getHeaders ().getAccept ()).containsExactly (MediaType .APPLICATION_JSON );
129205 }
130206
131- protected void verifyClientInvocation (String methodName , @ Nullable ParameterizedTypeReference <?> expectedBodyType ) {
132- assertThat (this .client .getInvokedMethodName ()).isEqualTo (methodName );
133- assertThat (this .client .getBodyType ()).isEqualTo (expectedBodyType );
207+ protected void verifyReactorClientInvocation (String methodName , @ Nullable ParameterizedTypeReference <?> expectedBodyType ) {
208+ assertThat (this .reactorClient .getInvokedMethodName ()).isEqualTo (methodName );
209+ assertThat (this .reactorClient .getBodyType ()).isEqualTo (expectedBodyType );
134210 }
135211
212+
136213 @ SuppressWarnings ("unused" )
137- private interface BlockingService {
214+ private interface Service {
138215
139216 @ GetExchange
140217 void execute ();
@@ -159,6 +236,64 @@ private interface BlockingService {
159236
160237 }
161238
239+
240+ private interface ReactorService {
241+
242+ @ GetExchange
243+ Mono <Void > execute ();
244+
245+ @ GetExchange
246+ Mono <HttpHeaders > getHeaders ();
247+
248+ @ GetExchange
249+ Mono <String > getBody ();
250+
251+ @ GetExchange
252+ Flux <String > getFluxBody ();
253+
254+ @ GetExchange
255+ Mono <ResponseEntity <Void >> getVoidEntity ();
256+
257+ @ GetExchange
258+ Mono <ResponseEntity <String >> getEntity ();
259+
260+ @ GetExchange
261+ Mono <ResponseEntity <Flux <String >>> getFluxEntity ();
262+
263+ default String getDefaultMethodValue () {
264+ return "default value" ;
265+ }
266+
267+ }
268+
269+
270+ @ SuppressWarnings ("unused" )
271+ private interface RxJavaService {
272+
273+ @ GetExchange
274+ Completable execute ();
275+
276+ @ GetExchange
277+ Single <HttpHeaders > getHeaders ();
278+
279+ @ GetExchange
280+ Single <String > getBody ();
281+
282+ @ GetExchange
283+ Flowable <String > getFlowableBody ();
284+
285+ @ GetExchange
286+ Single <ResponseEntity <Void >> getVoidEntity ();
287+
288+ @ GetExchange
289+ Single <ResponseEntity <String >> getEntity ();
290+
291+ @ GetExchange
292+ Single <ResponseEntity <Flowable <String >>> getFlowableEntity ();
293+
294+ }
295+
296+
162297 @ SuppressWarnings ("unused" )
163298 private interface MethodLevelAnnotatedService {
164299
@@ -173,7 +308,6 @@ private interface MethodLevelAnnotatedService {
173308 @ SuppressWarnings ("unused" )
174309 @ HttpExchange (url = "${baseUrl}" , contentType = APPLICATION_CBOR_VALUE , accept = APPLICATION_CBOR_VALUE )
175310 private interface TypeAndMethodLevelAnnotatedService extends MethodLevelAnnotatedService {
176-
177311 }
178312
179313}
0 commit comments