Skip to content

[HttpClient] add MockHttpClient #30604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 19, 2019

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Mar 19, 2019

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

This PR introduces MockHttpClient and MockResponse, to be used for testing classes that need an HTTP client without making actual HTTP requests.

MockHttpClient is configured via its constructor: you provide it either with an iterable or a callable, and these will be used to provide responses as the consumer requests them.

Example:

$responses = [
    new MockResponse($body1, $info1),
    new MockResponse($body2, $info2),
];

$client = new MockHttpClient($responses);
$response1 = $client->request(...); // created from $responses[0]
$response2 = $client->request(...); // created from $responses[1]

Or alternatively:

$callback = function ($method, $url, $options) {
    return new MockResponse(...);
};

$client = new MockHttpClient($callback);
$response = $client->request(...); // calls $callback internally

The responses provided to the client don't have to be instances of MockResponse - any ResponseInterface works (e.g. $this->getMockBuilder(ResponseInterface::class)->getMock()).

Using MockResponse allows simulating chunked responses and timeouts:

$body = function () {
    yield 'hello';
    yield ''; // the empty string is turned into a timeout so that they are easy to test
    yield 'world';
};
$mockResponse = new MockResponse($body());

Last but not least, the implementation simulates the full lifecycle of a properly behaving HttpClientInterface contracts implementation: error handling, progress function, etc. This is "proved" by MockHttpClientTest, who implements and passes the reference test suite in HttpClientTestCase.

@nicolas-grekas nicolas-grekas force-pushed the feature/mock-client branch 2 times, most recently from 8a7b0fc to 3a16803 Compare March 19, 2019 17:06
Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick review. It looks alright, but I have not tested it yet.

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also haven't tested it, but the user experience looks wonderful!

@fabpot
Copy link
Member

fabpot commented Mar 19, 2019

Thank you @nicolas-grekas.

@fabpot fabpot merged commit 8fd7584 into symfony:master Mar 19, 2019
fabpot added a commit that referenced this pull request Mar 19, 2019
This PR was merged into the 4.3-dev branch.

Discussion
----------

[HttpClient] add MockHttpClient

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

This PR introduces `MockHttpClient` and `MockResponse`, to be used for testing classes that need an HTTP client without making actual HTTP requests.

`MockHttpClient` is configured via its constructor: you provide it either with an iterable or a callable, and these will be used to provide responses as the consumer requests them.

Example:
```php
$responses = [
    new MockResponse($body1, $info1),
    new MockResponse($body2, $info2),
];

$client = new MockHttpClient($responses);
$response1 = $client->request(...); // created from $responses[0]
$response2 = $client->request(...); // created from $responses[1]
```

Or alternatively:
```php
$callback = function ($method, $url, $options) {
    return new MockResponse(...);
};

$client = new MockHttpClient($callback);
$response = $client->request(...); // calls $callback internal
```

The responses provided to the client don't have to be instances of `MockResponse` - any `ResponseInterface` works (e.g. `$this->getMockBuilder(ResponseInterface::class)->getMock()`).

Using `MockResponse` allows simulating chunked responses and timeouts:
```php
$body = function () {
    yield 'hello';
    yield ''; // the empty string is turned into a timeout so that they are easy to test
    yield 'world';
};
$mockResponse = new Mockresponse($body);
```

Last but not least, the implementation simulates the full lifecycle of a properly behaving `HttpClientInterface` contracts implementation: error handling, progress function, etc. This is "proved" by `MockHttpClientTest`, who implements and passes the reference test suite in `HttpClientTestCase`.

Commits
-------

8fd7584 [HttpClient] add MockHttpClient
@nicolas-grekas nicolas-grekas deleted the feature/mock-client branch March 19, 2019 18:42
@nicolas-grekas nicolas-grekas modified the milestones: next, 4.3 Apr 30, 2019
@fabpot fabpot mentioned this pull request May 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants