TestScheduler
TestScheduler
The TestScheduler class is part of the rxjs/testing module, and enables synchronous testing of asynchronous, or synchronous, Observables.
The primary method that we'll be using is run().
This method is invoked with a callback function, which receives a RunHelpers object.
Let's take a look at the RunHelpers interface:
export interface RunHelpers {
cold: typeof TestScheduler.prototype.createColdObservable;
hot: typeof TestScheduler.prototype.createHotObservable;
flush: typeof TestScheduler.prototype.flush;
expectObservable: typeof TestScheduler.prototype.expectObservable;
expectSubscriptions: typeof TestScheduler.prototype.expectSubscriptions;
}
As we can see in the interface above, the RunHelpers object contains several properties that are methods that we will use for wiring up our tests.
Let's take a look at each property:
expectObservable()provides the setup for an assertion of anactualObservable, and returns an object that contains a singletoBeproperty. ThetoBeproperty is a function that accepts themarblesstring as the first argument along with an optionalvaluesargument in order to assert theactualObservable to theexpectedresult described in themarblessyntax.expectSubscriptions()provides the setup for an assertion of anactualObservable, and returns an object that contains a singletoBeproperty. ThetoBeproperty is a function that accepts themarblesstring and asserts theactualObservable's subscription occurrences to those that are described in themarblessyntax.cold()provides a cold observable that emits notification(s) upon subscription. The marble syntax string defines the sequencing of notifications. And, we can optionally specify thevaluesof each notification using either an object or an array.hot()provides a hot observable. The primary distinction between a cold and hot observable when testing is the ability to test for notification upon subscribing.flush()provides the ability to manually execute the scheduled assertions created using either theexpectObservable()orexpectSubscriptionfunction.
Let's further clarify the details of the flush() function.
As indicated, the flush() function manually executes the scheduled assertions.
It is important to understand that when we define assertions using either the expectObservable() or expectSubscription() functions that the assertions are not executed synchronously, or in other words, at the time the statement is executed by the test runner.
Rather, the assertions are executed when the callback function to the run() function returns.
If we need to test side effects that are created by our Observable then we will need to manually execute the tests using the flush() function in order to use our testing library's assertion function on the side effect.
Finally, we should note that the TestScheduler can be used with any testing library and test runner.
In these examples we'll be using Jest.
Example
Let's take a look at an example of using the TestScheduler and the run() method:
import { TestScheduler } from 'rxjs/testing';
describe('getting started with RxJS testing with marbles', () => {
let testScheduler: TestScheduler;
beforeEach(() => {
testScheduler = new TestScheduler((actual, expected) =>
expect(actual).toEqual(expected)
);
});
test('say hello world then complete', () => {
testScheduler.run(({ cold, expectObservable }) => {
// todo
});
});
});
Let's review the example code above:
- First, we import the
TestSchedulerclass from therxjs/testingmodule. - We then declare a new
describeblock. The code within eachdescribeblock is executed before any of the actual tests. We'll use this as an opportunity to declare a block-leveltestSchedulervariable that each of our tests will use. - Within the
beforeEach()lifecycle method we create a new instance of theTestScheduler. The constructor function accepts aassertDeepEqualargument, which is a function that will be invoked to by theTestSchedulerinstance to assert a deep equality check. The function is invoked with theactualandexpectedvalues. We are using theexpect()andtoEqual()function provided by Jest in order to perform the deep equality check. - Finally, we use the
test()function to create a new test block. Within each test block we'll use therun()method to execute our tests. Therun()method is invoked with acallbackfunction, which receives theRunHelpersobject.