Раньше я писал несколько сообщений, используя Firebase и библиотеку AngularFire2. Библиотека AngularFire2 делает использование и интеграцию Firebase с вашими приложениями Angular очень увлекательным и простым занятием.

AngularFire2 также позволяет создавать приложения JAMStack, для которых требуется только интерфейс и вызовы различных служб Firebase (Auth, Database и т. Д.). После ознакомления с документацией по AngularFire2 README вы можете довольно легко приступить к работе. Оттуда просто нужно внедрить различные сервисы в ваши компоненты Angular.

Недавно я создал приложение Angular, которое использует AngularFire2. Приложение также использует Jest для модульного тестирования. Я узнал кое-что в процессе его создания и хотел поделиться для дальнейшего использования.

В этом посте я расскажу о моем приложении и некоторых основах настройки Jest. Я не собираюсь описывать начальную настройку AngularFire2, поскольку ее репозиторий на GitHub охватывает ее. Я также не собираюсь много говорить об интеграции Jest с Angular, за исключением того, что использую Angular Builder для Jest вместо Karma. Строители великолепны, поскольку позволяют использовать Angular CLI. Я кратко расскажу об этом, а также об использовании Jest в первом разделе.

Рестораны ReyRey’s

Приложение, о котором я расскажу, называется ReyRey’s Restaurants. Вы можете добраться до него, перейдя на https://www.reyreysrestaurants.com. Приложение представляет собой интересный способ отслеживать рестораны, которые вы посещаете в своем городе. Проект построен и размещен на Firebase и построен на AngularFire2 для подключения к службам аутентификации и баз данных. Я сделал его с открытым исходным кодом, и вы можете посмотреть исходный код на GitHub здесь.

Кроме того, я создал приложение, чтобы получить интересный способ отслеживать рестораны в моем городе и включить мою кошку (Рей) в один из моих проектов. У меня уже есть Chessie Choochoo для другого моего кота (Chestnut), поэтому я не хотел упускать из виду Рей (см. Https://www.chessiechoochoo.com).

Я установил некоторые документы, чтобы вы могли легко увидеть, как использовать приложение здесь. Основная предпосылка заключается в том, что вы создаете учетную запись, а затем добавляете рестораны в раздел Хочу пойти. Когда вы посещаете свой ресторан, вы можете продвигать его как был там и добавлять отзывы с комментариями, звездами и т. Д. Вот несколько скриншотов:

Настройка проекта

Как я уже упоминал в начале, двумя важными вещами в этом проекте были AngularFire2 и Jest.

Я не буду вдаваться в подробности, как настроить AngularFire2 с вашим проектом, поскольку README в их репозитории в значительной степени покрывает это. Я, тем не менее, порекомендую вам мой пост Как библиотека AngularFire делает Firebase похожим на Magic, поскольку в нем есть хороший набор инструкций для начала.

Для настройки Jest с вашим проектом есть несколько способов сделать это. Я обнаружил, что использование Jest Builder было для меня самым простым вариантом. Помимо инструкций в README, я также сделал следующее:

Самое интересное в использовании компоновщика заключалось в том, что я смог использовать существующий интерфейс командной строки Angular для выполнения этой работы. Таким образом, каждый раз, когда я звонил «ng test», он запускал средство выполнения тестов Jest, а не средство выполнения Karma, которое обычно используется по умолчанию.

Поиграв с ним, я должен сказать, что мне очень понравился Jest по следующим причинам:

  • Сообщения об ошибках и предупреждения легко понять.
  • Средство выполнения тестов дает вам более детальные параметры

Я не собираюсь вдаваться в подробности о Jest, потому что несколько других людей очень хорошо это осветили. Рекомендую прочитать пост Angular CLI:« ng test с Jest за 3 минуты (v2)». Также (хотя в статье не используются компоновщики) я рекомендую ознакомиться со статьей Интеграция Jest в приложение и библиотеку Angular, чтобы узнать больше о Jest с Angular. Наконец, Jest Getting Started Docs - отличное место, где можно найти примеры и более подробную информацию.

Тестирование Angularfire2 с помощью Jest

Обычно модульное тестирование библиотек с различными службами было довольно простым. Вы имитируете зависимости, которые вам нужно внедрить, и используете различные хуки (beforeEach, afterEach и т. Д.) Для обработки данных, которые вы тестируете.

С AngularFire2 у меня возник ряд проблем, пытаясь имитировать различные библиотеки из-за различных методов, которые мне нужно было обрабатывать для моих компонентов и т. Д. Это не было задокументировано так, как я надеялся, и потребовало довольно обширного поиска в Google. К счастью, я обнаружил проблему с GitHub здесь, в которой обсуждается добавление документации по тестированию в репозиторий проекта. В этом выпуске GitHub у этого ответа был отличный пример, который помог мне узнать, как это сделать для моего проекта.

В качестве небольшого отказа от ответственности, прежде чем я перейду к своим тестам, я хочу отметить, что есть еще много тестов, которые можно добавить. На момент написания этой статьи временные ограничения ограничили то, сколько я могу добавить. Я создал успешный набор тестов для классов обслуживания в моем проекте. Установку, которую я использовал для этих тестов, можно использовать и для компонентов, которые от них зависят.

Я написал набор классов обслуживания, которые выделяют сервисы AngularFire2 в свои собственные классы. Это упростило задачу, потому что тогда у меня было больше гибкости в именовании и в том, как я хотел использовать AngularFire2.

Основной процесс тестирования этих сервисов - создать заглушку и имитировать значения для методов библиотеки AngularFire2. Эти фиктивные значения фактически имитируют реальные значения, которые будут возвращены из методов службы Firebase.

Для Службы аутентификации у меня следующие настройки:

const credentialsMock = {
  email: '[email protected]',
  password: 'password'
};
 
const userMock = {
  uid: 'ABC123',
  email: credentialsMock.email
};
 
const createUserMock = {
  user: {
    uid: 'ABC123',
    email: credentialsMock.email
  }
};
 
const fakeAuthState = new BehaviorSubject(null);
 
const fakeSignInHandler = (email, password): Promise<any> => {
  fakeAuthState.next(userMock);
  return Promise.resolve(userMock);
};
 
const fakeCreateUserHandler = (email, password): Promise<any> => {
  fakeAuthState.next(createUserMock);
  return Promise.resolve(createUserMock);
};
 
const fakeSignOutHandler = (): Promise<any> => {
  fakeAuthState.next(null);
  return Promise.resolve();
};
 
const angularFireAuthStub = {
  authState: fakeAuthState,
  auth: {
    createUserWithEmailAndPassword: (email: string, password: string) =>
      fakeCreateUserHandler(email, password),
    signInWithEmailAndPassword: (email: string, password: string) =>
      fakeSignInHandler(email, password),
    signOut: () => fakeSignOutHandler()
  }
};

Затем в моем фактическом тестовом блоке describe я ссылаюсь на значение angularFireAuthStub здесь:

describe('AuthenticationService', () => {
  let service: AuthenticationService;
  let afAuth: AngularFireAuth;
 
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [{ provide: AngularFireAuth, useValue: angularFireAuthStub }]
    });
 
    service = TestBed.get(AuthenticationService);
    afAuth = TestBed.get(AngularFireAuth);
  });
 
  afterEach(() => {
    fakeAuthState.next(null);
  });

