【问题标题】:Angular 6 - Unit testing a subscribe function in constructorAngular 6 - 在构造函数中对订阅函数进行单元测试
【发布时间】:2019-04-15 01:34:30
【问题描述】:

我一直在尝试对该服务的订阅功能进行单元测试。并且查看istanbul生成的代码覆盖率报告,我可以看到这段代码没有被覆盖。

代码

layout.component.ts

import {Component, HostListener, Input} from '@angular/core';

import { LayoutService } from './layout.service';

import { some } from 'lodash';

@Component({
    selector: 'cgm-layout',
    templateUrl: './layout.component.html',
    styleUrls: ['./layout.component.scss'],
    providers: [LayoutService]
})
class LayoutComponent {

    message: any;

    constructor(
        private service: LayoutService
    ) {
        service.messagePublished$.subscribe(
            message => {
                this.setMessage(message);
            }
        );
    }

    setMessage(message): void {
        this.message = message;
        setTimeout(() => {
            this.message = null;
        }, 7000);
    }

}

export {
    LayoutComponent
};

这是我的单元测试

layout.component.spec.ts

import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { of } from 'rxjs';
import { LayoutComponent } from './layout.component';
import { LayoutService } from './layout.service';

describe('LayoutComponent', () => {
    let component: LayoutComponent;
    let fixture: ComponentFixture<LayoutComponent>;
    let service;

    beforeEach(async(() => {
        service = new LayoutService();
        mockLayoutService = jasmine.createSpyObj('LayoutService', ['messagePublished$']);
        TestBed.configureTestingModule({
            declarations: [
                LayoutComponent,

            ],
            providers: [
                LayoutService
            ],
            schemas: [
                NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA
            ]
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(LayoutComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();

        component.message = 'Garbage';
    });

    it('should call messagePublished', () => {
        spyOn(service.messagePublished$, 'subscribe');
        TestBed.createComponent(LayoutComponent);

        expect(service.messagePublished$.subscribe).toHaveBeenCalled();
    });

    describe('setMessage', () => {

        it('should set the Message', fakeAsync(() => {
            component.setMessage('Message');

            expect(component.message).toBe('Message');
            tick(7000);
            expect(component.message).toBeNull();
        }));

    });
});

所以代码似乎永远不会超过“service.messagePublished$.subscribe”部分。这是code coverage report

我得到的错误是'Expected spy subscribe to has been called',我猜这是你在没有覆盖该代码块时得到的错误。

【问题讨论】:

    标签: javascript angular unit-testing karma-jasmine


    【解决方案1】:

    我建议您将订阅从构造函数移至ngOnInit。 Angular 创建了几个生命周期钩子,这些钩子在创建组件 (ngOnInit) 时调用,以及在数据更改或销毁时调用 - 请参阅 Angular lifecycle hooks

    这样您就可以通过调用ngOnInit() 方法来测试您的代码。

    如果您无法更改代码,您可以尝试创建一个组件实例并检查您的方法是否像下面的伪代码一样被调用:

    import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
    import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
    import { of } from 'rxjs';
    import { LayoutComponent } from './layout.component';
    import { LayoutService } from './layout.service';
    
    describe('LayoutComponent', () => {
        let component: LayoutComponent;
        let fixture: ComponentFixture<LayoutComponent>;
        let serviceSpy: jasmine.SpyObj<LayoutService>;;
    
        beforeEach(async(() => {
            const spy = spyOn(service.messagePublished$, 'subscribe')
            TestBed.configureTestingModule({
                declarations: [
                    LayoutComponent,
    
                ],
                providers: [
                    { provide: LayoutService, useValue: spy }
                ],
                schemas: [
                    NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA
                ]
            })
                .compileComponents();
                serviceSpy = TestBed.get(ValueService);
        }));
    
        beforeEach(() => {
            fixture = TestBed.createComponent(LayoutComponent);
            component = fixture.componentInstance;
            fixture.detectChanges();
    
            component.message = 'Garbage';
        });
    
        it('should call messagePublished', () => {
            TestBed.createComponent(LayoutComponent);
    
            expect(service.messagePublished$.subscribe).toHaveBeenCalled();
        });
    
    });
    

    【讨论】:

    • 这是个好主意,但我现在只负责单元测试。所以我不能真正改变代码。我必须想办法在构造函数中访问代码。
    • 更新了测试构造函数测试的代码。希望它能让您了解如何检查代码是否已运行。
    • 这是因为您在这一行中将 LayoutService 替换为 mock:provide: LayoutService, useValue: mockLayoutService 并且 mockLayoutService 没有 messagePublished$ 的属性。 mockLayoutService 需要是一个具有属性 messagePublished$ 的对象,该属性具有 subscribe 属性。
    • 代码已更新。我希望它能给你一个更清晰的画面。
    • 实例化你的服务 replace service = new LayoutService(); with service = TestBed.get(LayoutService);在 configureTestingModule() 方法之后
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2010-09-26
    • 2012-06-28
    • 2018-09-14
    • 2022-11-23
    • 2019-08-20
    • 1970-01-01
    相关资源
    最近更新 更多