r3plica
r3plica

Reputation: 13367

Return value from subscribe in Angular

I am trying to understand rxjs Subscriptions, Observables, etc, but it is foreign to me. I have a service with a method I subscribe to. The method looks like this:

create(product: any): Observable<any> {
    return this.http.post<any>(`${environment.apiUrl}/products`, product).pipe(
        map((response: any) => {
            this.toastr.success(response.message);
            return response.model;
        }),
    );
}

Then I have a save method in my component and for the tests I want to get the returned model. So I did this:

onSubmit(): Observable<any> {
    this.submitted = true;

    if (this.saveForm.invalid) return;

    let valid = true;
    let categoryId = this.fields[0].categoryId;
    let product = {
        categoryId: categoryId,
    };

    this.fields.forEach(field => {
        let value = this.f[field.name].value;
        if (valid) {
            valid = value !== undefined;
        }
        product[field.name] = value;
    });

    product['state'] = valid ? 'verified' : 'raw';

    var response = new Subject<any>();
    this.productService.create(product).subscribe(data => {
        response.next(data);
    });
    return response.asObservable();
}

In my test, I wrote this:

it('should save a product and remove from range', () => {
    let component = spectator.component;

    component.onSubmit().subscribe(product => {
        expect(product.gtin).toBe(0);
        expect(product.state).toBe('verified');
        console.log(product);
    });

    expect(component.range.length).toBe(1);
    expect(component.saveForm.controls['gtin'].value).toBe(1);
});

I am guessing I have done it wrong. The test passes, but I think that's because subscribe is never reached. Can someone tell me what I need to do?

Upvotes: 1

Views: 1234

Answers (2)

MoxxiManagarm
MoxxiManagarm

Reputation: 9124

Instead of

var response = new Subject<any>();
this.productService.create(product).subscribe(data => {
   response.next(data);
});
return response.asObservable();

Just do

return this.productService.create(product);

Upvotes: 1

The Fabio
The Fabio

Reputation: 6250

Your assumption is correct, the subscribe body is never called in your test... as the observable would not yield.

You need to change the test like this:

let httpTestingController: HttpTestingController;

beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule, // <== very important to mock the request
      ],
    });

    (...)

    httpTestingController = TestBed.get(HttpTestingController);
  });

it('should save a product and remove from range', async done => {
    let component = spectator.component;

    component.onSubmit().subscribe(product => {
        expect(product.gtin).toBe(0);
        expect(product.state).toBe('verified');
        console.log(product);
        done();
    });

    expect(component.range.length).toBe(1);
    expect(component.saveForm.controls['gtin'].value).toBe(1);

    //This part simulates the backend responding your .onSubmit() call: 
    const testProductData = {your_atributes} // <== this should be what you expect to receive from the backend;
    const mockRequest = httpTestingController.expectOne(`${environment.apiUrl}/products`);
    mockRequest.flush(testProductData); // <== this call will make your observable respond.
});

This done callback in the test is what will make your test work for asynchronous tests requirements like yours. The test fails when the subscription body is not called.

Upvotes: 0

Related Questions