Home > Back-end >  How to Mock Angular service Emitter and return Subscription
How to Mock Angular service Emitter and return Subscription

Time:01-12

I have the following code in my Component, that I would like to test

  ngOnInit(): void {
    this.authService.loggedInSignal.subscribe((data) => {
      console.log(data)
      if (data) {
        this.planFeatures = this.authService.getProfileData().planFeatures;
        console.log(this.planFeatures)
      }
    });
  }

For that purpose, I mock the authService, but not sure how to test further. I would like to verify that subscription is fired and to test inside code

So far I did

describe('UpgradeSorageButtonComponent', () => {
  let component: UpgradeSorageButtonComponent;
  let fixture: ComponentFixture<UpgradeSorageButtonComponent>;
  let authService: AuthService;
  let mockAuthService = {
    loggedInSignal: {
      subscribe: {}
    },
    
    getProfileData() {return {};}
  };

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [UpgradeSorageButtonComponent],
      imports: [RouterTestingModule],
      providers: [{provide: AuthService, useValue: mockAuthService}]
    }).compileComponents();

    authService = TestBed.inject(AuthService);
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(UpgradeSorageButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  fit('should attach to subscriber', fakeAsync(() => {
    spyOn(authService.loggedInSignal, 'subscribe').and.returnValue(true)
  }));
});

but getting an error that type 'boolean' is not assignable to parameter of type 'Subscription'

Can someone help with how to properly mock the service and do tests for that piece of code? Thanks

CodePudding user response:

The first fixture.detectChanges() you call is when ngOnInit is called.

Since, ngOnInit has this.authService.loggedInSignal.subscribe, it is important that it is mocked and returning an observable value before this fixture.detectChanges().

Follow the comments with !! for more details.

// !! I would use a spy object, it makes constructions of spies easier
let mockAuthService: jasmine.SpyObj<AuthService>;

beforeEach(async () => {
    mockAuthService = jasmine.createSpyObj<AuthService>('AuthService', ['loggedInSignal', 'getProfileData']);
    await TestBed.configureTestingModule({
      declarations: [UpgradeSorageButtonComponent],
      imports: [RouterTestingModule],
      providers: [{provide: AuthService, useValue: mockAuthService}]
    }).compileComponents();
     // !! don't need the below line
    // authService = TestBed.inject(AuthService);
  });

beforeEach(() => {
    fixture = TestBed.createComponent(UpgradeSorageButtonComponent);
    component = fixture.componentInstance;
    authService.loggedInSignal.and.returnValue(of(true));
    // !!!~ ngOnInit is called here
    fixture.detectChanges();
  });

I like using Spy Objects because it makes mocking much easier.

Check this link out on how to use Spy Objects and check this link on how to unit test in Angular as a whole. The 2nd link is really good.

CodePudding user response:

First, remove the fixture.detectChanges(); from the beforeEach call, then call the ngOnInit in your test and -if you spied the code from the authService-, you should tick() the call to "enter" the subscription callback.

fit('should attach to subscriber', fakeAsync(() => {
    ...//Setup test (if necessary)
    spyOn(authService.loggedInSignal, 'subscribe').and.returnValue(true);
    componente.ngOnInit();
    tick();
    expect(thingInsideSubscription);
}));
  •  Tags:  
  • Related