Simple tests of Observable results in nodejs with chai and mocha

Viewed 2621

I am developing an app using Nodejs, RxJS and Typescript.

The app has a function which returns an Observable of a string

myObsFunction() : Observable<string> {
... do stuff
}

I would like to be able to make a simple test to check that when I subscribe to this function I get the expected string. I am using chai and mocha and so I write the following test case

import { expect } from 'chai';
import 'mocha';

import {myObsFunction} from './my-source-file';

describe('myObsFunction function', () => {

    it('check myObsFunction', () => {
        const expectedString = 'abc';
        let receivedString: string;
        myObsFunction().subscribe(
            data => receivedString = data,
            error => console.error(error),
            () => expect(receivedString).to.equal(expectedString)
        )
    });

});

Unfortunately this test case does not work as expected by me. It always behaves as it has been successfully passed even in case of errors. The expect check which I have written in the onCompleted function does not signal anything even when the expectedString is not equal the the receivedString. The onCompleted function is actually executed (I can see this just adding a console.log instruction in the onCompleted function) but the expect does not signal any error when there are errors

Is there any way to run such simple tests without having to start using Schedulers and more complex mechanisms?

1 Answers

The test logic looks sound, here's a working example with mocha and chai.

console.clear() 
const Observable = Rx.Observable
mocha.setup('bdd');
const assert = chai.assert;
const should = chai.should();
const expect = chai.expect;
const done = mocha.done;


const myObsFunction = () => Observable.of('xyz');
const myAsyncObsFunction = () => Observable.timer(500).mapTo('xyz');

describe('RxJs Observable Test Examples', function() {

  it('should test the observable succeeds', function () {
    const expectedString = 'xyz';
    let receivedString: string;
    myObsFunction().subscribe(
      data => receivedString = data,
      error => console.error(error),
      () => {
        expect(receivedString).to.equal(expectedString);
      }  
    )
  });

  it('should test the observable fails', function () {
    const expectedString = 'abc';
    let receivedString: string;
    myObsFunction().subscribe(
      data => receivedString = data,
      error => console.error(error),
      () => {
        expect(receivedString).to.equal(expectedString);
      }  
    )
  });

  it('should test the async observable succeeds', function (done) {
    const expectedString = 'xyz';
    let receivedString: string;
    myAsyncObsFunction().subscribe(
      data => receivedString = data,
      error => console.error(error),
      () => {
        //expect(receivedString).to.equal(expectedString);
        if (receivedString !== expectedString) {
          return done(new Error("Failed match"));
        } else {
          return done();
        }
      }  
    )
  });

  it('should test the async observable fails', function (done) {
    const expectedString = 'abc';
    let receivedString: string;
    myAsyncObsFunction().subscribe(
      data => receivedString = data,
      error => console.error(error),
      () => {
        //expect(receivedString).to.equal(expectedString);
        if (receivedString !== expectedString) {
          return done(new Error("Failed match"));
        } else {
          return done();
        }
      }  
    )
  });
});

mocha.run();
<link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.js"></script>
<div id="mocha"></div>


False positives when the observable never fires

One thing I came across was false positives if the observable never fires. Here's some helper functions I use to overcome that problem. Note that take(1) ensures the completed event is fired, even if the observable does not itself complete.

console.clear() 
const Observable = Rx.Observable
mocha.setup('bdd');
const assert = chai.assert;
const should = chai.should();
const expect = chai.expect;

const subscribeAndTestValue = function (observable: Observable<any>, expected: any): string {
  let fail = '';
  let wasSubscribed = false;
  const sub = observable
    .take(1)
    .subscribe(
      (result) => {
        if (result !== expected) {
          fail = 'Subscription result does not match expected value';
        }
        wasSubscribed = true;
      },
      (error) => {
        fail = 'Subscription raised an error';
      },
      (/*completed*/) => {
        // When testing a single value,
        // need to check that the subscription was activated,
        // otherwise the expected value is never tested
        if (!wasSubscribed) {
          fail = 'Subscription produced no results';
        }
      }
    );
  sub.unsubscribe();
  return fail;
}

const subscribeAndTestNoDataEmitted = function (observable: Observable<any>): string {
  let fail;
  let wasSubscribed = false;
  const sub = observable
    .subscribe(
      (result) => {
        wasSubscribed = true;
      },
      (error) => {
        fail = 'Subscription raised an error';
      },
      (/*completed*/) => {
        if (wasSubscribed) {
          fail = 'Subscription produced values when none were expected';
        }
      }
    );
  sub.unsubscribe();
  return fail;
}

const emptyObservable = Observable.empty();
const nonCompletingObservable = Observable.interval(1000);
const emittingObservable = Observable.of('abc');

describe('RxJs Observable Test Examples', function() {

  it('should test the observable fires', function () {
    const expectedString = 'xyz';
    const failed = subscribeAndTestValue(emptyObservable, expectedString);
    expect(failed).to.equal('Subscription produced no results');
  });

  it('should test first observable value of a non-completing observable', function () {
    const expectedString = '0';
    const failed = subscribeAndTestValue(nonCompletingObservable, expectedString);
    expect(failed).to.equal('');
  });

  it('should test the observable does not fire', function () {
    const expectedString = 'xyz';
    const failed = subscribeAndTestNoDataEmitted(emittingObservable, expectedString);
    expect(failed).to.equal('Subscription produced values when none were expected');
  });
  
});

mocha.run();
<link href="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.3.4/mocha.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.js"></script>
<div id="mocha"></div>

Related