Tutorial: Reactive Spring Boot, Part 9: Java RSocket Client
Let's add an RSocket client that can talk to the RSocket server we created in the last lesson.
Join the DZone community and get the full member experience.Join For Free
Here is everything you've missed so far:
This blog post contains a video showing the process step-by-step and a textual walk-through (adapted from the transcript of the video) for those who prefer a written format.
By now, we have an application that works end to end via Spring's WebClient. In the last lesson, we introduced a new RSocket server, which is currently running, and in this lesson, we're going to see how to create a client to connect to it.
Creating an Integration Test
As with the
WebClientStockClient, we're going to drive the RSocket client with an integration test. The test looks almost identical to the
- Create a copy of WebClientStockClientIntegrationTest in the same test directory and rename it to
- Change the
WebClientStockClientvariable to an
RSocketStockClientvariable and rename the variable to
- (Tip: Using IntelliJ IDEA's rename refactoring will change the name of this variable everywhere it's used so there's no need to find/replace).
- We know this isn't going to need a web client because that was only needed for the
WebClientstock client. Remove the constructor argument and the field declaration.
(Note: This code will not compile yet).
Creating the RSocket Stock Client
RSocketStockClientdoesn't exist yet, so create it as an empty class.
- (Tip: Pressing Alt+Enter on the red
RSocketStockClientcode gives the option of creating this class.)
- The test assumes a
pricesFormethod, so create the missing method on
- (Tip: pressing Alt+Enter on the red
pricesFormethod gives the option of creating this method with the correct signature on
Introducing a StockClient Interface
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.
- Create a new interface called
StockClient. We want the
pricesFormethod to appear on the interface since this is the method that has the same signature on both client classes.
- (Tip: use IntelliJ IDEA's Extract Interface feature on
WebClientStockClientto automatically create the new interface with a
WebClientStockClientclass has been updated to implement the new
StockClientand has the @Override annotation on the
Do the same in
Now the test is compiling run it to see it fail. It should fail on the
assertNotNull assertion since we're returning null from the
Implementing the RSocket Connection
Normally with Test Driven Development, we'd take small steps to make the tests pass and have more granular tests. To keep this lesson focused, we're going to jump right in and implement the working RSocket client.
1. Add spring-boot-starter-rsocket to the pom.xml file for the stock-client module.
- Add an RSocketRequester field called
- Add a constructor parameter for it.
- (Tip: IntelliJ IDEA can automatically generate constructor parameters for fields.)
rSocketRequester.route. For the route, we want to use the same route we defined in the back-end RSocket service, which in our case was "
- Send the stock symbol to the server, via the data method.
- We expect the call to return a Flux of
StockPrice, so pass
StockPrice.classinto the retrieveFlux method.
- Return the result of these calls from the
pricesFormethod instead of null.
Creating the RSocketRequester
The test doesn't compile because we added an
rSocketRequester constructor parameter and we don't have an instance of
RSocketRequester in our test.
- Create a private method called
createRSocketRequesterin the test near the top where other objects are typically initialized.
- Create a field for an RSocketRequester.Builder. If we add the @Autowired annotation, Spring will inject an instance of this into our test.
- To tell Spring to manage the test we need to annotate it with @SpringBootTest.
- Inside createRSocketRequester, use the
rSocketRequesterto connect via TCP to our
RSocketServer, which is running on localhost at port 7000.
- Call block to wait until we're connected.
A Passing Integration Test
We expect this test 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 among other application code, it's not an application in its own right. Let's look at one way to get our test to work.
- Create a class
TestApplicationin the same directory as the test.
- Annotate this with @SpringBootApplication.
- Re-run the integration test, everything should start up as expected, and the test should pass.
Testing With StepVerifier
Since the test passes we can assume the client successfully connects to the server via RSocket, gets a Flux of
StockPrice objects, can take the first five of these and checks 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.
- Create a new
StepVerifierwith five prices from the prices Flux.
- Use expectNextMatches to check the symbol for all five prices is correct.
- Call verifyComplete to not only check these expectations are met, but also that there are no more StockPrice objects after these five.
- Delete the old assertions (the
StepVerifierreplaces them all).
This approach can support much more than this simple example and is also very useful for testing time-based publishers like ours.
Adding a Retry Backoff and Error Handling Strategies
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.
- Copy the
WebClientStockClientand paste into
- Re-run the test, it should all still pass.
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 lesson, we're going to see how to switch from using the
WebClientStockClient to using our new
The full code is available on GitHub.
Published at DZone with permission of Trisha Gee, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.