【问题标题】:Angular 2 Observable and Promise callback unit testingAngular 2 Observable 和 Promise 回调单元测试
【发布时间】:2017-03-22 02:34:47
【问题描述】:

我在 Angular 2 服务中有一个方法,它获取特定用户帐户的所有详细信息 - 已登录的用户帐户 - 并返回一个 Observable。

(我使用 AngularFire2 进行身份验证,物有所值)

如您所见,getData 方法使用了getAuth 方法,该方法返回身份验证状态(作为可观察对象),而getToken 方法(返回一个 Promise)又使用该方法来获取用于填充 Authorization 标头并执行 http 请求的令牌。 (我知道我的代码可能需要重构,如果有任何反馈,我将不胜感激)

getData(): Observable<IData> {
    let authHeaders = new Headers();

    return Observable.create((o: Observer<IData>) => {
        this.getAuth().subscribe((authState: IState) => {
            this.getToken(authState).then(token => {
                /* ...
                 * Do things with that token, call an http service etc.
                 */ ...
                authHeaders.set('Authorization', token);

                this.http.get('myendpoint/', {
                    headers: this.authHeaders
                })
                .map((response: Response) => response.json())
                .map((data: IData) => {
                    o.next(data);
                });
            })
            .catch((error: Error) => {
                Observable.throw(new Error(`Error: ${error}`));
            });
        });
    });
}

我是单元测试的新手,一直在尝试测试这种方法,但我仍然无法覆盖该方法中 Promise 的.catch

这是我的单元测试的样子:

describe('Service: UserDataService', () => {
    let mockHttp: Http;
    let service: UserDataService;
    let getAuthSpy: jasmine.Spy;
    let getTokenSpy: jasmine.Spy;

    beforeEach(() => {
        mockHttp = { get: null } as Http;

        TestBed.configureTestingModule({
            providers: [
                { provide: Http, useValue: mockHttp },
                AngularFire,
                UserDataService,
            ]
        });

        service = new UserDataService(AngularFire, mockHttp);

        spyOn(mockHttp, 'get').and.returnValue(Observable.of({
            json: () => {
                "nickname": "MockNickname"
            }
        }));

        getAuthSpy = spyOn(service, 'getAuth').and.returnValue(Observable.of({
          "auth": {
            "uid": "12345"
        }));

        getTokenSpy = spyOn(service, 'getToken').and.returnValue(new Promise((resolve, reject) => {
            resolve('test promise response');
        }));
    });

    describe('getLead', () => {
        beforeEach(() => {
            spyOn(service, 'getLead').and.callThrough();
        });

        it('should return an object of user data and set the dataStore.userEmail if state exists', () => {
            service.getLead().subscribe(res => {
                expect(res).toEqual(jasmine.objectContaining({
                    nickname: 'MockNickname'
                }));
            });

            expect(service.getLead).toHaveBeenCalled();
        });

        it('should throw, if no authState is provided', () => {
            getAuthSpy.and.returnValue(Observable.of(false));

            service.getLead();

            expect(service.getLead).toThrow();
        });

        it('should throw, if getToken() fails to return a token', () => {
            getTokenSpy.and.returnValue(new Promise((resolve, reject) => {
                reject('test error response');
            }));

            /*
             * This is where I am getting lost
             */

            service.getLead();

            expect(service.getLead).toThrow();
        });
    });
});

据我了解,我必须以某种方式从 getToken 拒绝该 Promise,并测试我的 observable 是否会抛出。

非常感谢任何帮助或反馈。

【问题讨论】:

  • spyOn(...).and.returnValue(Observable.throw(...).toPromise())?您可以使用Observable.map 使其更加线性化,逐步而不是增加嵌套,根据需要转换fromPromisetoPromise

标签: javascript unit-testing angular jasmine rxjs


【解决方案1】:

我既不使用 jasmine 也不使用 AngularFire2,所以我不知道你到底要做什么,但从你的代码中可以明显看出 this.getToken(authState) 返回一个 Promise

问题来自这个 Promise 类来自哪个库(也就是您使用的是什么 polyfill)。

据我所知,Promises/A 的实现应该使用 try - catch 包装回调调用,因此如果您在 then(...) 中的回调抛出异常,Promise 将传播到 catch()。所以我猜你可以用一些格式错误的 JSON 伪造来自this.http.get('myendpoint/') 的响应,这会导致response.json() 出现错误。

第二件事是catch()的内部回调:

.catch((error: Error) => {
    Observable.throw(new Error(`Error: ${error}`));
});

这实际上没有任何作用。没有 return 语句,Observable.throw() 需要出现在 Observable 链中才能做某事。请注意,这个 .catch() 方法来自您的 Promise 类,而不是来自 Observable。

如果您希望它实际将错误传播到观察者,那么您必须显式调用:

.catch((error: Error) => {
    o.error(error);
});

也许如果错误冒泡到Observable.create(),它也会传递给观察者,我对此不确定。

【讨论】:

    【解决方案2】:

    我有类似的问题,然后我使用了 Observable.from:

    spyOn(service, 'function').and.returnValue(Observable.fromPromise(Promise.reject({})));
    

    你在 Promise.reject 中返回错误

    【讨论】:

      猜你喜欢
      • 2018-04-11
      • 1970-01-01
      • 2017-01-13
      • 2016-04-09
      • 2021-12-23
      • 1970-01-01
      • 2017-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多