Затем в самих тестах я просто вызываю свои методы обслуживания и проверяю ответы на макеты и заглушки:

test('should call the create user with email and password successfully', async () => {
  const response = await service.createUserWithEmailAndPassword(
    credentialsMock.email,
    credentialsMock.password
  );
  expect(response).toBe(createUserMock.user.uid);
});

После того, как у меня была запущена и запущена служба аутентификации, я построил тесты для службы базы данных. Настройка была похожа на службу аутентификации и имела следующее:

let service: DatabaseService;
let savedValues = [];
 
const user = {
  uid: 'ABC123',
  firstName: 'first',
  lastName: 'last',
  email: '[email protected]'
};
 
const wgRestaurant: WgRestaurant = {
  id: '1234',
  uid: 'abc123',
  name: 'name',
  link: 'link',
  description: 'description',
  recorded: 1234
};
 
const btRestaurant: BtRestaurant = {
  id: '1234',
  uid: '5678',
  name: 'restaurant name',
  description: 'restaurant description',
  location: 'restaurant location',
  link: 'restaurant link',
  stars: 5,
  review: 'restaurant review',
  recorded: 1234
};
 
const fakeAddValueHandler = (value: any): Promise<any> => {
  return Promise.resolve(savedValues.push(value));
};
 
const deleteAddedValueHandler = (): Promise<any> => {
  return Promise.resolve((savedValues = []));
};
 
const firestoreStub = {
  collection: (name: string) => ({
    doc: (id: string) => ({
      valueChanges: () => new BehaviorSubject({ foo: 'bar' }),
      set: (d: any) => fakeAddValueHandler(d)
    })
  }),
  createId: () => {
    return new Promise((resolve, reject) => resolve('1234567890'));
  },
  doc: (idFirst: string) => ({
    collection: (name: string) => ({
      doc: (idSecond: string) => ({
        valueChanges: () => new BehaviorSubject({ foo: 'bar' }),
        set: (d: any) => fakeAddValueHandler(d),
        delete: () => deleteAddedValueHandler()
      })
    })
  })
};

Если вы заметили, я обычно использую массив всякий раз, когда значения сохраняются в базе данных Cloud Firestore. Очевидно, это нужно настроить для более детального тестирования. Я был очень озабочен базовым вызовом различных методов, поэтому оставил все как есть.

Я использовал beforeEach и afterEach для настройки тестов, как вы видите здесь:

beforeEach(() => {
  TestBed.configureTestingModule({
    providers: [{ provide: AngularFirestore, useValue: firestoreStub }]
  });
 
  service = TestBed.get(DatabaseService);
});
 
// clear out any saved values
afterEach(() => (savedValues = []));

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

test('should call add user successfully', async () => {
  await service.addUser(user);
  expect(savedValues.length).toEqual(1);
});

У меня большой опыт работы с Karma, поэтому я впервые по-настоящему играл с Jest. В целом я нашел его очень интуитивно понятным, и с ним было довольно легко работать. В частности, мне понравились предупреждения и сообщения, которые мне давал CLI. Они действительно помогли мне отработать то, что мне нужно для нормальной конфигурации, построения тестов и т. Д.

Заключительные мысли

Надеюсь, вам понравился этот пост, и вы тоже кое-что из него узнали. Мне очень нравится использовать AngularFire2 в моих проектах, потому что он позволяет легко интегрировать Firebase в приложения Angular. Также было здорово использовать Jest для модульного тестирования вместо Karma, как я всегда делал раньше. Мой проект здесь действительно охватывает лишь некоторые основы, и вы можете сделать гораздо больше как с AngularFire2, так и с Jest. Также я надеюсь, что вы заглянули в ReyRey’s Restaurants и, возможно, даже используете его, когда проверяете местные рестораны!

Спасибо за чтение! Следуйте за мной в Твиттере на @ AndrewEvans0102!

Первоначально опубликовано на https://rhythmandbinary.com 6 января 2020 г.