angular: HttpTestingController.expectOne() not working with queryparameters
I’m submitting a…
[ ] Regression (a behavior that used to work and stopped working in a new release)
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
Related to #19903 When testing a request that contains query parameters, the test throws 2 errors:
- Expected one matching request for criteria “Match URL: ./assets/mock/modules.json”, found none.
- Expected no open requests, found 1: GET ./assets/mock/modules.json
Appending the paramters to the expected URL does not work either.
Expected behavior
Test failure message should show the full path including parameters. OR We should be able to specify expected parameters or ignore all.
Minimal reproduction of the problem with instructions
[example.service.spec.ts]
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Injector } from '@angular/core';
import { async, TestBed } from '@angular/core/testing';
import { ExampleService } from './example.service';
fdescribe('Create a test suite for the Example Service', () => {
let injector: Injector;
let exampleService: ExampleService;
let httpMock: HttpTestingController;
beforeEach(() => {
injector = TestBed.configureTestingModule({
imports:
[HttpClientTestingModule],
providers:
[ExampleService]
});
exampleService = injector.get(ExampleService);
httpMock = injector.get(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should not immediately connect to the server', () => {
httpMock.expectNone({});
});
describe('when fetching all stuff', () => {
it('should make a GET request', async(() => {
exampleService.getStuff();
let req = httpMock.expectOne(`./assets/mock/modules.json`);
expect(req.request.method).toEqual('GET');
req.flush([]);
}));
});
});
[example.service.ts]
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable()
export class ExampleService {
constructor(private http: HttpClient) {
}
getStuff() {
let params = new HttpParams().append('testparam', 'testvalue')
return this.http.get('./assets/mock/modules.json', {params}).toPromise().then(value => {
return value
})
}
}
What is the motivation / use case for changing the behavior?
Currently it is impossible to test http requests in a service which contain query parameters.
Environment
Angular version: 4.4.4
Browser:
- [X] Chrome (desktop) version 61.0.3163.100
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
For Tooling issues:
- Node version: V8.4.0
- Platform: Windows
Others:
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 78
- Comments: 31 (10 by maintainers)
Commits related to this issue
- feat(common): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- feat(common): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- feat(common): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- feat(common): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- feat(common/http): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- feat(common): http testing matches url without params as well First try to match by urlWithParams, then just by url Closes #20878, #19974 — committed to cironunes/angular by cironunes 7 years ago
- fix(common): include a hint in the error message The expectOne method throws a missleading error when a call to an url with parameters is expected. https://github.com/angular/angular/issues/19974 h... — committed to bogdanmanate/angular by bogdanmanate 5 years ago
- fix(common): include query parameters for open HTTP requests in `verify` When `HttpTestingController.verify` is used to verify that there are not open, unexpected requests it would throw an error wit... — committed to JoostK/angular by JoostK 2 years ago
- fix(common): include query parameters for open HTTP requests in `verify` (#44917) When `HttpTestingController.verify` is used to verify that there are not open, unexpected requests it would throw an ... — committed to angular/angular by JoostK 2 years ago
I just came across the same problem. Fixing the error message would be one improvement, but in my view it would be even better if it matched against
request.url
rather thanrequest.urlWithParams
. I’d rather write:than:
not least because it avoids any ordering issues when you have more than one parameter.
A current workaround is to use the request matcher function instead:
@JrPribs this is the same problem I was having just now. I was already beginning to assume that
expectOne()
is buggy, but it’s not.expectOne()
should also testhttpParams
as they might be important in some casesrequest.url
instead ofrequest.urlWithParams
if you don’t carehttpParams
toexpectOne()
it can’t print any params when no request matches. It doesn’t know any params.verify()
should printhttpParams
. Reading"No match found for https://my-url.com"
on the one hand and"Open request found for https://my-url.com"
on the other hand is very confusing!Just ran into this as well. What you could do however is to use the following check
Or in a more complex case, where for instance I check whether my service method removes all null/undefined/empty values from the query url, I simply check whether it’s the current URL in the
expectOne
and then I access the request and add expect statements for the individual params.Spent over an hour trying to get a single test with a singe query param to work to no avail, have tried all solutions above. Now I am getting…
Error: Expected one matching request for criteria "Match by function: ", found none.
Other tests without query params work.
Shame, cannot spend any more time on this, test commented out in my code 😦
@algorys the downside of the latter approach is that it is dependent on the order of query parameters being added to the URL, which I don’t think is guaranteed if you’re building the URL from a
params
object and probably shouldn’t be relied on. Obviously if you only have one query parameter that isn’t a problem, but it doesn’t scale. That’s why I suggested checking the URL without query params, then validating those separately.I wrote this pair of utility functions which should provide a more robust solution for testing requests with parameters:
YMMV, they helped me with my test cases.
Edit: fixed bugs pointed out by textbook below.
@samih-dev it’s not clear how that’s relevant to this issue. Per this comment by the OP it’s not about promise vs. observable, and my suggestion is independent of either.
This is quite annoying, just spent some hours until figuring this should be a bug in expectOne. And here it is.
@textbook work-around works fine, thank you.
@marcellarius this looks suspicious:
actualParams
isn’t used there at all,expected
andactual
are the same array. You should re-review your tests (and the code they cover), they aren’t telling you what you think they are.This behaviour is absolutely broken, undocumented and unexpected.
It’s not just an issue with the HttpTestingController though; if you make a request with a querystring baked into the URL, the
params
object in the request doesn’t get populated, and therequest.url
will contain these query params. The issue there seems to be the HttpRequest constructor logic. Ideally theparams
should reflect the actual URL params, including any that were already in theurl
string.My ideal expected behaviour would be to parse the expected URL, then ensure everything matches the request made – any query string parameters specified in the test expectation would be compared against the actual URL; any not specified would be ignored.
As seems to be usual with Angular, these caveats are not mentioned anywhere in the documentation.
I have run into this issue when I have a typo in my expectation when using params. Should the HttpTestingController not indicate that a different url was call than expected, or am I causing it to throw the nondescript error because of my implementation? I was under the impression that
.verify()
would let me know if the wrong url was called, am I mistaken?Same thing happens to me for my project:
Class:
Test:
Only the fix in comment help me to solve this problem. ERROR output:
I am able to proceed. This is not a bug. I had similar issue "Match by function: ", found none. It is solved after i added empty handler for then() in-case the method returns a Promise and subscribe() in-case method returns a Observable.
.then( (data) => { console.log('success'); }
.subscribe( () => { console.log('success'); }