如何单元测试Observable.zip(observable1,observable2)

时间:2017-11-02 16:21:10

标签: angular unit-testing jasmine rxjs

我正在尝试对使用Observable.zip()

的组件进行单元测试

服务代码

正如您所看到的 BehaviorSubject 已使用 null 进行初始化,但在测试代码的 beforeAll 中,我强制他返回 Observable.of

export class ProfiloUtenteService extends BaseService<ProfiloDto> {
    public static readonly profiloKey = 'profiloUtente';

    private _$profilo = new BehaviorSubject<ProfiloDto>(null);
    public $profilo = this._$profilo.asObservable();

    protected get storedProfilo(): ProfiloDto {
        const profilo = this.storageService.retrieve(ProfiloUtenteService.profiloKey);
        return profilo ? profilo : null;
    }
    protected set storedProfilo(profilo: ProfiloDto) {
        this.storageService.store(ProfiloUtenteService.profiloKey, profilo);
        this._$profilo.next(profilo);
    }

    private emptyProfile = {
        addettoId: null,
        scope: Scope.none,
        selectedScope: Scope.none,
        ufficioId: null,
        provinciaUfficioId: null,
        addettoCognome: null,
        addettoNome: null
    };

    constructor(
        protected httpClient: HttpClient,
        protected appConfig: AppConfig,
        protected storageService: StorageService
    ) {
        super(httpClient, appConfig, appConfig.endpoints.addetto.api.baseUrl);

        const profilo = this.storedProfilo;
        if (profilo) {
            this._$profilo.next(profilo);
        }
    }

    public updateProfiloUtente(): void {
        const readProfilo = this.storedProfilo;
        if (readProfilo) {
            this._$profilo.next(readProfilo);
        }else {
            super.get((<AppConfig>this.appConfig).endpoints.addetto.api.routes.profilo)
                .takeLast(1)
                .do(profilo => {
                    if (!profilo) {
                        this._$profilo.next(this.emptyProfile);
                    }
                })
                .filter(profilo => !!profilo)
                .subscribe(profilo => {
                    profilo.selectedScope = Scope.all;
                    this.storedProfilo = profilo;
                });
        }
    }

    public setSelectedScope(scope: Scope) {
        const profilo = this.storedProfilo;
        if (profilo) {
            profilo.selectedScope = scope;
            this.storedProfilo = profilo;
        }
    }

    public setSelectedUfficioId(ufficioId: number, provinciaUfficioId?: number) {
        const profilo = this.storedProfilo;
        if (profilo) {
            profilo.ufficioId = ufficioId;
            profilo.provinciaUfficioId = provinciaUfficioId || null;
            this.storedProfilo = profilo;
        }
    }

    public logout() {
        this.storedProfilo = null;
    }
}

组件代码

这里爆炸了。当它订阅Observable.zip并尝试从中获取结果[1]时,此结果为NULL。

Observable.zip(
    this.ufficioService.getODataForCombo({ skip: 0 }),
    this.profiloUtenteService.$profilo)
    .takeWhile(() => this.isAlive)
    .subscribe(result => {
        result[0].forEach(office => this.availableOffices.push(office));

        // when this point is reached an error is thrown
        this.selectedOfficeId = result[1].ufficioId;             
        this.selectedOfficeDescription = this.availableOffices.find(office => office.id === this.selectedOfficeId).descrizione;
});

无法读取属性'ufficioId'为null

看起来压缩的observable的结果[1]不返回值。我也尝试用Observable.combineLatest切换Observable.zip,但没有任何结果。错误是一样的。

测试代码:

beforeAll(() => {
        ufficioServiceMock = new UfficioService(null, fixedAppConfig);
        spyOn(ufficioServiceMock, 'getODataForCombo').and.returnValue(Observable.of([]));

        profiloUtenteServiceMock = new ProfiloUtenteService(null, fixedAppConfig, null);
        profiloUtenteServiceMock.$profilo = Observable.of({
            addettoId: 1,
            ufficioId: 1,
            provinciaUfficioId: 1,
            scope: 1,

            addettoNome: 'string',
            addettoCognome: 'string',

            selectedScope:  1
        });
    });

