Testable Observable

@ktrz__  |      /ktrz

 

Chris Trześniewski

Developer 🥑 Advocate

& Senior Frontend Developer @Scalac

Why we do test

  • Confidence
  • Maintainability
  • Guide the development (TDD)
  • Documentation

How we test

const multiply = (a, b) => a * b;
describe('multiply function', () => {
  it('should calculate properly', () => {
    





  });
});
    const input = {a: 3, b: 4};
    const expected = 12;

    const result = multiply(input.a, input.b);

    expect(result).toEqual(expected); 

How we test

  • Test Observables the same?
     
  • `notNull` operator
    • filters null values

Testing Observables

describe('notNull operator', () => {
  it('should filter null values', () => {
    








  });
});
    const source = from([1, null, 2]);
    const result = source.pipe(notNull);
    const spy = createSpy();

    result.subscribe(spy);

    expect(spy).toHaveBeenCalledTimes(2);
    expect(spy).toHaveBeenCalledWith(1);
    expect(spy).toHaveBeenCalledWith(2);

Testing Observables

  • Are we done?



     
  • ...not really

What's an Observable

  • Data delivered over time
  • Can be synchronous
  • or asynchronous

Second test

describe('notNull operator', () => {
  it('should filter null values - async', () => {
    









  });
});
    const source =
      from([1, null, 2], asyncScheduler);
    const result = source.pipe(notNull);
    const spy = createSpy();

    result.subscribe(spy);

    expect(spy).toHaveBeenCalledTimes(2);
    expect(spy).toHaveBeenCalledWith(1);
    expect(spy).toHaveBeenCalledWith(2);
describe('notNull operator', (done) => {
  it('should filter null values - async',
    (done) => {
    













    });
});
      const source =
        from([1, null, 2], asyncScheduler);
      const result = source.pipe(notNull);
      const spy = createSpy();
      result.subscribe({
        next: spy,
        complete: () => {
          



        }
      });
          expect(spy).toHaveBeenCalledTimes(2);
          expect(spy).toHaveBeenCalledWith(1);
          expect(spy).toHaveBeenCalledWith(2);
          done();
done

Async tests - pitfalls

  • Test end awareness
  • Can be long
      const source =
        from([1, null, 2], asyncScheduler)
        .pipe(delay(3000));

Fake async tests

    const source = from([1, null, 2])
      .pipe(delay(3000));
    const result = source.pipe(notNull);
    const spy = createSpy();

    result.subscribe(spy);
    expect(spy).toHaveBeenCalledTimes(0);
    tick(3000);
    expect(spy).toHaveBeenCalledTimes(2);
    expect(spy).toHaveBeenCalledWith(1);
    expect(spy).toHaveBeenCalledWith(2);

Fake async tests

  • Synchronous
  • Full control over time
  • Fast

Marble testing

  • Visual representation
  • Intuitive
  • Fast

Marble testing

  it('should filter null values - marbles',
    marbles((m: Context) => {
    









    }));
      const sor = '--x--y--z|';
      const exp = '--1-----2|';
      const values = {x: '1', y: null, z: '2'};
      const source$ = m.cold(s, values);
      const expected$ = m.cold(exp);

      const result$ = source.pipe(notNull);

      m.expect(result).toBeObservable(expected);

More examples

Summary

  • Observables - sync & async
  • 3 ways to test
    • async - with done function
    • fake async - manually control time
    • marble testing - visually 

Thank you!

 

Any questions?

 

Made with Slides.com