首页 > 解决方案 > Jasmine createSpyObj 服务未注册调用

问题描述

在阅读了每篇关于 Jasmine 间谍没有注册呼叫的帖子之后,这是我遇到这个问题的代码:

describe( 'shared.FormAutocompleteComponent', () => {
    let component: FormAutocompleteComponent;
    let fixture: ComponentFixture<FormAutocompleteComponent>;
    let displayCtrl: FormControl;

    const mockService = jasmine.createSpyObj( 'dynSearchService', ['search', 'lookup'] );
    mockService.lookup.and.returnValue( of( { unlocode: 'ZRH/CH', name: 'Zürich' } ) );
    mockService.search.and.returnValue( of( [{ unlocode: 'ZRH/CH', name: 'Zürich' }] ) );

    beforeEach( async(() => {
        TestBed.configureTestingModule( {
            declarations: [FormAutocompleteComponent],
            providers: [{ provide: DynamicSearchService, useValue: mockService }],
            imports: [
                NoopAnimationsModule, ReactiveFormsModule,
                RouterTestingModule.withRoutes( [] ),
                MatInputModule, MatAutocompleteModule, MatIconModule
            ]
        } )
            .compileComponents();
    } ) );

    beforeEach(() => {
        displayCtrl = new FormControl();
        const group = new FormGroup( {} );
        group.addControl( 'foobar', displayCtrl );

        fixture = TestBed.createComponent( FormAutocompleteComponent );
        component = fixture.componentInstance;
        component.config = {
            name: 'foobar',
            formControl: 'text',
            label: 'My foobar',
            lookupApi: 'location',
            searchApi: 'locations'
        };
        component.group = group;
        fixture.detectChanges();
    } );

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

    fit( 'should correctly extend single term to wildcard', async(() => {
        // arrange
        displayCtrl.setValue( 'zrh' );
        fixture.detectChanges();
        // act
        component.search();
        // assert
        expect( mockService.search ).toHaveBeenCalled();
        expect( mockService.search ).toHaveBeenCalledWith( 'zrh*', 'locations' );
    } ) );
} );

DynamicSearchService 被注入到 FormAutocompleteComponent 中,并且在组件调用 DynamicSearchService#search 之前直接放置一个 console.log 表明该方法正在被调用。另一个 console.log 显示返回的结果与火车 Jasmine 间谍的结果相匹配。

3396 (Windows 10 0.0.0)]: Connected on socket rlx5hC9uWlNSUz0pAAAA with id 73910165
LOG: 'callDynSearchService with displayCtrl.value=zrh'
LOG: 'callDynSearchService with displayCtrl.value=zrh'
LOG: 'About to run <search> on service with term zr* '
LOG: 'About to run <search> on service with term zr* '
LOG: 'Got results: [{"unlocode":"ZRH/CH","name":"Zürich"}]'
LOG: 'Got results: [{"unlocode":"ZRH/CH","name":"Zürich"}]'
Chrome 67.0.3396 (Windows 10 0.0.0) shared.FormAutocompleteComponent should correctly extend single term to wildcard FAILED
        Expected spy dynSearchService.search to have been called.
            at <Jasmine>

运行测试

const srv = fixture.debugElement.injector.get( DynamicSearchService );
expect( mockService ).toEqual( srv );

成功,剩下的唯一原因似乎是 .search() 调用从未记录在间谍上。我究竟做错了什么?

编辑,2018 年 6 月 23 日

更改为定义非间谍 mockService(仍然注入'useValue'),然后

fit( 'should correctly extend single term to wildcard', async(() => {
    // arrange
    let injectedService = TestBed.get( DynamicSearchService );
    spyOn( injectedService, 'search' ).and.callThrough();
    spyOn( injectedService, 'lookup' ).and.callThrough();
    displayCtrl.setValue( 'zrh' );
    fixture.detectChanges();
    // act
    component.search();
    // assert
    expect( injectedService.search ).toHaveBeenCalled();
} ) );

调试现在显示组件中的服务与“injectedService”是同一个对象。spyOn 所做的代理更改也会显示在组件服务上。但是,Jasmine 仍然记录“搜索”从未被调用过。

标签: angularservicejasmine

解决方案


我猜原因是对 Observable 的 debounceTime() 调用,一些 console.log 语句显示,尽管 async() 和 fixture.whenStable(),但搜索仅在expect() 断言运行后执行。

最终通过切换到 fakeAsync 并使用 tick() 解决了这个问题。


推荐阅读