Как вы издеваетесь над методами Firebase Firestore, используя Jest? - PullRequest
0 голосов
/ 27 августа 2018

У меня есть ряд функций, каждая из которых выполняет различные взаимодействия в пожарном депо. Как я могу использовать Jest, чтобы издеваться над звонками из пожарного магазина? Я хотел бы избежать использования библиотеки.

Когда я использую jest.mock("firebase/app") и jest.mock("firebase/firestore") и другие варианты, я либо получаю нулевые TypeErrors, либо ошибки, указывающие на то, что я все еще ссылаюсь на фактический импорт, а не на макет: Error: ... make sure you call initializeApp().

Например, простую функцию, которую я хочу проверить:

import firebase from "firebase/app";
import "firebase/firestore";

export const setDocData = (id, data) => {
  const newDoc = {
    created: firebase.firestore.FieldValue.serverTimestamp(),
    ...data
  };
  firebase
    .firestore()
    .doc("docs/" + id)
    .set(newDoc);
};

Обратите внимание на то, как Firebase импортируется как обычно, затем Firestore импортируется с побочным эффектом. Также обратите внимание на то, как firestore вызывается сначала как функция, а затем упоминается как свойство. Я считаю, что это источник моей проблемы.

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Вот как я издевался над огненной базой для шутки.

'use strict'

const collection = jest.fn(() => {
  return {
    doc: jest.fn(() => {
      return {
        collection: collection,
        update: jest.fn(() => Promise.resolve(true)),
        onSnapshot: jest.fn(() => Promise.resolve(true)),
        get: jest.fn(() => Promise.resolve(true))
      }
    }),
    where: jest.fn(() => {
      return {
        get: jest.fn(() => Promise.resolve(true)),
        onSnapshot: jest.fn(() => Promise.resolve(true)),
      }
    })
  }
});

const Firestore = () => {
  return {
    collection
  }
}

Firestore.FieldValue = {
  serverTimestamp: jest.fn()
}

export default class RNFirebase {

  static initializeApp = jest.fn();

  static auth = jest.fn(() => {
    return {
      createUserAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
      sendPasswordResetEmail: jest.fn(() => Promise.resolve(true)),
      signInAndRetrieveDataWithEmailAndPassword: jest.fn(() => Promise.resolve(true)),
      fetchSignInMethodsForEmail: jest.fn(() => Promise.resolve(true)),
      signOut: jest.fn(() => Promise.resolve(true)),
      onAuthStateChanged: jest.fn(),
      currentUser: {
        sendEmailVerification: jest.fn(() => Promise.resolve(true))
      }
    }
  });

  static firestore = Firestore;

  static notifications = jest.fn(() => {
    return {
        onNotification: jest.fn(),
        onNotificationDisplayed: jest.fn(),
        onNotificationOpened: jest.fn()
    }
  });

  static messaging = jest.fn(() => {
    return {
        hasPermission: jest.fn(() => Promise.resolve(true)),
        subscribeToTopic: jest.fn(),
        unsubscribeFromTopic: jest.fn(),
        requestPermission: jest.fn(() => Promise.resolve(true)),
        getToken: jest.fn(() => Promise.resolve('RN-Firebase-Token'))
    }
  });

  static storage = jest.fn(() => {
    return {
      ref: jest.fn(() => {
        return {
          child: jest.fn(() => {
            return {
              put: jest.fn(() => Promise.resolve(true))
            }
          })
        }
      })
    }
  })

}
0 голосов
/ 10 декабря 2018

Я использовал метод инжекции зависимостей для компонентов, и это означало, что я мог бы смоделировать и протестировать методы без всего шаблона.

Например, у меня есть компонент формы, который обрабатывает приглашения следующим образом:

import React, { useEffect } from 'react';
import { Formik } from 'formik';
import { validations } from '../../helpers';
import { checkIfTeamExists } from '../helpers';

const Invite = ({ send, userEmail, handleTeamCreation, auth, db, dbWhere }) => {
  useEffect(() => {
    checkIfTeamExists(send, dbWhere);
  }, []);
  return (
      <Formik
        initialValues={{ email: '' }}
        onSubmit={values =>
          handleTeamCreation(userEmail, values.email, db, auth, send)
        }
        validate={validations}
        render={props => (
          <form onSubmit={props.handleSubmit} data-testid="form">
            <input
              type="email"
              placeholder="Please enter your email."
              onChange={props.handleChange}
              onBlur={props.handleBlur}
              value={props.values.email}
              name="email"
            />
            {props.errors.email && (
              <p className="red" data-testid="error">
                {props.errors.email}
              </p>
            )}
            <button type="submit">Submit</button>
          </form>
        )}
      />
  );
};

