1

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.

1 Answer 1

1

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)

  });
5
  • Thank you for such a quick response. I think you're on to something with needing to spy on the original service, and not the mock. Either of your two versions above allow me to use returnValue(null) without TS errors, but unfortunately both options also give me the same 'Error: : Property userData does not have access type get' so it doesn't quite work. I've also tried to use several variations of createSpyObj. With createSpyObj, it doesn't error but it doesn't actually overwrite anything either. jasmine.createSpyObj('AuthService',{},{ userData: (null) }) Commented Jul 10 at 3:52
  • 1
    @RustyShackleford updated my answer! stackblitz Commented Jul 10 at 3:58
  • So that was actually my very original approach, I was just setting the field to null directly like you updated, with AuthServiceMock.userData = null. What I found was that for some reason setting that to null persisted outside of the individual test. With the way the tests are randomized each time, if it puts the 'not logged in' test before the 'logged in' test then logged in fails. I tried to set userData to a new object before the 'logged in' test, but that doesn't seem to work either. Kind of a weird issue that drove me to looking into the spyOnProperty in the first place. Commented Jul 10 at 4:14
  • 1
    Alright, that last update was exactly what I needed! When I updated the mock file with the get and set, I was able to use any of the other 'spyOnProperty' implementations without getting an error. Interestingly enough it seems to work with any of the variations. This is the one I ended up with: it('Text Rendered - Not Logged In', () => { const compiled = fixture.nativeElement as HTMLElement; spyOnProperty(AuthServiceMock, 'userData', 'get').and.returnValue(null) fixture.detectChanges(); expect(compiled.querySelector('#confirmText')?.textContent).toBeUndefined(); }); Commented Jul 10 at 4:45
  • 1
    Just looked at your stackblitz update and it reflects what I'm seeing too, this was exactly the solution I was looking for. I appreciate you sticking with me through several questions! Commented Jul 10 at 4:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.