@rxjs-stuff/marbles
provides a natural feeling integration with Mocha and Chai for RxJS "marbles" testing.
See the RxJS documentation on Marble Tests for more information.
import { expect } from 'chai'
describe.marbles('simple example', ({ cold }) => {
it('emits and closes', () => {
const example$ = cold('--a|')
const expected = '--a|'
expect(example$).to.equal(expected)
})
})
Add the Mocha and Chai integrations from @rxjs-stuff/marbles
in your mocha.config.js
file:
const chai = require('chai')
const { chaiMarbles, config } = require('@rxjs-stuff/marbles/chai')
chai.use(chaiMarbles)
require('@rxjs-stuff/marbles/mocha/node').mochaMarbles(config)
This file must be run before any specs, so it is easiest to include as a require
entry in your
mocha config.
Use with either the webpack-karma
plugin or the Angular CLI, and load @rxjs-stuff/marbles/karma
from your entry point. No other changes are required for karma.conf.js
.
Include @rxjs-stuff/marbles/karma
after Angular's initialization:
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
import '@rxjs-stuff/marbles/karma'
For non-Angular uses, use the "bundle" approach for loading your test files:
import '@rxjs-stuff/marbles/karma'
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp,
): {
keys(): string[]
<T>(id: string): T
}
}
const context = require.context('./src', true, /\.spec\.ts$/)
context.keys().map(context)
Add node_modules/@rxjs-stuff/marbles/@types
to your tsconfig.json
's typeRoots
:
{
"compilerOptions": {
...
"typeRoots": [
"node_modules/@rxjs-stuff/marbles/@types",
"node_modules/@types"
]
}
}
@rxjs-stuff/marbles
augments ambient types from Mocha and Chai in order to add the .marbles
modifier, as well as additional Chai assertions. Since these do not require importing from
@rxjs-stuff/marbles
in the modules where those would be referenced, the additional typeRoots
entry is required to allow TypeScript to pick up the augmented types.
@rxjs-stuff/marbles
provides API extensions for Mocha and Chai that allow you to do marbles testing
using the same syntax and patterns you're already used to.
Use the .marbles
chain when defining a suite to enable marbles testing for all tests and nested suites.
When marbles testing is enabled, the framework automatically takes care of setting up a TestScheduler
instance, as well as calling TestScheduler.flush
for each test.
The suite definition function will be passed an object with the same RunHelpers
utilities provided
when calling RxJS's TestScheduler.run
.
describe.marbles('example', ({ cold, hot }) => {
...
})
The usual suite definition modifiers are also supported when using describe.marbles
:
xdescribe.marbles(...)
describe.marbles.only(...)
describe.marbles.skip(...)
To disable marbles testing within a nested suite, use describe.noMarbles
:
describe.marbles('marbles tests', () => {
describe.noMarbles('non marbles tests', () => {
})
})
@rxjs-stuff/marbles/chai
adds several language chain functions and properties to assist in configuring
your assertions, and also overrides equal
to automatically handle comparisons between Observables
and marbles strings.
You do not need to use expectObservable
to compare marbles - Chai's existing equal
assertion is
overridden to use expectObservable
internally.
import { expect } from 'chai'
describe.marbles('simple example', ({ cold }) => {
it('emits and completes', () => {
const example$ = cold('--a|')
const expected = '--a|'
expect(example$).to.equal(expected)
})
})
Use .with.marbleValues(...)
to compare observables using a value map for marbles events.
import { expect } from 'chai'
describe.marbles('simple example', ({ cold }) => {
it('emits and completes', () => {
const marbleValues = { a: { foo: 'bar' }}
const example$ = cold('--a|', marbleValues)
const expected = '--a|'
expect(example$).with.marbleValues(marbleValues).to.equal(expected)
})
})
Use the subscribedWith(...)
assertion to compare subscriptions
import { expect } from 'chai'
describe.marbles('simple example', ({ cold }) => {
it('emits and completes', () => {
const example$ = cold('--a|')
const expected = '--a|'
const expectedSub = '^--!'
expect(example$).to.equal(expected)
expect(example$).to.have.been.subscribedWith(expectedSub)
})
})
Use the subscription
language chain to provide finer control of subscription behavior during
the test run.
import { expect } from 'chai'
describe.marbles('simple example', ({ hot }) => {
it('emits and completes', () => {
const example$ = hot('--a--a--a|')
const sub = '-----^---!'
const expected = '-----a--a|'
expect(example$).with.subscription(sub).to.equal(expected)
})
})
Use the marbleValues
helper function to generate typed marble values objects to use with the
cold
and hot
observable generators and with.marbleValues(...)
language chain.
// infers type to MarbleValues<string, 'a' | 'b'> // { a: string, b: string }
const myMarbles = marbleValues({ a: 'foo', b: 'bar' })
boolean marble values
The booleanMarbles
object includes marble values for working with boolean values.
export const booleanMarbles = marbleValues({
f: false,
t: true,
u: undefined,
})