Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing firebase functions in vuejs

I want to unit test my vue components. Since I'm working with firebase this is a little bit difficult.

Fir of all, I created a __mocks__ folder to contain all my mocked functions. Inside that folder, I've created firebase.js:

import * as firebase from 'firebase';

const onAuthStateChanged = jest.fn();

const getRedirectResult = jest.fn(() => Promise.resolve({
  user: {
    displayName: 'redirectResultTestDisplayName',
    email: '[email protected]',
    emailVerified: true,
  },
}));

const sendEmailVerification = jest.fn(() => Promise.resolve('result of sendEmailVerification'));

const sendPasswordResetEmail = jest.fn(() => Promise.resolve());

const createUserWithEmailAndPassword = jest.fn(() => {
  console.log('heeeeelllo');
  Promise.resolve({
    user: {
      displayName: 'redirectResultTestDisplayName',
      email: '[email protected]',
      emailVerified: true,
    },
  });
});

const signInWithEmailAndPassword = jest.fn(() => Promise.resolve('result of signInWithEmailAndPassword'));

const signInWithRedirect = jest.fn(() => Promise.resolve('result of signInWithRedirect'));

const initializeApp = jest // eslint-disable-line no-unused-vars
  .spyOn(firebase, 'initializeApp')
  .mockImplementation(() => ({
    auth: () => ({
      createUserWithEmailAndPassword,
      signInWithEmailAndPassword,
      currentUser: {
        sendEmailVerification,
      },
      signInWithRedirect,
    }),
  }));

jest.spyOn(firebase, 'auth').mockImplementation(() => ({
  onAuthStateChanged,
  currentUser: {
    displayName: 'testDisplayName',
    email: '[email protected]',
    emailVerified: true,
  },
  getRedirectResult,
  sendPasswordResetEmail,
}));

firebase.auth.FacebookAuthProvider = jest.fn(() => {});
firebase.auth.GoogleAuthProvider = jest.fn(() => {});

This file, I took from: https://github.com/mrbenhowl/mocking-firebase-initializeApp-and-firebase-auth-using-jest

The component I want to test is called EmailSignupLogin. In this particular case, I want to test the registerViaEmail-method:

methods: {
    registerViaEmail() {
      if (this.password.length > 0 && this.password === this.passwordReenter) {
        firebase.auth().createUserWithEmailAndPassword(this.emailAdress, this.password).then((result) => {
          const { user } = result;
          console.log(result);
          this.setUser(user);
          this.$router.push('/stocks');
        }).catch((error) => {
          const errorCode = error.code;
          const errorMessage = error.message;
          this.error = errorMessage;
          console.error(errorCode, errorMessage);
        });
      } else {
        this.error = 'passwords not matching';
      }
    },
  },

Now to my test file(email-signup-login.spec.js):

import { mount } from '@vue/test-utils';
import Vue from 'vue';
import EmailSignupLogin from '@/components/email-signup-login';

jest.mock('../../__mocks__/firebase');

describe('EmailSignupLogin', () => {
  let wrapper;
  const mockFunction = jest.fn();

  beforeEach(() => {
    wrapper = mount(EmailSignupLogin, {
      data() {
        return {
          password: '123456',
          passwordReenter: '123456',
          emailAdress: '[email protected]',
        };
      },
      store: {
        actions: {
          setUser: mockFunction,
        },
      },
    });
  });

  describe('methods', () => {
    describe('#registerViaEmail', () => {
      it('calls mockFunction', async () => {
        await wrapper.vm.registerViaEmail();

        expect(mockFunction).toHaveBeenCalled();
      });
    });
  });
});

Inside the registerViaEmail-method I call the setUser-action, which is a vuex-action.

The problem is that it doesn't seem to call my mocked functions from __mocks__/firebase.js. Can somebody please tell me why?

like image 948
Ic3m4n Avatar asked Dec 05 '25 05:12

Ic3m4n


1 Answers

Several issues turned up in your code:

  1. registerViaEmail() is not async (not returning a Promise), so the await call returns prematurely, at which point your test tries to assert something that hasn't occurred yet. To resolve this, just wrap the function body with a Promise:
registerViaEmail() {
  return new Promise((resolve, reject) => {
    if (this.password.length > 0 && this.password === this.passwordReenter) {
      firebase.auth().createUserWithEmailAndPassword(this.emailAdress, this.password).then((result) => {
        //...
        resolve()
      }).catch((error) => {
        //...
        reject()
      });
    } else {
      //...
      reject()
    }
  })
},
  1. The script you referred to is not intended to be used with Jest __mocks__. The script itself directly modifies the firebase object, replacing its methods/properties with mocks. To use the script, you just need to import it before importing the test module that uses firebase:
import './firebase-mock' // <-- order important
import EmailSignupLogin from '@/components/EmailSignupLogin'
  1. createUserWithEmailAndPassword does not return anything. It looks like it originally returned the Promise, but you modified it with a console.log, and forgot to continue returning the Promise, which prevented this method from being awaited (same issue as #1). The solution there is to return the Promise:
const createUserWithEmailAndPassword = jest.fn(() => {
  console.log('heeeeelllo')
  return /*πŸ‘ˆ*/ Promise.resolve(/*...*/)
})
  1. createUserWithEmailAndPassword is the method to be tested in EmailSignupLogin, but it's currently not mocked in your auth mock object. It's only mocked in the return of initializeApp.auth, but that's not what it being used in EmailSignupLogin. To resolve the issue, copy createUserWithEmailAndPassword to your auth mock object:
jest.spyOn(firebase, 'auth').mockImplementation(() => ({
  onAuthStateChanged,
  currentUser: {
    displayName: 'testDisplayName',
    email: '[email protected]',
    emailVerified: true,
  },
  getRedirectResult,
  sendPasswordResetEmail,
  createUserWithEmailAndPassword, //πŸ‘ˆ
}));
  1. In your test setup, you mocked the store with a plain object, but it actually needs to be an instance of Vuex.Store:
mount({
  //store: { /*...*/ },              //❌DON'T DO THIS
  store: new Vuex.Store({ /*...*/ }) //βœ…
})

Github demo

like image 195
tony19 Avatar answered Dec 07 '25 17:12

tony19



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!