问题
So I'm learning how to test services in Angular and I tried to copy the below example in the Angular docs.
let httpClientSpy: { get: jasmine.Spy };
let heroService: HeroService;
beforeEach(() => {
// TODO: spy on other methods too
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
heroService = new HeroService(<any> httpClientSpy);
});
it('should return expected heroes (HttpClient called once)', () => {
const expectedHeroes: Hero[] =
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'expected heroes'),
fail
);
expect(httpClientSpy.get.calls.count()).toBe(1, 'one call');
});
I tried to copy it quite literally, but it gives me the following error:
ERROR in src/app/services/find-locals.service.spec.ts(17,38): error TS2304: Cannot find name 'asyncData'.
Can someone help me with replacing this? Or telling me where I might have done something wrong elsewhere?
Here is the test file that copied from the Angular docs:
import {FindLocalsService} from './find-locals.service';
import {HttpClient, HttpClientModule} from '@angular/common/http';
let findLocalsService: FindLocalsService;
let httpClientSpy: { get: jasmine.Spy, post: jasmine.Spy };
beforeEach(() => {
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post']);
findLocalsService = new FindLocalsService(<any> httpClientSpy, null);
});
it('should save location to server', function () {
const expectedData: any =
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
httpClientSpy.post.and.returnValue(asyncData(expectedData));
findLocalsService.saveLocation('something').subscribe(
data => expect(data).toEqual(expectedData),
fail
);
expect(httpClientSpy.post.calls.count()).toBe(1, 'one call');
});
Here is the service itself
@Injectable()
export class FindLocalsService {
constructor(private http: HttpClient, private authService: AuthenticationService){}
saveLocation(locationObj){
return this.http.post(url + '/findLocals/saveLocation', locationObj);
}
getThreeClosestPlayers() {
const userId = this.authService.currentUser().user._id;
console.log('entered 3 closest service', userId);
return this.http.get(url + '/findLocals/getThreeClosestPlayers/' + userId)
.pipe(
map((data: any) => data.obj),
catchError(this.handleError)
)
}
}
回答1:
Change this line:
httpClientSpy.get.and.returnValue(asyncData(expectedHeroes));
to use the Observable operator of()
httpClientSpy.get.and.returnValue(of(expectedHeroes));
This will return an observable that can be subscribed to and will return expectedHeroes. If you are using Angular 6, you can import this directly from rxjs:
import {of} from 'rxjs';
回答2:
I quote from: https://angular.io/guide/testing
The async observable was produced by an asyncData
helper. The asyncData
helper is a utility function that you'll have to write yourself. Or you can copy this one from the sample code:
testing/async-observable-helpers.ts
/** Create async observable that emits-once and completes
* after a JS engine turn */
export function asyncData<T>(data: T) {
return defer(() => Promise.resolve(data));
}
Note: defer
comes from rxjs
, i.e.: import { defer } from 'rxjs';
回答3:
If download sample code from angular docs, you will find 'asyncData' definition in testing>async-observable-helpers.ts file.
回答4:
see Observable.of turn async
Rx.Observable.of(1, 2, 3, Rx.Scheduler.async).subscribe(
(val) => console.log(val)
);
console.log('first');
This will log out:
//first
//1
//2
//3
However, it's nice having of()
synchronous by default. By just using of
you get this order:
- expect1
- tap
- sub
- finalize
- expect2
example:
expect1()
of('hello').pipe(finalize(..),tap(..)).subscribe(..)
expect2()
if you make it async instead I think you will get this order:
- expect1
- expect2
- tap
- sub
- finalize
when i design a service method, i typically have it return an observable even if the component doesn't need any data from the service, I might return Observable using (map(() => null)). If I need to test code within that chain, I may create a helper method that can be called by the tests.
来源:https://stackoverflow.com/questions/52283055/angular-service-testing-cannot-find-name-asyncdata