新的测试版代码版本(将服务$ profilo更改为getter)

甚至将属性$ profilo转换为getter并监视它返回一个值,没有任何变化......

describe('PraticheSearchComponent', () => {
    let comp: PraticheSearchComponent;
    let fixture: ComponentFixture<PraticheSearchComponent>;
    let de: DebugElement;
    let el: HTMLElement;

    let ufficioServiceMock: UfficioService;
    let profiloUtenteServiceMock: ProfiloUtenteService;

    beforeAll(() => {
        ufficioServiceMock = new UfficioService(null, fixedAppConfig);
        spyOn(ufficioServiceMock, 'getODataForCombo').and.returnValue(Observable.of([]));

        profiloUtenteServiceMock = new ProfiloUtenteService(null, fixedAppConfig, null);
        spyOn(profiloUtenteServiceMock, '$profilo').and.returnValue(new BehaviorSubject({
            addettoId: 1,
            ufficioId: 1,
            provinciaUfficioId: 1,
            scope: 1,

            addettoNome: 'string',
            addettoCognome: 'string',

            selectedScope: 1
        }).asObservable());
    });
    //     profiloUtenteServiceMock.$profilo = new BehaviorSubject({
    //         addettoId: 1,
    //         ufficioId: 1,
    //         provinciaUfficioId: 1,
    //         scope: 1,

    //         addettoNome: 'string',
    //         addettoCognome: 'string',

    //         selectedScope: 1
    //     }).asObservable();
    // });

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                PraticheSearchComponent
            ],
            imports: [
                NgProgressModule,
                AuthModule.forRoot(),
                CustomHttpHeadersModule.forRoot(),

                SharedModule.forRoot(),

                ReactiveFormsModule,
                PatronatoSharedModule,
                FrameworkCoreModule.forRoot(),
                LoggerModule.forRoot(Level.LOG),
                MaterialModule,
                BrowserAnimationsModule,
                RouterTestingModule.withRoutes([])
            ],
            providers: [
                { provide: AppConfig, useValue: fixedAppConfig },
                { provide: LocalizationKeys, useValue: new LocalizationKeys() },
                { provide: ProfiloUtenteService, useValue: profiloUtenteServiceMock },
                { provide: NavbarService, useValue: new NavbarServiceMock() },
                { provide: PraticheSearchService, useValue: new PraticheSearchServiceMock() },
                { provide: UfficioService, useValue: ufficioServiceMock }
            ]
        }).compileComponents();

        fixture = TestBed.createComponent(PraticheSearchComponent);
        comp = fixture.componentInstance;
        de = fixture.debugElement;
        el = de.nativeElement;
    }));

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

我错过了什么吗?我从昨天早上起就遇到了这个问题,我即将把我的工作站撞到地板上。任何帮助都非常感谢(通过机器:P)

2 个答案:

答案 0 :(得分:1)

除了单元测试之外,在我看来,zip()不可能在结果[1]为空时发出结果,除非this.profiloUtenteService.$profilo发出空值。

快速测试,试试

this.profiloUtenteService.$profilo
  .filter(x => x)

编辑服务模拟

当我使用.and.returnValue时,我使用jasmine创建模拟,

const mockService  = jasmine.createSpyObj('ProfiloUtenteService ', ['$profilo']);
mockService.$profilo.and.returnValue(...)

您的代码也可以,但上面的内容对我来说很有用 请注意,有时不会创建依赖项,但不会引发错误。也许服务的基类导致失败,我无法在提供者列表中看到它。使用来自茉莉花的完全分离模拟将消除这种情况。

答案 1 :(得分:0)

我笨蛋

感谢所有想要解决这个问题的人。

解决方案:

我正在处理错误的规范文件。测试从 app.component.spec.ts 开始,但错误是从 pratiche.component.ts 所以我错误地认为错误在于 pratiche.component.spec.ts ,而 app.component.spec.ts 需要模拟 复制并将代码从文件粘贴到另一个修复所有内容