【问题标题】:How to Unti Test Angular Components Depending on Firebase Service如何根据 Firebase 服务对 Angular 组件进行单元测试
【发布时间】:2021-06-01 14:32:30
【问题描述】:

我有一个简单的 Create-User-Component 想要进行单元测试。

该组件依赖于具有一些方法的 Auth Service,例如“登录、注销”等。

身份验证服务同时导入 AngularFireAuth 和 AngularFireStore。它是处理数据库交互的服务。

问题是我无法运行我的测试,因为我得到一个空注入器错误(如下所示)。

我想模拟任何数据库交互,以便我可以单独测试组件。

我尝试了各种方法,监视 auth 服务,模拟其方法,甚至模拟 firestore 依赖项,但在我面临的特定错误方面没有任何进展。

在下面的代码版本中,我注入了 auth 服务的存根版本,该服务应该在调用其方法之一时进行控制台日志,但不会发生日志记录。

为什么会出现这个错误?如何正确模拟我的 firebase 依赖项以便测试我的组件?

我的代码的最小化示例

组件

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/core/services/auth.service';

@Component({
  selector: 'app-create-user',
  templateUrl: './create-user.component.html',
  styleUrls: ['../credentials.component.scss', './create-user.component.scss'],
})
export class CreateUserComponent {

  public createUserForm: FormGroup;
  public message: string;

  constructor(
    private fb: FormBuilder,
    private auth: AuthService,
    private router: Router
  ) {
    this.createUserForm = this.fb.group({
      firstName: ['some name', Validators.required],
      lastName: ['some name2', Validators.required],
      email: ['some.fake@mail.com', Validators.compose([Validators.required, Validators.email])],
      password: ['password123', Validators.required]
    });
    this.message = '';
  }

  onSubmit(){
    if(!this.createUserForm.valid) {
      return;
    }
    this.auth.createNewUser(this.createUserForm.value)
    .then(async () => {
      this.router.navigate(['/credentials/verify-email']);
    })
    .catch(async (e) => console.error(e));
  }
}

测试

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { FormBuilder, FormsModule } from '@angular/forms';
import { AuthService } from 'src/app/core/services/auth.service';
import { CreateUserComponent } from './create-user.component';

fdescribe('CreateUserComponent', () => {
  let component: CreateUserComponent;
  let fixture: ComponentFixture<CreateUserComponent>;

  let authStub = {
    constructor: () => {console.log('constructor called')},
    login: () => {console.log('login called')},
    logout: () => {console.log('logout called')},
    createNewUser: () => {console.log('createNewUser called')},
  }


  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [ CreateUserComponent ],
      providers: [ FormBuilder, 
        { provide: AuthService, usevalue: authStub},
        ],
      imports: [ FormsModule, AngularFireModule, AngularFirestoreModule ],
    }).compileComponents();

    fixture = TestBed.createComponent(CreateUserComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

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

服务

import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';
import { Router } from '@angular/router';
import firebase from 'firebase/app';
import { UserRegistration } from '../types/user.types';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(
    private auth: AngularFireAuth,
    private firestore: AngularFirestore,
    private router: Router
  ) {
  }

  login(email: string, password: string): Promise<firebase.auth.UserCredential> {
    return this.auth.signInWithEmailAndPassword(email, password);
  }

  logout() {
    this.auth.signOut()
    .then(() => this.router.navigate(['/credentials/login']));
  }

  createNewUser(user: UserRegistration): Promise<void | firebase.auth.UserCredential> {
    return this.auth.createUserWithEmailAndPassword(user.email, user.password)
    .then((res: firebase.auth.UserCredential) => {
      res.user && res.user.sendEmailVerification();
      this.firestore.collection('users')
      .add({
        firstName: user.firstName,
        lastName: user.lastName,
        email: res.user ? res.user.email : user.email,
        createdAt: new Date(),
      });
    });
  }
}

我得到的错误

Chrome 90.0.4430.212 (Windows 10) CreateUserComponent 应该创建失败 失败:R3InjectorError(DynamicTestModule)[AuthService -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]: NullInjectorError:没有 InjectionToken angularfire2.app.options 的提供者! 错误属性:对象({ ngTempTokenPath:null,ngTokenPath:['AuthService','AngularFireAuth','InjectionToken angularfire2.app.options','InjectionToken angularfire2.app.options']}) NullInjectorError:R3InjectorError(DynamicTestModule)[AuthService -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]: NullInjectorError:没有 InjectionToken angularfire2.app.options 的提供者! 在 NullInjector.get (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11077:1) 在 R3Injector.get (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11243:1) 在 R3Injector.get (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11243:1) 在 injectInjectorOnly (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:4728:1) 在 Module.ɵɵinject (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:4732:1) 在 Object.AngularFireAuth_Factory [作为工厂] (node_modules/@angular/fire/ivy_ngcc/fesm2015/angular-fire-auth.js:127:152) 在 R3Injector.hydrate (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11412:1) 在 R3Injector.get (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11232:1) 在 injectInjectorOnly (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:4728:1) 在 ɵɵinject (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:4732:1) 错误:预期未定义是真实的。 在 在用户上下文。 (src/app/views/credentials/create-user/create-user.component.spec.ts:35:23) 在 ZoneDelegate.invoke (node_modules/zone.js/fesm2015/zone.js:372:1) 在 ProxyZoneSpec.onInvoke (node_modules/zone.js/fesm2015/zone-testing.js:287:1) Chrome 90.0.4430.212(Windows 10):执行 1 of 37(1 FAILED)(跳过 36)(0.199 秒 / 0.129 秒) 总计:1 次失败,0 次成功

【问题讨论】:

标签: angular typescript firebase unit-testing mocking


【解决方案1】:

将usevalue 更改为useValue(大写V),我认为您应该很高兴。

【讨论】:

  • 哇,这确实有帮助。至少我现在遇到了新的错误。谢谢你快速的回复。我发现 usevalue 不会触发任何智能感知或编译错误,这有点令人惊讶。
猜你喜欢
  • 2019-01-24
  • 2016-05-21
  • 1970-01-01
  • 1970-01-01
  • 2019-11-02
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
相关资源
最近更新 更多