I have an angular component that will POST some data to a URL in our app, and then does nothing else since no data is returned back from that post. I am having trouble testing this since normally HTTP requests are tested by subscribing to the observables they return. This isn't needed to be exposed in this case.
here's my component code:
shareData(): void {
this.isFinishing = true;
this.myService.sendSharedData$()
.pipe(first())
.subscribe(() => {
//Data s now shared, send the request to finish up everything
this.submitFinishRequest();
}, (e: Error) => this.handleError(e)));
}
private submitFinishRequest(): void {
//submit data to the MVC controller to validate everything,
const data = new FormData();
data.append('ApiToken', this.authService.apiToken);
data.append('OrderId', this.authService.orderId);
this.http.post<void>('/finish', data)
.pipe(first())
.subscribe((d) => {
// The controller should now redirect the app to the logged-out MVC view, so there's nothing more we need to do here
this.isFinishing = false;
}, (e: Error) => this.handleError(e));
}
And here is my testing code
let component: FinishComponent;
let fixture: ComponentFixture<FinishComponent>;
let myService: MyService;
let httpMock: HttpTestingController;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
declarations: [ FinishComponent ],
providers: [ MySerVice ],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FinishComponent);
component = fixture.componentInstance;
myService = TestBed.get(MyService);
httpMock = TestBed.get(HttpTestingController);
sendSharedData$Spy = spyOn(myService, 'sendSharedData$');
//Add some accounts and shared items to the service for all of these tests
accountsService.dataToShare = ['foo', 'bar'];
});
afterEach(() => {
httpMock.verify();
});
it('should make an HTTP POST to the `/finish` MVC Controller after successfully sharing data', () => {
sendSharedData$Spy.and.callThrough(); //call through using data provided in `beforeEach`
fixture.detectChanges(); //triggers ngOnInit()
component.shareData();
fixture.detectChanges();
const req = httpMock.expectOne('/finish');
expect(req.request.method).toEqual('POST');
expect(req.request.body).toEqual({
apiKey: 'api-key-98765',
orderId: 'order-id-12345'
});
//server can send back any data (except for an error) and we would respond the same way, so just send whatever here
req.flush('');
});
What I actually get in my test is:
Error: Expected one matching request for criteria "Match URL: /finish", found none.
I think this happens because I don't subscribe to the http.post()
from inside my test, but if I do doesn't that completely negate the reason I'm testing this method? i shouldn't have to subscribe to things if my methods already do that, right?
Also, when I run this with other tests, another unrelated test usually fails with
Error: Expected no open requests, found 1: POST /finish
Which indicates to me that the request is happening, but at an incorrect time, or I'm not properly waiting on it somehow.
Edit - it's fixed now
The issue was due to the .and.callThrough()
. I replaced that with .and.returnValue(of([... some data here ...]));
that now it all works as expected. Sorry for the hassle, and thanks for all the help & ideas!
submitFinishRequest
method. I'm just not subscribing in the test since that seems to defeat the purpose – Decline/finish
URL to correctly log out. So, this method works, and it subscribes correctly and it does call that private method correctly as well. it just doesn't seem to call thehttp.post
method in time. I've tried adding afakeAsync
and atick()
but that doesn't seem to work – Decline.and.callThrough()
. I replaced that with.and.returnValue(of([... some data here ...]));
that now it all works as expected. – Decline