具有 observables 和 promise 的方法的单元测试

Unit test for method with observables and promise

我有这个方法,我正在尝试为其编写单元测试。 目前的测试只是以正确的顺序调用 spyOn 方法。

      handleSave() {
        this.saving = true;
        this.presenters.presentLoader({
          message: "Uploading..."
        });
        this.loginService.loginToFirebase().subscribe(() => {
          Promise.all(
            this.credential.inputs.map((input) => this.putStorageItem(input))
          )
          .then((url) => {
            this.presenters.dismissLoader();
            this.presenters.presentAlert({
              message: 'Successfully Uploaded Identification.',
              buttons: ['Ok']
            });
            this.presenters.dismissModal(true);
            this.saving = false
          })
          .catch((error) => {
            this.presenters.dismissLoader();
            this.presenters.presentAlert({
              message: 'Something went wrong.'
            });
            this.saving = false
          });
        })
      }

到目前为止,单元测试适用于 presentLoader 的初始调用,但随后的 dismissLoader(在 loginToFirebase 可观察对象中并且 Promise.all 不会被调用(至少在可观察对象和承诺解决之前不会)

在我每次模拟 putStorageItem 之前 component.putStorageItem = (item) => Promise.resolve(true); 在里面,我用这个模拟了 loginService。

    {
      provide: LoginService,
      useValue: {
        loginToFirebase: () => of(true)
      }
    },

我目前的测试

    it("call handle save should do stuff", () => {
        let presenters = TestBed.inject(PresentersService);
    
        spyOn(presenters, 'presentLoader');
        spyOn(presenters, 'dismissLoader');
        spyOn(presenters, 'presentAlert');
        spyOn(presenters, 'dismissModal');
    
        component.handleSave();
    
        expect(presenters.presentLoader).toHaveBeenCalled();
        expect(presenters.dismissLoader).toHaveBeenCalled();
        expect(presenters.presentAlert).toHaveBeenCalled();
        expect(presenters.dismissModal).toHaveBeenCalled();
    
      });

putStorageItem

      putStorageItem(input) {
        const fileExt = input.file.metadata.name.split('.').slice(-1);
        const filepath = ${input.label}.${fileExt}`;
        let task;
        if(input.file.metadata.isBase64) {
          task = this.fireStorage.ref(filepath).put(this.util.b64toBlob(input.file.img), {contentType:input.file.metadata.type, customMetadata:{originalName:input.file.metadata.name}});
        } else {
          task = this.fireStorage.ref(filepath).put(input.file.img, {contentType:input.file.metadata.type, customMetadata:{originalName:input.file.metadata.name}});
        }
        input.fileProgress = task.percentageChanges();
        return task.then((snapshot) => {
          console.log('One success:', input.file)
        }).catch((error) => {
          console.log('One failed:', input.file, error.message)
        });
      }

由于您正在使用 Observables and/or promises,您需要使用不同的技术进行测试。这些技巧是:

  • Jasmine
  • done回调
  • async/awaitfixture.whenStable() 共 Angular
  • waitForAsyncfixture.whenStable() 共 Angular
  • fakeAsynctick 共 Angular

您也可以在 Angular 中阅读有关异步测试的更多信息。

我最喜欢的是 fakeAsynctick,我认为它可以在这种情况下帮助您。

// !! wrap the test in a `fakeAsync` so you have better control of promises
it("call handle save should do stuff", fakeAsync(() => {
        let presenters = TestBed.inject(PresentersService);
    
        spyOn(presenters, 'presentLoader');
        spyOn(presenters, 'dismissLoader');
        spyOn(presenters, 'presentAlert');
        spyOn(presenters, 'dismissModal');
    
        component.handleSave();
        // !! Call tick() to tell the test that before running the
        // statements below the tick, 
        // ensure the promises in the component code have resolved
        // since the expect statements rely on them.
        tick();
    
        expect(presenters.presentLoader).toHaveBeenCalled();
        expect(presenters.dismissLoader).toHaveBeenCalled();
        expect(presenters.presentAlert).toHaveBeenCalled();
        expect(presenters.dismissModal).toHaveBeenCalled();
    
      }));

tick 也可用于确保可观察流的 subscribe 在继续之前已完成,但我主要将其用于承诺。