RxJS log value pipe

Sometimes, instead of using debugger keyword, you'll just want to display the values in the console. When using RxJS you'll probably use quick&dirty tap(value => console.log(value)). There's nothing wrong with it, but it can be done a little nicer and manageable. I'll create a custom RxJS operator to achieve this.

Setup

Requirements: Node, npm, npx

Install dependencies

npm i jest@29 @types/jest@29 ts-jest rxjs typescript

Init ts-jest

npx ts-jest config:init

logValue operator

This operator uses the native tap operator to call the console.log(). Simple as that.

import { MonoTypeOperatorFunction } from 'rxjs';
import { tap } from 'rxjs/operators';

export function logValue<T>(): MonoTypeOperatorFunction<T> {
  return tap((value: T): void => {
    console.log(value);
  });
}

So why use it anyway? There are a couple of advantages:

  • easily find usages of this operator instead of searching the whole project for console.log string

  • easily replace console.log with any other logging mechanism

  • easily add some logic to enable/disable logging (i.e. use environment variable)

  • less code and ease of use

Test it

The test performs the following operations:

  • spies on console.log method

  • emits single and multiple values in the stream

  • tests the number of calls and the arguments of the spy

import { from, of } from 'rxjs';
import { logValue } from './log-value.operator';

describe('Test logValue operator', () => {
  let spy: jest.SpyInstance;

  beforeEach(() => {
    spy = jest.spyOn(console, 'log');
  });

  afterEach(() => {
    spy.mockClear();
  });

  describe('Test single logValue call', () => {
    beforeEach(() => {
      of('the string').pipe(logValue()).subscribe();
    });

    it('should call console.log once', () => {
      expect(console.log).toHaveBeenCalledTimes(1);
      expect(console.log).toHaveBeenNthCalledWith(1, 'the string');
    });
  });

  describe('Test multiple logValue calls', () => {
    beforeEach(() => {
      const data = [1, 2, 3, 4, 5];
      from(data).pipe(logValue()).subscribe();
    });

    it('should call console.log 5 times', () => {
      expect(console.log).toHaveBeenCalledTimes(5);
      expect(console.log).toHaveBeenNthCalledWith(1, 1);
      expect(console.log).toHaveBeenNthCalledWith(2, 2);
      expect(console.log).toHaveBeenNthCalledWith(3, 3);
      expect(console.log).toHaveBeenNthCalledWith(4, 4);
      expect(console.log).toHaveBeenNthCalledWith(5, 5);
    });
  });
});

Run npx jest and check the results. By default, you'll get some console.log outputs on the screen, but the final result should be similar to:

 PASS  src/log-value.operator.spec.ts
  Test logValue operator
    Test single logValue call
      ✓ should call console.log once
    Test multiple logValue calls
      ✓ should call console.log 5 times

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
0
Subscribe to my newsletter

Read articles from Bartosz Szłapak directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bartosz Szłapak
Bartosz Szłapak