Angular2 单元测试使用 MdlSnackbarService 的组件

Angular2 unit testing a component that uses MdlSnackbarService

我有以下组件:

import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormGroup, FormControl, Validators} from "@angular/forms";
import {ValidationService} from "../../services/validation.service";
import {Router} from "@angular/router";
import {UsersService} from "../../services/users.service";
import {MdlSnackbarService} from "angular2-mdl";

@Component({
  selector: 'app-signup',
  templateUrl: 'signup.component.html',
  styleUrls: ['signup.component.css'],
  providers: [UsersService]
})
export class SignupComponent implements OnInit {

  form: FormGroup;

  constructor(private fb: FormBuilder,
              private router: Router,
              private usersService: UsersService,
              private mdlSnackbarService: MdlSnackbarService) {
    this.form = fb.group({
      "email": new FormControl("", [Validators.required, ValidationService.emailValidator]),
      "password": new FormControl("", Validators.required)
    });
  }

  ngOnInit() {
  }

  onSignup() {
    if (this.form.valid) {
      let email = this.form.value.email;
      let password = this.form.value.password;
      this.usersService.signup(email, password)
        .then(() => {
          this.router.navigate(['/app/home']);
        })
        .catch(err => {
          this.mdlSnackbarService.showToast(err);
        });
    }

  }

}

我正在尝试为此设置一些单元测试,但几个小时后我仍然无法 运行 最简单的测试(由 angular CLI 自动生成的测试):

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SignupComponent ],
      providers: [UsersService, AngularFire, MdlDialogOutletService],
      imports: [
        AngularFireModule.initializeApp(firebaseConfig),
        ReactiveFormsModule,
        CommonModule,
        RouterTestingModule.withRoutes([
          // { path: 'settings/:collection/edit/:item', component: DummyComponent }
        ])
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(SignupComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

我收到以下错误:No provider for MdlSnackbarService! 所以我所做的是将 MdlSnackbarService 添加到提供者 configureTestingModule:

providers: [UsersService, AngularFire, MdlDialogOutletService, MdlSnackbarService]

但是,我得到的错误是 Error: No component factory found for MdlSnackbarComponent. Did you add it to @NgModule.entryComponents? 我不知道如何解决。我还没有找到任何与单元测试相关的答案。

有谁知道如何解决这个问题?

您应该为所有这些服务使用模拟,即 RouterUserServiceMdlSnackbarService。您希望能够控制这些服务在测试期间执行的操作。您并不真正关心服务本身的作用。重要的是组件如何与它们交互。这就是你要测试的。

要设置模拟,您可以执行以下操作

let router;
let userService;
let snackbar;

beforeEach(() => {
   router = { navigate: jasmine.createSpy('navigate') };
   snackbar = { showToast: jasmine.createSpy('showToast') };
   userService = { signup: (email, pass) => null };

   TestBed.configureTestingModule({
     providers: [
       { provide: Router, useValue: router },
       { provide: UserService, useValue: userService },
       { provide: MdlSnackbarService, useValue: snackbar }
     ]
   });;
});

现在在您的测试中,您可以控制 UserService 的作用,即 return 一个成功的承诺,或 return 一个错误的承诺。这样您就可以测试组件对这两种情况的反应。

it('should navigate on success', async(() => {
  spyOn(userService, 'signup').and.returnValue(Promise.resolve());

  component.signup();

  fixture.whenStable().then(() => {
    expect(router.navigate).toHaveBeenCalledWith(['/app/home']);
  })
}))

it('should show toast on error', async(() => {
  spyOn(userService, 'signup').and.returnValue(Promise.reject('error'));

  component.signup();

  fixture.whenStable().then(() => {
    expect(snackbar.showToast).toHaveBeenCalledWith('error');
  })
}))

这基本上就是您想要测试组件的方式以及它与这些服务交互的方式。

就错误 Error: No component factory found for MdlSnackbarComponent. 而言,您需要将该组件添加到测试平台 declarations 中,就像您需要的任何其他组件一样。如果您不想这样做,只需通过使用相同的选择器制作一个虚拟组件并将 that 组件添加到 declarations.[=19= 来模拟该组件]