Using the StreamBase JUnit TimeService

Introduction

Starting with StreamBase 7.4, you can control the passage of time when running StreamBase JUnit tests of EventFlow applications. This feature allows you to run unit tests of modules that react to time, such as those containing timeouts or those that use metronomes. Using the TimeService to set a custom start time is analogous to temporarily setting the BIOS system time of the machine hosting StreamBase Server.

For example, you can run a unit test, enqueue events that should all be received by the application within one second, and then tell the Server that the one-second time period has elapsed. This allows you to align the EventFlow logic more closely with a real-world input stream.

Starting with StreamBase 7.6.0, support for the TimeService API was updated and extended, as described in the Streambase 7.6.0 New and Noteworthy.

See Creating and Running StreamBase JUnit Tests for an introduction to configuring and running StreamBase JUnit tests.

Configuring Controllable Time

Starting with StreamBase 7.6.0, to manage the reference starting point time of a StreamBase module, you must configure the sbd.sbconf file of the containing project to use the <time-service-configurations> element and one or two of its sub-elements.

To be able to advance the current time of a running StreamBase Server, the project's sbd.sbconf must contain the following:

<time-service-configurations>
  <type>CONTROLLABLE</type>
</time-service-configurations>

To restore the default behavior, change the CONTROLLABLE keyword to WALLCLOCK, or comment out the entire element.

To specify a custom start timestamp for the module, use the <target-time> child element in addition to <type>. The <target-time> element is ignored unless <type> is set to CONTROLLABLE. For example:

<time-service-configurations>
  <type>CONTROLLABLE</type>
  <target-time>2015-10-06 12:00</target-time>
</time-service-configurations>

Utilities for Controlling Time

When a StreamBase application is configured as described in the previous section, you can control the reference start time of the running application either at the command prompt or in expression language constructions.

  • Three expression language functions can be used to manage the running application's reference start time: advanceTimeBy(), advanceTimeTo(), and getTargetTime().

  • Two jsbadmin subcommands, getTargetTime and fastForwardTime, can retrieve the current time and advance it by a specified number of milliseconds, as described on the jsbadmin reference page.

Advancing Time Programmatically in Unit Tests

The overall steps to control time in your StreamBase unit tests are:

  • When configuring your StreamBase JUnit Java file, modify the BaseTestEnvironment's NowImplementation to use an IMPL_TIME_SERVICE.

  • Create a ControllableTimeService and set that as the server's time service.

  • In your test, use timeService.advanceBy() and advanceTo() to manage the clock.

The TimeServiceFactory class methods create TimeService objects to manage time:

  • getWallClockTimeService() — Returns a TimeService that uses wall clock time (that is, it uses your operating system's clock); this is the default.

  • getControllableTimeService() — Returns a ControllableTimeService instance initialized with the current system time. This is the usual starting point if you are trying to control time.

  • getControllableTimeService(long initTargetTimeMsec) — Returns a ControllableTimeService instance initialized with a specified time.

In the SBTestEnvironment interface, the enum NowImplementation indicates how now() behaves. It takes these values:

  • IMPL_SYSTEM — Uses calls to System.currentTimeMillis() on the calling thread

  • IMPL_THREAD — Uses calls to System.currentTimeMillis() on a dedicated thread

  • IMPL_TIME_SERVICE — Uses the current TimeService implementation to get the current time

To get the current implementation use getNowImpl(). To set it to one of the above, use setNowImpl(NowImplementation).

In addition, the SBServerManager class has two methods for time services:

  • getTimeService() — Returns the current TimeService implementation used by StreamBase Server.

  • setTimeService(TimeService timeservice) — Set a TimeService for the StreamBase Server instance controlled by this Manager.

    The input TimeService value can be either a valid object or null. In the latter case it is assumed to be WallClockTimeService object.

    Caution

    The setTimeService method is not thread safe. It can be called from any thread but it should not be called from more than one thread at the same time.

Modifying Generated Test Code to Control Time

When you select an application to test and click NewStreamBase unit test (JUnit), the StreamBase Unit Test Class wizard opens. When you click Finish, it generates code that begins like this:

package com.sb.support.test;

import com.streambase.sb.unittest.SBServerManager;
import com.streambase.sb.unittest.ServerManagerFactory;

public class baseMainTest {

  private static SBServerManager server;

  @BeforeClass
  public static void setupServer() throws Exception {
    // create a StreamBase server and load applications once for 
    // all tests in this class
    server = ServerManagerFactory.getEmbeddedServer();
    server.startServer();
    server.loadApp("main.sbapp");
  }

As the default time service is wall clock time, the wizard generates no code to control time service. To establish control of time, modify the the @BeforeClass block as follows:

@BeforeClass
public static void setupServer() throws Exception {
  // create a StreamBase server and load applications once 
  // for all tests in this class

  // Set NowImplementation to IMPL_TIME_SERVICE instead of the default
  BaseTestEnvironment.DEFAULT_ENVIRONMENT.setNowImpl(NowImplementation
    .IMPL_TIME_SERVICE);

  server = ServerManagerFactory.getEmbeddedServer();

  // Create a ControllableTimeService to use as the server's time service
  timeService = TimeServiceFactory.getControllableTimeService();
  server.setTimeService(timeService);

  server.startServer();
  server.loadApp("main.sbapp");
}

Then, at the beginning of each test, you must initialize the time with this code:

timeService.advanceTo(System.currentTimeMillis());

When you advance the clock, specify the advance interval size and unit. In this example, the advance is 15 seconds:

timeService.advanceBy(15, TimeUnit.SECONDS);

See Java API Documentation in the StreamBase API Documentation for documentation on the classes involved, which are in the packages com.streambase.sb and com.streambase.sb.unittest.