I'm trying to get all lines green on the following Angular 12.6 service: I have this test group
describe('AuthService', () => {
let service: AuthService;
let apiResponse: ApiResponse;
let controller: HttpTestingController;
let accountDetails: AccountDetails;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
{ provide: APP_CONFIG, useValue: AppConfig },
],
});
service = TestBed.inject(AuthService);
controller = TestBed.inject(HttpTestingController);
apiResponse = {
accessToken: 'Gimme access'
};
accountDetails = {
Password: 'bla',
UserName: 'vlad',
};
});
/// a few other tests below
it('should call the back-end', () => {
spyOn(service, 'setSession');
spyOn(service, 'login').and.callThrough();
service.login(accountDetails.UserName, accountDetails.Password).subscribe(() => {
expect(service.setSession).toHaveBeenCalled();
});
expect(service.login).toHaveBeenCalledOnceWith('vlad', 'bla');
controller.match(AppConfig.apiEndPoint);
});
});
And this as service method:
@Injectable({
providedIn: 'root',
})
export class AuthService {
#storage = localStorage;
options = new HttpHeaders({
'Content-Type': 'application/json',
});
private static handleError(error: HttpErrorResponse) {
if (error.status === 0) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
console.error(`Backend returned code ${error.status}, body was: `, error.error);
}
// Return an observable with a user-facing error message.
return throwError('Something happened; please try again later.');
}
constructor(@Inject(APP_CONFIG) private config: IapConfig, private http: HttpClient) {}
login(customerKey: string, username: string, password: string): Observable<any> {
const timestamp = new Date().getTime();
return this.http
.post<AccountDetails>(this.config.apiEndPoint, {
Password: password,
UserName: username,
})
.pipe(catchError(AuthService.handleError))
.pipe(map((response: AccountDetails) => {
this.setSession(response);
return response;
}))
}
setSession(data: ApiResponse | any): void {
try {
// tslint:disable-next-line:no-console
console.info('User session is set 😁');
} catch (error) {
throw new Error(`Incorrect response structure. Unable to map: ${error}`);
}
}
}
One: Somehow I have the feeling I'm not really testing the setSession
method invocation from the login
method. And Two how can I test the line in the catch block?
How I can mock to test without mocking the Observable returned by the login though the pipe operator. The setSession under the subscription of the login method I can't mock and check either is called with the right arguments.
After reading the recommended by @AliF50 article from the Testing Angular - Testing a Service for which I thank him. I come up with the following in my test files:
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { APP_CONFIG, AppConfig } from '../app.config';
import { AccountDetails, ApiResponse } from '../interfaces';
describe('AuthService', () => {
let service: AuthService;
let apiResponse: ApiResponse;
let controller: HttpTestingController;
let accountDetails: AccountDetails;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
AuthService,
{ provide: APP_CONFIG, useValue: AppConfig },
],
});
service = TestBed.inject(AuthService);
controller = TestBed.inject(HttpTestingController);
apiResponse = {
accessToken: 'Gimme access',
languageCode: 'EN',
};
accountDetails = {
Password: 'bla',
UserName: 'vlad',
TimeStamp: '123454',
};
});
it('should call the back-end', () => {
spyOn(service, 'setSession');
spyOn(service, 'login').and.callThrough();
let result: any;
service.login(accountDetails.UserName, accountDetails.Password)
.subscribe((response) => {
result = response;
expect(service.setSession).toHaveBeenCalled();
expect(service.setSession).toHaveBeenCalledWith(apiResponse);
expect(result).toEqual(apiResponse);
});
const request = controller.expectOne(AppConfig.apiEndPoint);
request.flush(apiResponse);
});
});
Some key elements I missed:
request.flush
and testing the requestI also changed a bit the login component and its call to the service without a subscription. Instead of, the service is calling setSession
on successful response. I've edited the question. Once again - thank you @AliF50 for this precious and unofficial resource~!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.