Angular 2服务主题的单元测试

14

我试图为一个具有Subject属性和调用.next()方法的angular服务编写测试。

该服务如下:

@Injectable()
export class SubjectService {
  serviceSubjectProperty$: Subject<any> = new Subject();

  callNextOnSubject(data: any) {
    this.serviceSubjectProperty$.next(data);
  }
}

以及该服务的测试文件:

import { TestBed, inject } from '@angular/core/testing';

import { SubjectService } from './subject.service';

describe('SubjectService', () => {

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        SubjectService
      ]
    });
  });

  it('callNextOnSubject() should emit data to serviceSubjectProperty$ Subject',
    inject([SubjectService], (subjectService) => {
      subjectService.callNextOnSubject('test');

      subjectServiceProperty$.subscribe((message) => {
        expect(message).toBe('test');
      })
  }));
});

即使我将subjectService.callNextOnSubject的参数从'test'更改为其他任何内容,测试始终会通过。

我还尝试使用asyncfakeAsync包装所有内容,但结果是相同的。

如何正确测试是否callNextOnSubject正在向serviceSubjectProperty$主题发出数据?


2
在调用该方法之前,你应该先订阅,因为该主题没有任何重播/缓冲行为。 - jonrsharpe
@jonrsharpe,这个可行,谢谢。 - user7332973
当你进行单元测试一个_service_时,不应该使用angular的测试基础设施。它只会让你的测试代码变得更长,并且容易出错。 - Aluan Haddad
@crdevdeu 如果其中一个答案是正确的 - 请标记它 ;) - pbialy
4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
20

我在寻找解决方案时发现了这篇文章:

http://www.syntaxsuccess.com/viewarticle/unit-testing-eventemitter-in-angular-2.0

对我很有帮助(它非常简短,不要害怕打开它)。

我在这里粘贴它,希望能帮助那些来到这个网站寻求答案的人。


关于提出的问题 - 我认为你需要更改:

  subjectService.callNextOnSubject('test');

  subjectServiceProperty$.subscribe((message) => {
    expect(message).toBe('test');
  })

  subjectServiceProperty$.subscribe((message) => {
    expect(message).toBe('test');
  })

  subjectService.callNextOnSubject('test');

所以首先订阅,然后再发出事件。

如果在订阅之前发出'test',那么没有任何东西会"捕获"该事件。


2
当您的主题被调用后,应该测试组件中更改的数据。应该仅测试公共变量,而不是私有或受保护的变量;例如: 服务:
@Injectable()
export class SomeService {
    onSomeSubject: Subject<any> = new Subject();

    someSubject(string: string) {
        this.onSomeSubject.next(string);
    }
}

组件:

export class SomeComponent {
    @Input() string: string;

    constructor(private service: SomeService) {
        service.onSomeSubject.subscribe((string: string) => {
            this.string = string;
        }); //don't forget to add unsubscribe.
    }
}

测试:

...
describe('SomeService', () => {
    let someService: SomeService; // import SomeService on top
    let someComponent: SomeComponent; // import SomeService on top

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [SomeService, SomeComponent]
        });
        injector = getTestBed();
        someService = injector.get(SomeService);
        someComponent = injector.get(SomeComponent);
    });

    describe('someSubject', () => {
        const string = 'someString';

        it('should change string in component', () => {
            someService.someSubject(string);
            expect(someComponent.string).tobe(string);
        });
    });
});

2
使用jasmine的'done'回调函数可以解决这个问题,具体请查看下面的文档:https://jasmine.github.io/api/edge/global(参见implementationCallback(doneopt))。 以下是使用你的测试用例的示例:
it('callNextOnSubject() should emit data to serviceSubjectProperty$ Subject', (done) => {
    inject([SubjectService], (subjectService) => {
      subjectService.callNextOnSubject('test');

      subjectServiceProperty$.subscribe((message) => {
        expect(message).toBe('test');
        done();
      })
  }) // function returned by 'inject' has to be invoked
});

嘿@mth khaled,解释得很好,但是我遇到了一个TypeError: Cannot read property 'subscribe' of undefined的问题。`fit('callNextOn_page() should emit page data', (done) => { inject([searchOrderSpy], (_page) => { searchOrderSpy._page(5) })searchOrderSpy._page.subscribe((page) => { expect(page).toBe(5); done(); }) })`下面的代码是服务中的实现:changePage(newPage: number) { this._page.next(newPage); } - Girum

0

订阅必须在前面,然后调用方法

it(`#${YourCompoent.prototype.yourMethod.name} subscribe must come before, and then call method`, fakeAsync( () => {
    yourServiceSubject.filter$.subscribe(ev => {
      expect(ev).toEqual(mock.mockFilterDefault())
    });
    component.sendFilterForm();
  }));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,