Search code examples
angulartypescriptunit-testingjasmine

Angular Unit Testing - Setting a Property in Mock to Null (spyOnProperty)


I'm having trouble doing a negative test of an ngIf statement that looks at a property on my authService.

I have a mock provider already, and the positive version of this test works great. I can see that the HTML is pulling the data from my mock and correctly reflecting it in the DOM. If I change a value in the mock, I can fail the test.

What I haven't been able to do is to write a test to set the userData object from the mock to 'null' so that the ngIf fails and I can confirm the selector is undefined. If I change the actual mock for userData to (null) that works, but I'm not quite understanding how to spy on the mock and return a different value in a test.

HTML to Be Tested

<div class="fs-4 text-secondary" *ngIf="authService.userData as user">
  <p id="confirmText">We have sent a confirmation email to <strong>{{user.email}}</strong>.</p>
  <p>Please check your email and click on the link to verfiy your email address.</p>
</div>

AuthServiceMock

import { User } from "../shared/interface/user";

const authState: User = {
  uid: 'fakeuser',
  email: '[email protected]',
  emailVerified: true
};

export const AuthServiceMock = {

  ForgotPassword: () => {
    return
  },

  SignUp: () => {
    return
  },

  userData: (authState)

};

Basic Test That Passes

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [VerifyEmailAddressComponent],
      providers: [{ provide: AuthService, useValue: AuthServiceMock }]
    })
      .compileComponents();

    fixture = TestBed.createComponent(VerifyEmailAddressComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

it('Text Rendered - Logged In', () => {
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('#confirmText')?.textContent).toContain('We have sent a confirmation email to [email protected].');
  });

Because I'm going just going after a property (userData) in the mock, and not a method spyOn() gives me errors, so I assumed I should use 'spyOnProperty', but I can't even get that to return let alone a null value:

SpyOnProperty Attempt, I just wanted to see if I could manipulate the return values

  it('Text Rendered - Not Logged In', () => {
    const compiled = fixture.nativeElement as HTMLElement;
    
    spyOnProperty(AuthServiceMock, 'userData', 'get').and.returnValue({
      uid: '1234',
      email: '[email protected]',
      emailVerified: false
    })
    //expect(compiled.querySelector('#confirmText')?.textContent).toBeUndefined();

    console.log(AuthServiceMock.userData)

  });

Running this produces the following error:

Error: : Property userData does not have access type get

I've tried a couple of other things, but I haven't been able to find a solution to this one yet. Any help in how i can return null for the userData object from my mock would be most appreciated.


Solution

  • UPDATE:

    You can directly create a get property for mocking it

    const AuthServiceMock = {
    
      ForgotPassword: () => {
        return
      },
    
      SignUp: () => {
        return
      },
      _authState: authState,
      get userData() {
        return this._authState;
      },
    
      set userData(value: any) {
        this._authState = value;
      }
    
    };
    

    Stackblitz showing no errors


    I think you need to spy on the instance of a class and not on the actual class, you can try either.

    it('Text Rendered - Not Logged In', () => {
        const compiled = fixture.nativeElement as HTMLElement;
        const component = fixture.componentInstance; 
        spyOnProperty(component.authService, 'userData', 'get').and.returnValue({
          uid: '1234',
          email: '[email protected]',
          emailVerified: false
        })
        console.log(omponent.authService.userData)
    
      });
    

    Or you can try.

    it('Text Rendered - Not Logged In', () => {
        const compiled = fixture.nativeElement as HTMLElement;
        const authService = TestBed.inject(AuthService);
        spyOnProperty(authService, 'userData', 'get').and.returnValue({
          uid: '1234',
          email: '[email protected]',
          emailVerified: false
        })
        console.log(authService.userData)
    
      });