angulartypescriptunit-testingjasmine

Angular Unit Test - Mocking a function on a property


Updating with some new attempts

I've tried several different approaches, and I'm obviously missing something fundamental on how to avoid this error and mock out this function. I've updated the mock and spec.ts with what I'm currently working with.

I'm trying to mock/test SendVerificationMail() from my AuthService, which starts by calling the AngularFireAuth method currentUser which returns an angularFire User object, which you can then use to call sendEmailVerification(). I've tried a few different mocking methods, but I just end up with the errors below because it can't find user.sendEmailVerification

Error

    Error: Uncaught (in promise): TypeError: user.sendEmailVerification is not a function
    TypeError: user.sendEmailVerification is not a function

AuthService

  SendVerificationMail() {
    return this.afAuth.currentUser
      .then((user: any) => {user.sendEmailVerification(),})
      .then(() => {
        this.router.navigate(['verify-email-address']);
      });
  }

AngularFireAuth Mock File

import { User } from '../shared/interface/user';
import { of } from 'rxjs';
import { userCredential } from './mock_userCredential';

const authState: User = {
    uid: 'fakeuser',
    email: 'test@test.com',
    emailVerified: true
};

export const AngularFireAuthMock: any = {

    returnedUser: {

        sendEmailVerification: () => {
            return Promise.resolve()
        }

    },

    createUserWithEmailAndPassword: () => {
        return Promise.resolve(userCredential)
    },

    signOut: () => {
        return Promise.resolve()
    },

    signInWithEmailAndPassword: () => {
        return Promise.resolve()
    },

    sendPasswordResetEmail: () => {
        return Promise.resolve()
    },

    get authState() {
        return of(authState);
    },

    get user() {
        return this.authState;
    },

    get currentUser() {
        return Promise.resolve(userCredential)
    }
}

auth.service.spec.ts

  it('SendVerificationMail() - Functions', fakeAsync(() => {

    // let returnedUser: any = {
    //   sendEmailVerification: () => {
    //     return Promise.resolve()
    //   }
    // }

    // let returnedUser = {
    //   sendEmailVerification: jasmine.createSpy('Promise.resolve()')
    // }

    // spyOn(returnedUser, 'sendEmailVerification').and.callThrough()

    spyOn(AngularFireAuthMock.returnedUser, 'sendEmailVerification').and.callThrough()

    tick()

    service.SendVerificationMail()

  }));

Solution

  • I am not sure what userCredential is but I would do the following:

    get currentUser() {
            return Promise.resolve({
             sendEmailVerification: () => Promise.resolve(null);
            });
        }
    

    Make currentUser return a promise that's an object where it resolves to a promise of null.

    Edit: To assert sendEmailVerification, do the following:

    // Eventually, sendEmailVerificationSpy will be a Spy function
    let sendEmailVerificationSpy: jasmine.Spy;
    
    ...
    // Assign sendEmailVerificationSpy to be a spy function
    sendEmailVerificationSpy = jasmine.createSpy();
    
    // And then modify `get currentUser()` to be:
    
    
    get currentUser() {
      // Call the spy on sendEmailVerification.
      return Promise.resolve({
        sendEmailVerification: () => sendEmailVerificationSpy(),
      });
    }
    
    ...
    
    // And then, assert that it was called.
    expect(sendEmailVerificationSpy).toHaveBeenCalled();
    

    You have to be strategic in when you declare and assign the spy function.