Reactive Spring Boot: Part 9: Java RSocket Client

Reactive Spring Boot: Part 9: Java RSocket Client


By now, we have an application that works
end to end via Spring’s WebClient. Last lesson, we introduced a new RSocket server,
which is currently running, and in this video we’re going to see how to create a client
to connect to it. As with the WebClientStockClient, we’re going
to drive the RSocket client with an integration test. The test looks almost identical to the WebClientStockClientIntegrationTest,
so let’s copy this test and rename it to RSocketStockClientIntegrationTest. We need to change this to work with an RSocket
client. First let’s declare an RSocketStockClient
variable. RSocketStockClient doesn’t exist yet, so let’s
create it. For now we’ll leave it as an empty class so
our test compiles, we’ll come back here in a minute. We’ll use the rename refactoring to rename
this variable so it’s correctly changed everywhere it’s used. And again, this needs to be an RSocketStockClient. We know this isn’t going to need a webClient,
because that was only needed for the WebClient Stock Client. The test assumes a pricesFor method, we want
the RSocket functionality to work exactly the same as the WebClient functionality, so
we’ll assume our test is correct. Let’s create the missing method on RSocketStockClient. Of course the method declaration looks exactly
the same as it does in WebClientStockClient, so it feels like a good time to introduce
an interface that both clients can implement. We can use IntelliJ IDEA’s Extract Interface
feature to create an interface for WebClientStockClient to implement. It seems logical to call this interface StockClient,
and we want the pricesFor method to appear on the interface since this is the method
that has the same signature on both client classes. We can get IntelliJ IDEA to replace the use
of WebClientStockClient with the StockClient interface instead if we want, this seems sensible
since later we might want to choose either the WebClient or the RSocket implementation
in these places. Let’s just go ahead and do the refactor without
making any additional changes. Our WebClientStockClient class has been updated
to add the Override annotation onto the pricesFor method, and to implement our new StockClient. We want to do something similar in RSocketStockClient,
so let’s go to that class and have it implement StockClient, and add the Override annotation
to document that this method implements the method on the interface. Now our test is compiling we can run it to
see it fail. It should fail since we haven’t implemented
the actual functionality yet. Indeed, it fails on the assertNotNull assertion,
since we’re returning null from the pricesFor method. Normally with Test Driven Development we’d
take small steps to make the tests pass and have more granular tests. For this lesson we’re going to jump right
in and implement the working RSocket client. To make an RSocket connection we need an RSocketRequester. This module doesn’t have the correct dependencies
yet, so let’s go to our pom.xml file. Let’s duplicate one of the spring-boot-starter
dependencies and change it to spring-boot-starter-rsocket. IntelliJ IDEA will download the correct dependencies
and add them to the module. Back in the RSocketStockClient we can now
add the import for RSocketRequester. We’ll make the field private, and add a constructor
parameter for it. Now we can use the rSocketRequester, giving
it a route. In our case, the route is the String value
in the MessageMapping annotation of our RSocket controller method. We also need to send some data to the server,
in our case this is the stock symbol. Then we need to state the type we expect the
call to return, which is a Flux of StockPrice objects. We think this method should work, so we’ll
re-run the test. The test doesn’t compile because we added
an rSocketRequester constructor parameter and we don’t have an instance of RSocketRequester
in our test. Let’s create a method called createRSocketRequester
in the test. Let’s move it to the top near where other
objects are typically initialised. To create an RSocketRequester we use the Builder
inside RSocketRequester. If we add the Autowired annotation, Spring
will inject an instance of this into our test. To finish up creating our RSocketRequester,
we’re going to connect via TCP to our RSocketServer, which is running on localhost at port 7000. We need to call block to wait until we’re
connected. We’re using Autowired because we want Spring
to inject dependencies into the test. To tell Spring to manage the test we need
to annotate it with SpringBootTest. We expect this to work when we run it, but
actually we’re missing something important. We get an error that we’re missing a SpringBootConfiguration,
which might be a little puzzling. In fact, this module doesn’t have a SpringBootApplication
at all, because it was designed to be library code shared amongst other application code,
it’s not an application in its own right. One way to get our test to work in this case
is to create a TestApplication in our test directory, and annotate this as our SpringBootApplication. We don’t need to do anything else, and when
we re-run our integration test everything starts up as expected. The test even passes! Since the test passes we can assume the client
successfully connects to the server via RSocket, gets a Flux of StockPrices, can take the first
five of these and check the first one has the correct symbol. This is a slightly simplistic approach to
testing reactive applications. There are other approaches, one of which is
to use the StepVerifier. Using this approach, we can code our expectations
for the events that we see. For example, we could take just two prices
from the Flux of StockPrice, and expect that both of these prices have the correct symbol. By calling verifyComplete, not only do we
check these expectations are met, but also that there are no more StockPrice objects
after our expected two. We can actually replace our original assertions
entirely with a StepVerifier with five expectations. This approach can support much more than this
simple example, and is also very useful for testing time-based publishers like ours. We have one final piece to consider. Our WebClientStockClient defined a retryBackoff
strategy, and a simple approach for handling errors. We can actually apply exactly the same approach
to our RSocketStockClient as well. When we re-run the test we see it all still
works as expected. So now we have an RSocket server on the back
end emitting stock prices and an RSocket client that can connect to it and see those prices. In the next video we’re going to see how to
switch from using the WebClientStockClient to using our new RSocketStockClient. Thanks for watching!

Author:

Leave a Reply

Your email address will not be published. Required fields are marked *