MockHttpTrait: support multiple MultiHttpClients

With mocking HTTP, we can supply an array of requests
to expect, but if MultiHttpClient or Guzzle client are
used, we can only supply one. Now we can mock a sequence
of requests done via either of these clients.

Change-Id: I350ed59af603a0504704af3265787a4be8f2dc2a
This commit is contained in:
Petr Pchelko 2021-08-11 07:09:20 -07:00
parent 0f76e5ad26
commit 295557d1ae
2 changed files with 75 additions and 14 deletions

View file

@ -51,6 +51,8 @@ trait MockHttpTrait {
* or a string that will be used as the response body of a successful request.
* If a MultiHttpClient is given, createMultiClient() is supported.
* If a GuzzleHttp\Client is given, createGuzzleClient() is supported.
* Array of MultiHttpClient or GuzzleHttp\Client mocks is supported, but not an array
* that contains the mix of the two.
* If null is given, any call to create(), createMultiClient() or createGuzzleClient()
* will cause the test to fail.
*/
@ -70,6 +72,9 @@ trait MockHttpTrait {
* or a callable producing such an MWHttpRequest,
* or a string that will be used as the response body of a successful request.
* If a MultiHttpClient is given, createMultiClient() is supported.
* If a GuzzleHttp\Client is given, createGuzzleClient() is supported.
* Array of MultiHttpClient or GuzzleHttp\Client mocks is supported, but not an array
* that contains the mix of the two.
* If null or a MultiHttpClient is given instead of a MWHttpRequest,
* a call to create() will cause the test to fail.
*
@ -93,20 +98,20 @@ trait MockHttpTrait {
->onlyMethods( [ 'create', 'createMultiClient', 'createGuzzleClient' ] )
->getMock();
if ( $request instanceof MultiHttpClient ) {
$mockHttpRequestFactory->method( 'createMultiClient' )
foreach ( [
MultiHttpClient::class => 'createMultiClient',
GuzzleHttp\Client::class => 'createGuzzleClient'
] as $class => $method ) {
if ( $request instanceof $class ) {
$mockHttpRequestFactory->method( $method )
->willReturn( $request );
} elseif ( $this->isArrayOfClass( $class, $request ) ) {
$mockHttpRequestFactory->method( $method )
->willReturnOnConsecutiveCalls( ...$request );
} else {
$mockHttpRequestFactory->method( 'createMultiClient' )
->willReturn( $this->createNoOpMock( MultiHttpClient::class ) );
$mockHttpRequestFactory->method( $method )
->willReturn( $this->createNoOpMock( $class ) );
}
if ( $request instanceof GuzzleHttp\Client ) {
$mockHttpRequestFactory->method( 'createGuzzleClient' )
->willReturn( $request );
} else {
$mockHttpRequestFactory->method( 'createGuzzleClient' )
->willReturn( $this->createNoOpMock( GuzzleHttp\Client::class ) );
}
if ( $request === null ) {
@ -135,6 +140,26 @@ trait MockHttpTrait {
return $mockHttpRequestFactory;
}
/**
* Check whether $array is an array where all elements are instances of $class.
*
* @internal to the trait
* @param string $class
* @param mixed $array
* @return bool
*/
private function isArrayOfClass( string $class, $array ): bool {
if ( !is_array( $array ) || !count( $array ) ) {
return false;
}
foreach ( $array as $item ) {
if ( !$item instanceof $class ) {
return false;
}
}
return true;
}
/**
* Constructs a fake MWHTTPRequest. The request also represents the desired response.
*

View file

@ -238,6 +238,24 @@ class MockHttpTraitTest extends MediaWikiIntegrationTestCase {
$this->assertSame( $expected, $data );
}
/**
* @dataProvider provideMultiRequestData
*/
public function testMakeMockHttpRequestFactoryEmulatesMultipleMultiClients(
$requests, $responses, $expected
) {
$clients = [
$this->makeFakeHttpMultiClient( $responses ),
$this->makeFakeHttpMultiClient( $responses ),
];
$factory = $this->makeMockHttpRequestFactory( $clients );
foreach ( $clients as $unused ) {
$client = $factory->createMultiClient();
$data = $client->runMulti( $requests );
$this->assertSame( $expected, $data );
}
}
public function provideGuzzleClientData() {
yield [
'Hello Wörld',
@ -291,6 +309,24 @@ class MockHttpTraitTest extends MediaWikiIntegrationTestCase {
$this->assertGuzzleResponse( $expected, $client->post( 'http://b.example.com' ) );
}
/**
* @dataProvider provideGuzzleClientData
*/
public function testMakeMockHttpRequestFactoryEmulatesMultipleGuzzleRequests( $response, $expected ) {
$fakeClients = [
$this->makeFakeGuzzleClient( $response ),
$this->makeFakeGuzzleClient( $response ),
];
$factory = $this->makeMockHttpRequestFactory( $fakeClients );
foreach ( $fakeClients as $unused ) {
$client = $factory->createGuzzleClient();
$this->assertGuzzleResponse( $expected, $client->request( 'TEST', 'http://b.example.com' ) );
$this->assertGuzzleResponse( $expected, $client->get( 'http://b.example.com' ) );
$this->assertGuzzleResponse( $expected, $client->put( 'http://b.example.com' ) );
$this->assertGuzzleResponse( $expected, $client->post( 'http://b.example.com' ) );
}
}
/**
* @param ResponseInterface $expected
* @param ResponseInterface $actual