简体   繁体   English

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

[英]How to unit test Observable.zip(observable1, observable2)

I'm trying to unit test a component that uses a Observable.zip() 我正在尝试对使用Observable.zip()的组件进行单元测试

Service code 服务代码

As you can see the BehaviorSubject is initialized with null but in the beforeAll of the test code I force him to return an Observable.of 如您所见, 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;
    }
}

Component code 组件代码

Here it blows up. 在这里,它炸毁了。 When it subscribes to the Observable.zip and tries to fetch the result[1] from it, this result is NULL. 当它订阅Observable.zip并尝试从中获取result [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;
});

Cannot read property 'ufficioId' of null 无法读取null的属性“ ufficioId”

It looks like the result[1] of the zipped observable doesn't return a value. 看起来可压缩的observable的result [1]没有返回值。 I also tried to switch Observable.zip with Observable.combineLatest, but without any result. 我也尝试用Observable.combineLatest切换Observable.zip,但没有任何结果。 The error is the same. 错误是一样的。

The testing code: 测试代码:

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
        });
    });

New testbed code version (changed the service $profilo into a getter) 新的测试平台代码版本(将服务$ profilo更改为getter)

Even converting the property $profilo into a getter and spying on it returning a value, nothing changes... 即使将$ profilo属性转换为吸气剂并监视返回值,也没有任何变化...

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();
    });
});

Am I missing something? 我想念什么吗? I'm on this problem since yesterday morning and I'm about to crash my workstation on the floor. 自昨天早上以来,我就一直在处理此问题,而我的工作站正要崩溃。 Any help is really appreciated (by the machine :P) 真的很感谢任何帮助(通过机器:P)

Leaving aside the unit test, seems to me it's impossible for zip() to emit result where result[1] is null, unless this.profiloUtenteService.$profilo is emitting a null. 撇开单元测试,在我看来, zip()不可能在result [1]为null的情况下发出结果,除非this.profiloUtenteService.$profilo发出的是null。

Quick test, try 快速测试,尝试

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

Edit for service mock 编辑服务模拟

When I use .and.returnValue I create the mock with jasmine, 当我使用.and.returnValue我用茉莉花创建了模拟,

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

Your code may be ok too, but above works for me. 您的代码也可以,但是上面的代码对我有用。
Note that sometimes a dependency doesn't get created, but no error is raised. 请注意,有时不会创建依赖项,但不会引发错误。 Perhaps the base class of the service is causing a fail, I can't see it in the providers list. 服务的基类可能导致失败,我在提供程序列表中看不到它。 Using a completely detach mock from jasmine will eliminate that. 使用完全独立的茉莉花模拟可以消除这种情况。

Me dumb 我笨

Thanks to everyone who tried to figure this out. 感谢所有试图弄清楚这一点的人。

Solution: 解:

I was working on the wrong spec file. 我正在处理错误的规格文件。 The tests where starting from app.component.spec.ts but the error was thrown from the pratiche.component.ts so I wrongly thought that the error were in the pratiche.component.spec.ts while the mock were required by the app.component.spec.ts Copy and pasting the code from a file to another fixed everything 测试从app.component.spec.ts开始,但是错误是从pratiche.component.ts引发的,所以我错误地认为错误是在pratiche.component.spec.ts中,应用程序需要模拟。 component.spec.ts将代码从文件复制并粘贴到另一个固定的内容

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM