angularjasminekarma-jasminemicrosoft-entra-idmsal-angular

ERROR mocking getAllAccounts method with Jasmine and MsalService - Angular


I am facing issues with test-coverage using jasmine, for that I am working with MsalService-Entra (Microsoft Authentication Library) and Angular 15, I am using a method to get All user accounts from a list that MsalService library provides us and the idea is to create a test-coverage for that, specificaly for this line:

this.msalService.instance.getAllAccounts().length 

So I tried in many ways but still I am having problems mocking that getAllAccounts method from msaservice library, this is my code that I tried and I hope you could help me with that.

login.ts

checkAndSetActiveAccount() {
    if (this.msalService.instance && this.msalService.instance.getAllAccounts().length > 0) {
        const accounts = this.msalService.instance.getAllAccounts();

This is my login.spec.ts

    let msalServiceMock: jasmine.SpyObj<MsalService>;    
    
    beforeEach(waitForAsync(() => {
            msalServiceMock = jasmine.createSpyObj('MsalService', ['instance', 'getAllAccounts']);

        TestBed.resetTestingModule();
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule.withRoutes(
                    [{path: 'home', component: HomeComponent}]
                ),
            ],
            providers: [
                {provide: MsalService, useValue: msalServiceMock}
            ],
            declarations: [LoginComponent]
        }).compileComponents();
    }));
...

    it('checkAndSetActiveAccount() check and validate Active Account', ()=> {

        let mockAccounts =[
            { homeAccountId: '', environment:'', tenantId:'', username:'', localAccountId:'' }
        ];

        //msalServiceMock = TestBed.inject(MsalService);
        //mockMsalService.instance.setActiveAccount(mockData[0]);
        //spyOn(msalServiceMock, 'instance').and.returnValue(mockAccounts);
        spyOn(msalServiceMock.instance, 'getAllAccounts').and.returnValue(mockAccounts);
        //spyOn(component.msalService.instance, 'getAllAccounts').and.returnValue(of(mockData));
        component.checkAndSetActiveAccount();

Error on console:

Firefox 132.0 (Mac OS 10.15) LoginComponent processCredentials() is processing username and password checkAndSetActiveAccount() check and validate Active Account FAILED
    Error: <spyOn> : getAllAccounts() method does not exist
    Usage: spyOn(<object>, <methodName>) in node_modules/karma-jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 9619)
    <Jasmine>

Firefox 132.0 (Mac OS 10.15) LoginComponent processCredentials() is processing username and password username and password are empty strings FAILED
        TypeError: this.msalService.instance.getAllAccounts is not a function in main.js (line 98918)

Solution

  • I have not implemented the exact MsalService, but I have mocked the service to solve the test case.

    We use createSpyObj to mock the methods and the properties of the service. I set instance as null so that it can be set on the test case.

      msalServiceMock = jasmine.createSpyObj('MsalService', ['getAllAccounts'], {
        instance: null,
      });
    

    I use `` to set the return value for the property instance and set it to the scope of the same service, this is only for testing purposes. Due to typing issue I had to wrap it inside a as any block.

      (
        Object.getOwnPropertyDescriptor(msalServiceMock, 'instance').get as any
      ).and.returnValue(component.msalService);
    

    Then we can spy on the method and perform the checks.

    it('checkAndSetActiveAccount() check and validate Active Account', () => {
      let mockAccounts = [
        {
          homeAccountId: '',
          environment: '',
          tenantId: '',
          username: '',
          localAccountId: '',
        },
      ];
      (
        Object.getOwnPropertyDescriptor(msalServiceMock, 'instance').get as any
      ).and.returnValue(component.msalService);
      msalServiceMock.getAllAccounts.and.returnValue(mockAccounts as any);
      component.checkAndSetActiveAccount();
      expect(msalServiceMock.getAllAccounts).toHaveBeenCalled();
      expect(component.data).toEqual(mockAccounts);
    });
    

    Full Code:

    import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing';
    import { AppComponent, MsalService } from './app.component';
    import { AppModule } from './app.module';
    import { RouterTestingModule } from '@angular/router/testing';
    import { of } from 'rxjs';
    
    describe('AppComponent', () => {
      let component: AppComponent;
      let fixture: ComponentFixture<AppComponent>;
      let msalServiceMock: jasmine.SpyObj<MsalService>;
    
      beforeEach(waitForAsync(() => {
        msalServiceMock = jasmine.createSpyObj('MsalService', ['getAllAccounts'], {
          instance: null,
        });
    
        TestBed.resetTestingModule();
        TestBed.configureTestingModule({
          imports: [
            // RouterTestingModule.withRoutes([
            // { path: 'home', component: HomeComponent },
            // ]),
          ],
          providers: [{ provide: MsalService, useValue: msalServiceMock }],
          declarations: [AppComponent],
        }).compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
      });
    
      it('checkAndSetActiveAccount() check and validate Active Account', () => {
        let mockAccounts = [
          {
            homeAccountId: '',
            environment: '',
            tenantId: '',
            username: '',
            localAccountId: '',
          },
        ];
        (
          Object.getOwnPropertyDescriptor(msalServiceMock, 'instance').get as any
        ).and.returnValue(component.msalService);
        msalServiceMock.getAllAccounts.and.returnValue(mockAccounts as any);
        component.checkAndSetActiveAccount();
        expect(msalServiceMock.getAllAccounts).toHaveBeenCalled();
        expect(component.data).toEqual(mockAccounts);
      });
    });
    

    Stackblitz Demo