Hands-On Reactive Programming in Spring 5
上QQ阅读APP看书,第一时间看更新

Reactive Streams technology compatibility kit

While at first glance, Reactive Streams does not seem to be tricky, in reality it does contain a lot of hidden pitfalls. Apart from Java interfaces, the specification includes a lot of documented rules for implementation—perhaps this is the most challenging point. These rules strictly constrain each interface, and it is vital to preserve all of the behaviors mentioned in the specification. This allows further integration of implementations from different vendors, which does not cause any problems. That is the essential point for which these rules were formed. Unfortunately, building a proper test suit that covers all corner cases may take much more time than the proper implementation of interfaces. On the other hand, developers need a common tool that may validate all behaviors and ensure that reactive libraries are standardized and compatible with each other. Luckily, a toolkit has already been implemented by Konrad Malawski for that purpose, and this has the name Reactive Streams Technology Compatibility Kit - or simply TCK.

To learn more about TCK, please see the following link: https://github.com/reactive-streams/reactive-streams-jvm/tree/master/tck .

TCK defends all Reactive Streams statements and tests corresponding implementations against specified rules. Essentially, TCK is a bunch of TestNG test-cases, which should be extended and prepared for verification by a corresponding Publisher or Subscriber. TCK includes a full list of test-classes which aim to cover all of the defined rules in the Reactive Streams specification. Actually, all tests are named to correspond to the specified rules. For instance, one of the sample test-cases which might be found within org.reactivestreams.tck.PublisherVerification is the following:

...
void
required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements()
throws Throwable {
...
ManualSubscriber<T> sub = env.newManualSubscriber(pub); // (1)
try {
sub.expectNone(..., pub)); // (2)
sub.request(1); //
sub.nextElement(..., pub)); //
sub.expectNone(..., pub)); //
sub.request(1); //
sub.request(2); //
sub.nextElements(3, ..., pub)); //
sub.expectNone(..., pub)); //
} finally {
sub.cancel(); // (3)
}
...
}

The key is as follows:

  1. This is the manual subscription to the tested publisher. Reactive Streams' TCK provides its own test classes, which allow the verification of the particular behavior.
  2. This is the expectations' declaration. As might be noticed from the preceding code, here we have a particular verification of the given Publisher's behaviors according to rule 1.01. In that case, we verify that the Publisher cannot signal more elements than the  Subscriber has requested.
  3. This is the Subscription's cancellation stage. Once the test has passed or failed, to close the opened resource and finalize the interaction, we unsubscribe from the Publisher using the ManualSubscriber API.

The importance of the mentioned test is hidden behind the verification of the essential guarantee of interaction that any implementation of the Publisher should provide. Moreover, all test-cases within the  PublisherVerification ensure that the given Publisher is to some degree compliant to the Reactive Streams specification. Here, to some degree means that it is impossible to verify all the rules in the full size. The example of such rules is rule 3.04, which states that the request should not perform heavy computations that cannot be meaningfully tested.