Reputation: 1126
I'm trying to pass the return value of my API to my BehaviorSubject, but I'm encountering the Typescript error above. It's not clear to me how to fix it. The line of code that is causing the error is return this.sleepReponse.next(score)
How should this be passed to my BehaviorSubject?
export class SleepService {
private endpoint = '';
private sleepResponse = new BehaviorSubject<any>({});
currentResponse = this.sleepResponse.asObservable();
constructor(private http: HttpClient) { }
public postSleepDuration(score: string, forceResponse?: boolean, errorCode?: number): Observable<any | HttpErrorResponse> {
if (forceResponse === undefined) {
return this.http.post<any>(this.endpoint, score );
} else {
const accounts = RESULTS_MOCK;
const error = mockError(errorCode, this.endpoint);
console.log('forceREsponse', of(accounts))
let response = !!forceResponse ? of(accounts) : throwError(error);
return this.sleepResponse.next(response)
}
}
}
const RESULTS_MOCK: any = {
count: 1,
next: null,
previous: null,
status: 200,
results: [
{
id: "1",
name: "Asleep and In bed results"
}
]
}
Upvotes: 1
Views: 1817
Reputation: 1126
Using a pipe worked.
public postSleepDuration(score: string, forceResponse?: boolean, errorCode?: number): Observable<any | HttpErrorResponse> {
if (forceResponse === undefined) {
return this.http.post<any>(this.endpoint, score );
} else {
const accounts = RESULTS_MOCK;
const mockedError = mockError(errorCode);
return of(accounts).pipe(
tap(response => {
this.sleepResponse.next(response)
}),
catchError(error =>
throwError(mockedError) )
)
}
}
Upvotes: 2
Reputation: 4320
One of the nicest things about Angular is the pattern of passing all the dependencies in via the constructor, and having many of the dependencies defined as abstract classes instead of concrete implementations.
So, if you wanted to test SleepService
, you can move the mocking logic from inside SleepService
to its dependencies, leaving SleepService
quite simple:
import { Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
export class SleepService {
private endpoint = '';
constructor(private http: HttpClient) { }
postSleepDuration(score: string): Observable<any | HttpErrorResponse> {
return this.http.post<any>(this.endpoint, score);
}
}
and provide a HttpClient
instance with a mocked HttpHandler
dependency. This HttpHandler
implementation only requires a single method, handle()
, which has access to the HttpRequest
being made so it can react as appropriate for the tests.
It can be as simple as a hard-coded response/error, for example in the success case, something like:
import { of, Observable } from 'rxjs';
import { HttpClient, HttpEvent, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { SleepService } from './sleep-service';
const RESULTS_MOCK: any = {
count: 1,
next: null,
previous: null,
status: 200,
results: [
{
id: '1',
name: 'Asleep and In bed results'
}
]
}
class OkHandler implements HttpHandler {
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
return of(new HttpResponse({body: RESULTS_MOCK}));
}
}
const okService = new SleepService(new HttpClient(new OkHandler()));
okService.postSleepDuration('test').subscribe({
next(x) { console.log(JSON.stringify(x, null, 2)); }
});
and in the failure case, something like:
import { throwError, Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { SleepService } from './sleep-service';
class ErrHandler implements HttpHandler {
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
return throwError(new HttpErrorResponse({ error: mockedError }));
}
}
const errService = new SleepService(new HttpClient(new ErrHandler()));
errService.postSleepDuration('test').subscribe({
error(x) { console.log(JSON.stringify(x, null, 2)); }
});
Or you can provide a single HttpHandler
implementation which has different behaviour based on its state, something like:
import { of, throwError, Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse, HttpEvent, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { SleepService } from './sleep-service';
function mockError(errorCode: number, endpoint: string) {
return `${errorCode}: ${endpoint}`;
}
const RESULTS_MOCK: any = {
count: 1,
next: null,
previous: null,
status: 200,
results: [
{
id: '1',
name: 'Asleep and In bed results'
}
]
}
class Handler implements HttpHandler {
errorCode?: number;
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.errorCode !== undefined) {
return throwError(new HttpErrorResponse({ error: mockError(this.errorCode, request.url) }));
} else {
return of(new HttpResponse({body: RESULTS_MOCK}));
}
}
}
const handler = new Handler();
const service = new SleepService(new HttpClient(handler));
service.postSleepDuration('test').subscribe({
next(x) { console.log(JSON.stringify(x, null, 2)); }
});
handler.errorCode = 42;
service.postSleepDuration('test').subscribe({
error(x) { console.log(JSON.stringify(x, null, 2)); }
});
Upvotes: 1