Switchboard

An in-process message router that helps to write simple, asynchronous, state-based and collaboration tests.

License

License

Categories

Categories

KeY Data Data Formats Formal Verification
GroupId

GroupId

io.github.whiskeysierra
ArtifactId

ArtifactId

switchboard
Last Version

Last Version

0.3.0
Release Date

Release Date

Type

Type

jar
Description

Description

Switchboard
An in-process message router that helps to write simple, asynchronous, state-based and collaboration tests.
Project URL

Project URL

https://github.com/whiskeysierra/switchboard
Source Code Management

Source Code Management

https://github.com/whiskeysierra/switchboard

Download switchboard

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.whiskeysierra/switchboard/ -->
<dependency>
    <groupId>io.github.whiskeysierra</groupId>
    <artifactId>switchboard</artifactId>
    <version>0.3.0</version>
</dependency>
// https://jarcasting.com/artifacts/io.github.whiskeysierra/switchboard/
implementation 'io.github.whiskeysierra:switchboard:0.3.0'
// https://jarcasting.com/artifacts/io.github.whiskeysierra/switchboard/
implementation ("io.github.whiskeysierra:switchboard:0.3.0")
'io.github.whiskeysierra:switchboard:jar:0.3.0'
<dependency org="io.github.whiskeysierra" name="switchboard" rev="0.3.0">
  <artifact name="switchboard" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.github.whiskeysierra', module='switchboard', version='0.3.0')
)
libraryDependencies += "io.github.whiskeysierra" % "switchboard" % "0.3.0"
[io.github.whiskeysierra/switchboard "0.3.0"]

Dependencies

compile (5)

Group / Artifact Type Version
com.google.code.findbugs : jsr305 jar 3.0.2
org.slf4j : slf4j-api jar 1.7.30
com.google.guava : guava jar 28.2-jre
org.zalando : faux-pas jar 0.8.0
org.organicdesign : Paguro jar 3.1.2

provided (2)

Group / Artifact Type Version
com.google.gag : gag jar 1.0.1
org.projectlombok : lombok jar 1.18.10

test (8)

Group / Artifact Type Version
org.slf4j : slf4j-nop jar 1.7.30
org.junit.jupiter : junit-jupiter-api jar 5.6.0
org.junit.jupiter : junit-jupiter-engine jar 5.6.0
org.junit.jupiter : junit-jupiter-params jar 5.6.0
org.hamcrest : java-hamcrest jar 2.0.0.0
org.hobsoft.hamcrest : hamcrest-compose jar 0.4.0
org.mockito : mockito-core jar 3.2.4
org.mockito : mockito-junit-jupiter jar 3.2.4

Project Modules

There are no modules declared in this project.

Switchboard: In-Process Message Router

Switchboard

Stability: Active Build Status Coverage Status Code Quality Javadoc Release Maven Central License

Switchboard noun, /swɪtʃ bɔːɹd/: The electronic panel that is used to direct telephone calls to the desired recipient.

An in-process message router that helps block on asynchronous events.

  • Technology stack: Java 11+
  • Status: Beta, ported from an internal implementation that is used in production

Example

server.save("bob");
User user = switchboard.subscribe(userCreated("bob"), atLeastOnce(), ofSeconds(10)).get();

Features

  • makes asynchronous interactions more easily
  • simple, extensible API

Origin

In any non-trivial application you'll most probably seen a process like this.

Synchronous Interaction

Since the call to the database is synchronous we can be sure it happened before the client got the response. Testing this case is therefore relatively straightforward:

Response response = server.save(bob);
assertThat(database.getUser("bob"), is(not(nullValue())));

It all gets more difficult when you start to do time-consuming tasks in an asynchronous fashion:

Asynchronous Interaction

The test case from above may no longer work, since we may get the response back from the server before the database finished its part.

The idea of Switchboard is to encapsulate the necessary event-based communication behind a small API that allows to write synchronous-style assertions for asynchronous messages.

The term switchboard refers to old-style telephone switchboards, i.e. big communication devices handled by a switchboard operator that allow to connect multiple parties via telephone lines, usually one caller and one receiver.

In our case those parties are threads and they talk to each other by passing messages.

Dependencies

  • Java 11 or higher

Installation

Add the following dependency to your project:

<dependency>
    <groupId>io.github.whiskeysierra</groupId>
    <artifactId>switchboard</artifactId>
    <version>${switchboard.version}</version>
    <scope>test</scope>
</dependency>

Usage

Any communication via Switchboard consists of two parts: Receiving and sending messages.

Receiving messages

You receive messages by subscribing to it. Subscriptions can be done either in a blocking or non-blocking fashion. Additionally one specifies a subscription mode to indicate how much messages are going to be consumed.

Think of blocking subscriptions as actively sitting in front of the phone and waiting while non-blocking could be seen as having call forwarding from your home to your cell so you can do something else, while waiting for a call.

Subscriptions

A subscription is basically a key that specifies your requirements:

Key<UserCreated, String> userCreated(String name) {
    return Key.of(UserCreated.class, name);
}

Receiving messages in a non-blocking way is usually required if you need to subscribe to multiple different messages:

Future<User> future = switchboard.subscribe(userCreated("bob"), atLeastOnce(), ofSeconds(10));

future.get(); // wait 10 seconds
future.get(5, SECONDS); // wait at most 5 seconds

Subscription Modes

When subscribing to message you can specify one of the following modes. They have different characteristics in terms of termination and success conditions:

Mode Termination Success
atLeast(n) m >= n m >= n
atLeastOnce() m >= 1 m >= 1
atMost(n) m > n m <= n
atMostOnce() m > 1 m <= 1
exactlyOnce() m > 1 m == 1
never() m > 0 m == 0
times(n) m > n m == n

Note: Be ware that only atLeast(n) and atLeastOnce() have conditions that allow early termination for success cases before the timeout is reached. All others will wait for the timeout to ensure its success condition holds true.

Sending messages

You send messages by placing a Deliverable on the switchboard, e.g. a message:

var key = Key.of(UserCreated.class, user.id);
switchboard.publish(message(key, user));

Switchboard has an answering machine builtin. That means any message that arrives without anyone receiving it right away will be recorded and delivered as soon as at least one receiver starts listening. This is especially useful if your tests need to listen to multiple messages and their order is not guaranteed.

switchboard.publish(message);

String string = switchboard.subscribe(key, atLeastOnce(), ofSeconds(10));

The subscriber will get the message immediately upon subscription.

Getting Help

If you have questions, concerns, bug reports, etc., please file an issue in this repository's Issue Tracker.

Getting Involved/Contributing

To contribute, simply make a pull request and add a brief description (1-2 sentences) of your addition or change. For more details, check the contribution guidelines.

Alternatives

Credits and references

Creative Commons License Meals at all Hours by Justin Brown is licensed under Attribution-NonCommercial-ShareAlike 2.0 Generic.

Versions

Version
0.3.0