无法在页面上找到 firebase ui 小部件 - Angular 测试环境

问题描述

我正在为我的 Angular Web 应用程序编写测试,该应用程序包含一个带有 firebase UI 元素的页面。有两项测试,一项确保页面加载,一项确保 firebaseui 组件正确加载:

authentication.component.spec.ts

/*eslint-env jasmine*/
import { ComponentFixture,Testbed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { AuthenticationComponent } from './authentication.component';
import { FirebaseService } from '../services/firebase.service';

describe('AuthenticationComponent_Logged_Out',() => {
  let component: AuthenticationComponent;
  let fixture: ComponentFixture<AuthenticationComponent>;
  let service;

  beforeEach(async () => {
    await Testbed.configureTestingModule({
      declarations: [ AuthenticationComponent ],schemas: [ NO_ERRORS_SCHEMA ]
    }).compileComponents();
    service = Testbed.inject(FirebaseService);
    fixture = Testbed.createComponent(AuthenticationComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create',() => {
    expect(component).toBeTruthy();
  });

  it('should render auth ui',() => {
    const compiled = fixture.nativeElement;
    expect(compiled.querySelector("#firebaseui_auth_container")).toBeTruthy();
  });

  afterEach(async () => {
    service.ui.reset();
  });
});

并带有以下模板文件

<script src="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.js"></script>
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.css" />

<div id="authentication-wrapper">
    <h1>Please sign in below to access your quotes!</h1>
    <div id="firebaseui_auth_container"></div>
    <div id="loader">Loading...</div>
</div>

和一类:

import { Component } from '@angular/core';
import { FirebaseService } from '../services/firebase.service';

@Component({
  selector: 'app-authentication',templateUrl: './authentication.component.html',styleUrls: ['./authentication.component.css']
})
export class AuthenticationComponent {

  constructor (private fbService: FirebaseService) {
    sessionStorage.removeItem('displayed_random');
    // If user logged in,redirect to Feed
    if (fbService.currentUser) {
      window.location.href = "/Feed";
    } else {
      this.fbService.instantiateUi();
    }
  }
}

实际加载 firebase ui 的服务是:

firebase.service.ts

import { Injectable } from '@angular/core';
import firebase from "firebase/app";
import * as firebaseui from "firebaseui";
import { config } from './config';
import 'firebase/database';
import 'firebase/auth';
firebase.initializeApp(config);

@Injectable({
  providedIn: 'root'
})
export class FirebaseService {
  currentUser: string;
  auth = firebase.auth();
  ui = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(this.auth);

  constructor () {
    const username = sessionStorage.getItem('username');
    if (username) {
      this.currentUser = username;
    }
  }

  signoutUser () {
    this.auth.signOut();
    this.currentUser = undefined;
    if (sessionStorage.getItem('username')) {
      sessionStorage.removeItem('username');
    }
  }

  getRef (path) {
    return firebase.database().ref(path);
  }

  instantiateUi () {
    this.ui.start("#firebaseui_auth_container",{
      callbacks: {
        signInSuccessWithAuthResult: (authResult) => {
          // Save username in storage
          sessionStorage.setItem('username',authResult.user.displayName);
          return true;
        },uiShown: () => {
          // The widget is rendered,hide the loader.
          document.getElementById('loader').style.display = 'none';
        }
      },// Will use popup for IDP Providers sign-in flow instead of the default,redirect.
      signInFlow: 'popup',signInSuccessUrl: 'Feed',signInoptions: [
        {
          provider: firebase.auth.GoogleAuthProvider.PROVIDER_ID,customParameters: {
            prompt: 'select_account' // Forces account selection even when only one account is available.
          }
        },firebase.auth.EmailAuthProvider.PROVIDER_ID
      ]
    });
  }
}

现在,在正常的开发服务器环境(即 ng serve)中运行应用程序,UI 按预期创建。但是,在测试中,由于某种原因无法正常生成,并且无法创建UI。

enter image description here

enter image description here

我应该怎么做才能让测试框架找到 firebaseui auth 容器?我显然尝试多次注入服务并在每次测试后重置 UI,但没有任何乐趣。我猜这与 compiled 出于某种原因未定义有关,这也很奇怪,因为该逻辑适用于我的其他测试。

解决方法

根据您提供的文件,在这种情况下,我们可以采用几种方法来测试您的组件。我将模拟您的 FirebaseService,因为我们应该相信此外部依赖项运行良好。

我们应该编写 unite test 来锁定代码,如果我们源代码上的其他更改会破坏测试。

测试范围

我们应该编写最少的大约四个测试来锁定 àuthentication.component.ts

  • 它应该创建
  • 它应该是来自 sessionStorage 的display_random 值的空
  • 当 currentUser 为真时执行 location href。
  • 当 currentUser 为假时调用 fbService 函数。
constructor (private fbService: FirebaseService) {
    sessionStorage.removeItem('displayed_random');
    // If user logged in,redirect to feed
    if (fbService.currentUser) {     
      window.location.href = "/feed";
    } else {
      this.fbService.instantiateUi();
    }
  }

当前用户为真时

在您的authentication.component.spec.ts

describe('AuthenticationComponent with current user',() => {
beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ AuthenticationComponent ],providers: [
        {
          provide: FirebaseService,useValue: {
             currentUser: true,instantiateUi: () => null
          }
        }
      ],schemas: [ NO_ERRORS_SCHEMA ]
    }).compileComponents();
    service = TestBed.inject(FirebaseService);
    fixture = TestBed.createComponent(AuthenticationComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
it('should create',() => {
  expect(component).toBeTruthy();
});
it('should be session storage displayed_random empty on start',() => {
  expect(sessionStorage.getItem('displayed_random').toBeFalsy();
});
it('should change href value to feed',() => {
   expect(window.location.href).toBe('/feed');
});

当前用户为false时

在您的authentication.component.spec.ts

describe('AuthenticationComponent without current user',useValue: {
             currentUser: false,() => {
  expect(component).toBeTruthy();
});
it('should call instantiateUI when no current user',() => {
   const instantiateUiSpy = spyOn(service,'instantiateUi');
   expect(instantiateUiSpy).toHaveBeenCalled();
});