最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Test that an Angular observable emits a value or sequence - Stack Overflow

programmeradmin3浏览0评论

I have a service that creates an observable that emits values, and it's relatively easy to test that an emitted value is as expected.

For example:

describe('service', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({providers: [MyService]});
    });

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        service.values$.subscribe((value) => expect(value).toBeTruthy());
    }));
});

The above works to test the expected value, but it only works if the service actually emits a value. If you have the edge case where the service fails to emit a value, then the test itself actually passes and Jasmine logs the warning message "SPEC HAS NO EXPECTATIONS should be created".

I searched Google for a while trying to figure out how to catch this case as an error and came up with this approach.

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        let value;
        service.values$.subscribe((v) => value = v);
        expect(value).toBeTruthy();
    }));

The above works only for synchronous observables and feels like code smell to me. Another developer will see this and think it's a poor quality test.

So after thinking about this for a few days. I thought of using takeUntil() to force the observable to plete and then test the expected result then.

For example:

describe('service', () => {
    let finished: Subject<void>;

    beforeEach(() => {
        TestBed.configureTestingModule({providers: [MyService]});
        finished = new Subject();
    });

    afterEach(() => {
        finished.next();
        finishedplete();
    });

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        let value;
        service.changes$
            .pipe(
                takeUntil(finished),
                finalize(() => expect(value).toBeTruthy())
            )
            .subscribe((v) => value = v);
    }));
});

In the above example the value is being stored in a local variable and then the expected result is checked when the observable pletes. I force the pletion by using afterEach() with takeUntil().

Question:

Are there any side effects with my approach, and if so what would be the more Angular/Jasmine way of performing these kinds of tests. I am worried that you are not suppose to perform expect assertions during the afterEach() life-cycle call.

I have a service that creates an observable that emits values, and it's relatively easy to test that an emitted value is as expected.

For example:

describe('service', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({providers: [MyService]});
    });

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        service.values$.subscribe((value) => expect(value).toBeTruthy());
    }));
});

The above works to test the expected value, but it only works if the service actually emits a value. If you have the edge case where the service fails to emit a value, then the test itself actually passes and Jasmine logs the warning message "SPEC HAS NO EXPECTATIONS should be created".

I searched Google for a while trying to figure out how to catch this case as an error and came up with this approach.

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        let value;
        service.values$.subscribe((v) => value = v);
        expect(value).toBeTruthy();
    }));

The above works only for synchronous observables and feels like code smell to me. Another developer will see this and think it's a poor quality test.

So after thinking about this for a few days. I thought of using takeUntil() to force the observable to plete and then test the expected result then.

For example:

describe('service', () => {
    let finished: Subject<void>;

    beforeEach(() => {
        TestBed.configureTestingModule({providers: [MyService]});
        finished = new Subject();
    });

    afterEach(() => {
        finished.next();
        finished.plete();
    });

    it('should emit true', async(() => {
        const service = TestBed.get(MyService);
        let value;
        service.changes$
            .pipe(
                takeUntil(finished),
                finalize(() => expect(value).toBeTruthy())
            )
            .subscribe((v) => value = v);
    }));
});

In the above example the value is being stored in a local variable and then the expected result is checked when the observable pletes. I force the pletion by using afterEach() with takeUntil().

Question:

Are there any side effects with my approach, and if so what would be the more Angular/Jasmine way of performing these kinds of tests. I am worried that you are not suppose to perform expect assertions during the afterEach() life-cycle call.

Share Improve this question asked Dec 18, 2018 at 13:37 ReactgularReactgular 54.9k24 gold badges173 silver badges218 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

This seems overkill to me.

Jasmine offers a callback in its tests, you could simply use it ?

it('should X', doneCallback => {
  myObs.subscribe(res => {
    expect(x).toBe(y);
    doneCallback();
  });
});

If the callback isn't called, the test fails with a timeout exception (meaning no more test will run after this failed one)

发布评论

评论列表(0)

  1. 暂无评论