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