export default Invite;

Метод checkIfTeamExists основан на аутентификации firebase, а метод handleTeamCreation записывает данные в пожарное хранилище.

Когда я ссылался на компонент в его родительском элементе, он создавал его так:

<Invite
 send={send}
 userEmail={value.user.user.email}
 handleTeamCreation={handleTeamCreation}
 auth={auth.sendSignInLinkToEmail}
 db={db.collection('games')}
 dbWhere={db.collection('games')
            .where('player1', '==', value.user.user.email)
            .get}
 />

Затем, используя react-testing-library, в своих тестах я смог смоделировать вещи простым jest.fn().

test('Invite form fires the send function on Submit ', async () => {
  const handleTeamCreation = jest.fn();
  const send = jest.fn();
  const userEmail = 'ex@mple.com';
  const db = jest.fn();
  const auth = jest.fn();
  const dbWhere = jest.fn().mockResolvedValue([]);
  const { getByPlaceholderText, getByTestId } = render(
    <Invite
      send={send}
      userEmail={userEmail}
      handleTeamCreation={handleTeamCreation}
      auth={auth}
      db={db}
      dbWhere={dbWhere}
    />
  );
  const inputNode = getByPlaceholderText('Please enter your email.');
  const email = 'me@gmail.com';
  fireEvent.change(inputNode, { target: { value: email } });
  const formNode = getByTestId('form');
  fireEvent.submit(formNode);
  await wait(() => {
    expect(handleTeamCreation).toHaveBeenCalledWith(
      userEmail,
      email,
      db,
      auth,
      send
    );

    expect(handleTeamCreation).toHaveBeenCalledTimes(1);
  });
});

и издевались над пожарным магазином, где запрашивали аналогичным образом.

test('Invite form must contain a valid email address', async () => {
  const send = jest.fn();
  const db = jest.fn();
  const dbWhere = jest.fn().mockResolvedValue([]);

  const { getByPlaceholderText, queryByTestId } = render(
    <Invite send={send} db={db} dbWhere={dbWhere} />
  );
  expect(queryByTestId('error')).not.toBeInTheDocument();
  const inputNode = getByPlaceholderText('Please enter your email.');
  const email = 'x';
  fireEvent.change(inputNode, { target: { value: email } });

  await wait(() => {
    expect(queryByTestId('error')).toHaveTextContent('Invalid email address');
  });
});

Это очень просто, но работает. Это также довольно многословно, но я подумал, что реальный вариант использования будет более полезным, чем надуманный пример. Надеюсь, это кому-нибудь поможет.

0 голосов
/ 27 августа 2018

Вот решение, которое я нашел. В Интернете не так много информации об этом, поэтому я надеюсь, что это кому-нибудь поможет.

Хитрость заключается в том, чтобы создать связанный API фиктивных функций и установить его на объекте firebase вместо импорта и насмешки над firestore. Приведенный ниже пример позволяет мне протестировать приведенную выше примерную функцию, а также doc().get() обещания.

const docData = { data: "MOCK_DATA" };
const docResult = {
  // simulate firestore get doc.data() function
  data: () => docData
};
const get = jest.fn(() => Promise.resolve(docResult));
const set = jest.fn();
const doc = jest.fn(() => {
  return {
    set,
    get
  };
});
const firestore = () => {
  return { doc };
};
firestore.FieldValue = {
  serverTimestamp: () => {
    return "MOCK_TIME";
  }
};

export { firestore };

Я объявляю его в файле, который запускается до выполнения всех моих тестов (см. Документы), и импортирую и использую его в моих тестовых файлах, например так:

import firebase from "firebase/app";
import { firestore } from "../setupTests";
firebase.firestore = firestore;

describe("setDocData", () => {
  const mockData = { fake: "data" };
  beforeEach(() => {
    jest.clearAllMocks();
    setDocData("fakeDocID", mockData);
  });

  it("writes the correct doc", () => {
    expect(firestore().doc).toHaveBeenCalledWith("docs/fakeDocID");
  });

  it("adds a timestamp, and writes it to the doc", () => {
    expect(firestore().doc().set).toHaveBeenCalledWith({
      created: "MOCK_TIME",
      fake: "data"
    });
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...