Andy Furniss
Andy Furniss

Reputation: 3914

Jasmine spyOn service when testing Angular component

I'm trying to test my Angular component with Jasmine. The component is a simple form that submits some search criteria to a service which then goes off and does the Http stuff and returns an array of entities.

I am using Jasmine to 'spyOn' the service method and then return a mock entity. This mock entity should then be saved in a variable in the component.

The problem I am facing is that when I come to assert that the entity has been successfully returned, I am getting undefined in the entities variable which makes me think I haven't set up my spy correctly or something similar.

Any help will be greatly appreciated!

Service:

@Injectable()
export class DynamicsSearchService {
    private apiUrl = '/api/DynamicsSearch/Search';
    private headers = new Headers({ 'Content-Type': 'application/json' });

    constructor(private http: Http) { }

    search(search: DynamicsSearch): Promise<any[]> {
        search.fields = this.getDefaultFields(search.entity);
        return this.http
            .post(this.apiUrl, JSON.stringify(search), { headers: this.headers })
            .toPromise()
            .then((response) => { return this.extractResults(search.entity, response.json()); })
            .catch(this.handleError);
    }

    ...
}

Component:

@Component({
    selector: 'dynamics-search-component',
    templateUrl: 'dynamics-search.component.html'
})
export class DynamicsSearchComponent {
    ...

    entities: any[];

    constructor(private searchService: DynamicsSearchService) { }

    submitSearch() {
        this.searching = this.searched = true;
        this.searchService.search(this.model)
            .then(results => {
                this.entities = results;
                this.searching = false;
                this.searchSuccessful = results !== null && results.length > 0;
            });
    }

    ...
}

Test:

describe('DynamicsSearchComponent', () => {

    let fixture: ComponentFixture<DynamicsSearchComponent>;
    let component: DynamicsSearchComponent;

    let configuration = new Configuration();

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [
                FormsModule,
                SharedModule
            ],
            providers: [
                BaseRequestOptions,
                MockBackend,
                DynamicsSearchService,
                Configuration,
                {
                    provide: Http,
                    useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
                        return new Http(backend, defaultOptions);
                    },
                    deps: [
                        MockBackend,
                        BaseRequestOptions
                    ]
                }
            ],
            declarations: [
                DynamicsSearchComponent
            ]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(DynamicsSearchComponent);
        component = fixture.componentInstance;
    });

    it('on submit should get a single contact',
        inject([DynamicsSearchService], (service: DynamicsSearchService) => {
            var expected = [
                {
                    contactid: 'A7806F57-002C-403F-9D3B-89778144D3E1'
                }
            ];

            const spy = spyOn(service, 'search')
                .and.returnValue(Promise.resolve(expected));            

            component.model = new DynamicsSearch('contacts', 'A7806F57-002C-403F-9D3B-89778144D3E1', null, 'contactid');
            component.submitSearch();

            fixture.detectChanges();

            expect(spy.calls.count()).toBe(1, `expected service search method to be called once but was called ${spy.calls.count()} times`);
            expect(component.entities).toBeDefined('no entities returned');
            expect(component.entities.length).toBe(1, `expected 1 entity to be returned but only ${component.entities.length} were returned`);
        }
    ));
});

It fails on the second expect because component.entities is undefined.

Upvotes: 0

Views: 3737

Answers (1)

Julia Passynkova
Julia Passynkova

Reputation: 17889

You are working with Promise that is async code. Put expect into fixture.whenStable func and add async function into 'it' unit test.

fixture.whenStable().then(() => {
    expect(component.entities).toBeDefined('no entities returned');
});

Upvotes: 2

Related Questions