Angular 角度单元测试-如何从注入服务触发另一个发射?

Angular 角度单元测试-如何从注入服务触发另一个发射?,angular,unit-testing,jasmine,angular-test,Angular,Unit Testing,Jasmine,Angular Test,我有一个组件可以执行以下操作: 在构造函数中,它获取查询参数并将其解析为给定的季度和年度 在ngOnInit中,它订阅了一个返回季度和年份列表的服务。在订阅的回调中,它使用季度和年度的值为查询参数中选择的季度和年度赋值,如果它们无效,则为列表中的第一个 在我的单元测试中,我可以设置参数(或者至少设置快照),但我希望在subscribe方法中重新触发回调并断言一些期望。我不知道如何在subscribe方法中重新触发回调 以下是我的组件的相关代码: @Component({ ... })

我有一个组件可以执行以下操作:

  • 在构造函数中,它获取查询参数并将其解析为给定的季度和年度

  • 在ngOnInit中,它订阅了一个返回季度和年份列表的服务。在订阅的回调中,它使用季度和年度的值为查询参数中选择的季度和年度赋值,如果它们无效,则为列表中的第一个

  • 在我的单元测试中,我可以设置参数(或者至少设置快照),但我希望在subscribe方法中重新触发回调并断言一些期望。我不知道如何在subscribe方法中重新触发回调

    以下是我的组件的相关代码:

    @Component({
        ...
    })
    export class MyComponent implements OnInit {
    quarter: number;
    year: number;
    paramQuarter: string;
    selectedQuarter: IQuarter;
    quartersList: IQuarter[];
    
    constructor(
                private quarterService: QuarterService,
                private activatedRoute: ActivatedRoute,
                private router : Router) {
    
        // if no query param is set, set as the current quarter and year from moment.js
        this.paramQuarter = this.activatedRoute.snapshot.queryParams['quarter'] 
            || moment().quarter() + '-' + moment().year();
    
    }
    
    ngOnInit(): void {
    
        this.quarterService.getQuarters().subscribe(response => {
            this.quartersList = response;
            /// ----------------------------------
            /// The code I want to test is in here
            /// ----------------------------------
            const [quarter, year] = this.paramQuarter.split("-").map(string => parseInt(string));
            // get the correct quarter based on URL params, or the current one if the url param is malformed
            this.selectedQuarter: IQuarter = this.quartersList
                .filter(q => q.quarter === quarter && q.year === year)[0]
                || this.quartersList[0];
        });
    }
    
    }
    
    以及测试的结果:

    const MOCK_QUARTERS = [
    {
        quarter: 2,
        year: 2020,
        startDate: '2020-04-01',
        endDate: '2020-06-30',
    },
    {
        quarter: 1,
        year: 2020,
        startDate: '2020-01-01',
        endDate: '2020-03-31',
    }
    ]
    
    describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;
    let quarterService: QuarterService;
    let router : Router;
    let activatedRoute : ActivatedRoute;
    
    const MockQuarterService = {
        getQuarters: jasmine.createSpy('getQuarters').and.returnValue(
        of(MOCK_QUARTERS)
        )
    }
    
    beforeEach(async(() => {
        TestBed.configureTestingModule({
        declarations: [
            MyComponent,
        ],
        imports: [
            SharedModule,
            RouterTestingModule,
            ...HttpClientTestingImports,
        ],
        providers: [
            ...HttpClientTestingProviders,
            { provide: QuarterService, useValue: MockQuarterService },
            {
            provide: ActivatedRoute,
            useValue: {
                queryParams: of({ quarter: '2-2020' }),
                snapshot: {
                queryParams: { quarter: '2-2020' }
                }
            }
            },
        ]
        })
        .compileComponents();
    }));
    
    beforeEach(() => {
        fixture = TestBed.createComponent(MyComponent);
        component = fixture.componentInstance;
        quarterService = TestBed.inject(QuarterService);
        router = TestBed.inject(Router);
        activatedRoute = TestBed.inject(ActivatedRoute);
        fixture.detectChanges();
    });
    
    fit('should set the current quarter from correctly formed URL params', async(() => {
        // set from the params in the beforeEach callback
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[0]);
        spyOn(component, 'updateQuarterSelection').and.callThrough();
    
        let actRoute : ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
        actRoute.queryParams = of( {quarter: `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}` } );
        actRoute.snapshot.queryParams.quarter = `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}`;
        /// ----------------------------------
        // Somehow need the callback of the quarterService.getQuarters().subscribe() 
        // method to retrigger here
        /// ----------------------------------
        fixture.detectChanges();
        console.log(component);
        
        expect(quarterService.getQuarters).toHaveBeenCalledTimes(2);
        expect(component.updateQuarterSelection).toHaveBeenCalledTimes(2);
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    
    });
    
    const模拟宿舍=[
    {
    季度:2,
    年份:2020年,
    起始日期:“2020-04-01”,
    截止日期:“2020-06-30”,
    },
    {
    季度:1,
    年份:2020年,
    起始日期:“2020-01-01”,
    截止日期:“2020-03-31”,
    }
    ]
    描述('MyComponent',()=>{
    let组分:MyComponent;
    let夹具:组件夹具;
    让军需服务:军需服务;
    让路由器:路由器;
    让activatedRoute:activatedRoute;
    常量服务={
    getQuarters:jasmine.createSpy('getQuarters')和.returnValue(
    办公室(模拟宿舍)
    )
    }
    beforeach(异步(()=>{
    TestBed.configureTestingModule({
    声明:[
    MyComponent,
    ],
    进口:[
    共享模块,
    路由器测试模块,
    …HttpClientTestingImports,
    ],
    供应商:[
    …HttpClientTestingProviders,
    {provide:QuarterService,useValue:MockQuarterService},
    {
    提供:激活的路由,
    使用价值:{
    queryParams:of({quarter:'2-2020'}),
    快照:{
    查询参数:{quarter:'2-2020'}
    }
    }
    },
    ]
    })
    .compileComponents();
    }));
    在每个之前(()=>{
    fixture=TestBed.createComponent(MyComponent);
    组件=fixture.componentInstance;
    quarterService=TestBed.inject(quarterService);
    路由器=试验台。注入(路由器);
    activatedRoute=TestBed.inject(activatedRoute);
    fixture.detectChanges();
    });
    fit('应根据格式正确的URL参数设置当前季度',异步(()=>{
    //从beforeach回调中的参数设置
    expect(component.selectedQuarter).toEqual(模拟季度[0]);
    spyOn(组件'updateQuarterSelection')。和.callThrough();
    让actRoute:ActivatedRoute=fixture.debugElement.injector.get(ActivatedRoute);
    actRoute.queryParams=of({quarter:`${MOCK_QUARTERS[1]。quarter}-${MOCK_QUARTERS[1]。year}`});
    actRoute.snapshot.queryParams.quarter=`${MOCK_QUARTERS[1]。quarter}-${MOCK_QUARTERS[1]。year}`;
    /// ----------------------------------
    //不知何故,我们需要quarterService.getQuarters().subscribe()的回调
    //方法在此处重新触发
    /// ----------------------------------
    fixture.detectChanges();
    控制台日志(组件);
    期望(四分之一服务。获得四分之一)已被催缴时间(2);
    期望(component.updateQuarterSelection).tohavebeincalledtimes(2);
    expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    });
    
    要快速解锁,只需再次调用
    ngOnInit

    fit('should set the current quarter from correctly formed URL params', async(() => {
        // set from the params in the beforeEach callback
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[0]);
        spyOn(component, 'updateQuarterSelection').and.callThrough();
    
        let actRoute : ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
        actRoute.queryParams = of( {quarter: `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}` } );
        actRoute.snapshot.queryParams.quarter = `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}`;
        
        component.ngOnInit();    
    
        fixture.detectChanges();
        console.log(component);
        
        expect(quarterService.getQuarters).toHaveBeenCalledTimes(2);
        expect(component.updateQuarterSelection).toHaveBeenCalledTimes(2);
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    
    要获得更详细的答案,您调用的第一个
    fixture.detectChanges()
    是在调用
    ngOnInit
    时。因此,在调用构造函数(createComponent)之前,您可以对
    进行策略性的处理(我不展示这个方法,因为它需要更多的簿记)。我们可以利用
    BehaviorSubject
    重新触发一些订阅

    import { BehaviorSubject } from 'rxjs';
    ....
    // replace any with Quarter interface
    const mockQuarters = new BehaviorSubject<any>(MOCK_QUARTERS);
    const MockQuarterService = {
        getQuarters: () => mockQuarters, // return BehaviorSubject
    }
    
    ...
    beforeEach(async(() => {
        TestBed.configureTestingModule({
        declarations: [
            MyComponent,
        ],
        imports: [
            SharedModule,
            RouterTestingModule,
            ...HttpClientTestingImports,
        ],
        providers: [
            ...HttpClientTestingProviders,
            { provide: QuarterService, useValue: MockQuarterService },
            {
            provide: ActivatedRoute,
            useValue: {
                queryParams: of({ quarter: '2-2020' }),
                snapshot: {
                queryParams: { quarter: '2-2020' }
                }
            }
            },
        ]
        })
        .compileComponents();
    }));
    
    beforeEach(() => {
        fixture = TestBed.createComponent(MyComponent);
        component = fixture.componentInstance;
        quarterService = TestBed.inject(QuarterService);
        router = TestBed.inject(Router);
        activatedRoute = TestBed.inject(ActivatedRoute);
        fixture.detectChanges();
    });
    
    fit('should set the current quarter from correctly formed URL params', async(() => {
        // set from the params in the beforeEach callback
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[0]);
        spyOn(component, 'updateQuarterSelection').and.callThrough();
    
        let actRoute : ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
        actRoute.queryParams = of( {quarter: `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}` } );
        actRoute.snapshot.queryParams.quarter = `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}`;
    
        // this should retrigger the subscription
        mockQuarters.next(MOCK_QUARTERS);
        
        fixture.detectChanges();
        console.log(component);
        
        expect(quarterService.getQuarters).toHaveBeenCalledTimes(2);
        expect(component.updateQuarterSelection).toHaveBeenCalledTimes(2);
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    
    
    
    
    
    从'rxjs'导入{BehaviorSubject};
    ....
    //用四分之一接口替换任何接口
    const mockQuarters=新行为主体(MOCK_QUARTERS);
    常量服务={
    getQuarters:()=>mockQuarters,//返回行为主体
    }
    ...
    beforeach(异步(()=>{
    TestBed.configureTestingModule({
    声明:[
    MyComponent,
    ],
    进口:[
    共享模块,
    路由器测试模块,
    …HttpClientTestingImports,
    ],
    供应商:[
    …HttpClientTestingProviders,
    {provide:QuarterService,useValue:MockQuarterService},
    {
    提供:激活的路由,
    使用价值:{
    queryParams:of({quarter:'2-2020'}),
    快照:{
    查询参数:{quarter:'2-2020'}
    }
    }
    },
    ]
    })
    .compileComponents();
    }));
    在每个之前(()=>{
    fixture=TestBed.createComponent(MyComponent);
    组件=fixture.componentInstance;
    quarterService=TestBed.inject(quarterService);
    路由器=试验台。注入(路由器);
    activatedRoute=TestBed.inject(activatedRoute);
    fixture.detectChanges();
    });
    fit('应根据格式正确的URL参数设置当前季度',异步(()=>{
    //从beforeach回调中的参数设置
    expect(component.selectedQuarter).toEqual(模拟季度[0]);
    spyOn(组件'updateQuarterSelection')。和.callThrough();
    让actRoute:ActivatedRoute=fixture.debugElement.injector.get(ActivatedRoute);
    actRoute.queryParams=of({quarter:`${MOCK_QUARTERS[1]。quarter}-${MOCK_QUARTERS[1]。year}`});
    actRoute.snapshot.queryParams.quarter=`${MOCK_QUARTERS[1]。quarter}-${MOCK_QUARTERS[1]。year}`;
    //这将重新触发订阅
    模拟宿舍。下一个(模拟宿舍);
    fixture.detectChanges();
    控制台日志(组件);
    期望(四分之一服务。获得四分之一)已被催缴时间(2);
    期望(component.updateQuarterSelection).tohavebeincalledtimes(2);
    expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    
    要快速解锁,只需再次调用
    ngOnInit

    fit('should set the current quarter from correctly formed URL params', async(() => {
        // set from the params in the beforeEach callback
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[0]);
        spyOn(component, 'updateQuarterSelection').and.callThrough();
    
        let actRoute : ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
        actRoute.queryParams = of( {quarter: `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}` } );
        actRoute.snapshot.queryParams.quarter = `${MOCK_QUARTERS[1].quarter}-${MOCK_QUARTERS[1].year}`;
        
        component.ngOnInit();    
    
        fixture.detectChanges();
        console.log(component);
        
        expect(quarterService.getQuarters).toHaveBeenCalledTimes(2);
        expect(component.updateQuarterSelection).toHaveBeenCalledTimes(2);
        expect(component.selectedQuarter).toEqual(MOCK_QUARTERS[1]);
    }));
    
    要获得更详细的答案,请参见第一个
    夹具。