Тестирование функций firebase в vuejs - PullRequest
3 голосов
/ 26 февраля 2020

Я хочу провести модульное тестирование моих vue компонентов. Так как я работаю с Firebase, это немного сложно.

Во-первых, я создал папку __mocks__, которая будет содержать все мои проверенные функции. Внутри этой папки я создал firebase.js:

import * as firebase from 'firebase';

const onAuthStateChanged = jest.fn();

const getRedirectResult = jest.fn(() => Promise.resolve({
  user: {
    displayName: 'redirectResultTestDisplayName',
    email: 'redirectTest@test.com',
    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: 'redirectTest@test.com',
      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: 'test@test.com',
    emailVerified: true,
  },
  getRedirectResult,
  sendPasswordResetEmail,
}));

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

Этот файл я взял из: https://github.com/mrbenhowl/mocking-firebase-initializeApp-and-firebase-auth-using-jest

Компонент, который я хочу протестировать, называется EmailSignupLogin. В данном конкретном случае я хочу протестировать registerViaEmail -метод:

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';
      }
    },
  },

Теперь в моем тестовом файле (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: 'test@test.com',
        };
      },
      store: {
        actions: {
          setUser: mockFunction,
        },
      },
    });
  });

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

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

Внутри registerViaEmail -метод Я вызываю setUser -действие, которое является vuex-действием.

Проблема в том, что он не вызывает мои проверенные функции из __mocks__/firebase.js. Может кто-нибудь сказать, пожалуйста, почему?

1 Ответ

0 голосов
/ 03 марта 2020

В вашем коде обнаружено несколько проблем:

  1. registerViaEmail() не равно async (не возвращает Promise), поэтому вызов await преждевременно возвращается, и в этот момент ваш Тест пытается утверждать то, что еще не произошло. Чтобы решить эту проблему, просто оберните тело функции с 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()
    }
  })
},
Сценарий , на который вы ссылались, не предназначен для использования с Jest __mocks__. Сам скрипт непосредственно изменяет объект firebase, заменяя его методы / свойства на mocks. Чтобы использовать скрипт, вам просто нужно импортировать его перед импортом тестового модуля, который использует firebase:
import './firebase-mock' // <-- order important
import EmailSignupLogin from '@/components/EmailSignupLogin'
createUserWithEmailAndPassword ничего не возвращает. Похоже, что первоначально вернул Promise, но вы изменили его с помощью console.log и забыли продолжить возвращать Promise, что помешало этому методу await ed (та же проблема как № 1). Решение есть вернуть Promise:
const createUserWithEmailAndPassword = jest.fn(() => {
  console.log('heeeeelllo')
  return /*?*/ Promise.resolve(/*...*/)
})
createUserWithEmailAndPassword - это метод, который должен быть протестирован в EmailSignupLogin, но в настоящее время он не смоделирован в вашем auth макете объекта. Это только издевается над возвращением initializeApp.auth, но это не то, что используется в EmailSignupLogin. Чтобы решить эту проблему, скопируйте createUserWithEmailAndPassword в ваш auth макет объекта:
jest.spyOn(firebase, 'auth').mockImplementation(() => ({
  onAuthStateChanged,
  currentUser: {
    displayName: 'testDisplayName',
    email: 'test@test.com',
    emailVerified: true,
  },
  getRedirectResult,
  sendPasswordResetEmail,
  createUserWithEmailAndPassword, //?
}));
В вашей тестовой настройке вы издевались над магазином простым объектом, но на самом деле это должен быть экземпляр Vuex.Store:
mount({
  //store: { /*...*/ },              //❌DON'T DO THIS
  store: new Vuex.Store({ /*...*/ }) //✅
})

Github demo

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...