How to test subscription inside method

Published

Consider this angular component:

export class CheckoutComponent {
  constructor(private paymentService: PaymentService, private paymentModalService: PaymentModalService) {}

  public onCheckout(): void {
    const paymentStatus$: Observable<string> = this.paymentService.getStatus();

    const paymentRequired$ = paymentStatus$.pipe(map(paymentStatus => paymentStatus === 'INCOMPLETE'));

    paymentRequired$.subscribe(() => {
      this.paymentModalService.open();
    });
  }
}

I’m trying to write a jasmine test to confirm that paymentModalService.open() was called:

import { cold } from 'jasmine-marbles';

describe('CheckoutComponent', () => {
  let component: CheckoutComponent;
  let mockPaymentService: jasmine.SpyObj<PaymentService>;
  let mockPaymentModalService: jasmine.SpyObj<PaymentModalService>;

  beforeEach(() => {
    mockPaymentService = jasmine.createSpyObj(['getStatus']);
    mockPaymentModalService = jasmine.createSpyObj(['open']);
    component = new CheckoutComponent(mockPaymentService, mockPaymentModalService);
  });

  it('should open payment modal if required', () => {
    mockPaymentService.getStatus.and.returnValue(cold('a', { a: 'INCOMPLETE' }));
    component.onCheckout();

    expect(mockPaymentModalService.open).toHaveBeenCalled(); // FAIL: was never called
  });
});

However the open method was apparently never called.

Is there any way to wait for the cold test observable to complete emit before asserting that the method has been called?

I’ve tried using fakeAsync and tick but it does not either seem to help. I realize it works with of() instead of a TestColdObservable, but I would like more granular control of observable behavior. I don’t really intend to complete the getStatus() observable.

Source: Angular Questions

Published
Categorized as angular, jasmine, jasmine-marbles, rxjs Tagged , , ,

Answers

Try returning a dummy value from your mocked mockPaymentModalService

For example: mockPaymentModalService.open.and.returnValue(of(1));

This applies even if your real service returns void.


Your main question is:

I would like more granular control of observable behavior. I don’t really intend to complete the getStatus() observable.

If you do not want to complete it then it essentially is not a cold observable, its a hot observable, that would mean you can create a subject and pass

   var sub = new Subject();
    mockPaymentService.getStatus.and.returnValue(sub.asObservable());
    // run method and let it subscribe 
    component.onCheckout();
    
// fire it
    sub.next('a', { a: 'INCOMPLETE' });
 expect(mockPaymentModalService.open).toHaveBeenCalled();

I would suggest you go through the document of how to write component test-cases:
Angular guide to writing test cases

Problem:
In your current method of testing you won’t be able to run component life-cyle, and instead would be running them manually and skip on real-life test cases.
It is fine for service but not components.


Lisa Funk

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Still Have Questions?


Our dedicated development team is here for you!

We can help you find answers to your question for as low as 5$.

Contact Us
faq