-
Notifications
You must be signed in to change notification settings - Fork 212
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
Add Mock API Request Capability and Mocked Connections Tests #314
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it wouldn't make much sense to have a method that constructs this response for you and you testing the response contents. Because first the combinations or parameters are going to be countless.
And second, you coded this generation logic for the tests sake. You know they are always going to work fine, thus tests would pass and you're not really testing shipped behavior, just "test environment" behavior.
e.g. Why would you test that the generated response did actually filter the items "by strategy" when you know it will succeed because that's how you coded the logic for the response generator? Unless your php implementation (read "no test environment/context") in the API-client-entity-file is doing some extra manipulation after receiving the response. That would be a candidate test case, but if it's a call hand-passing the result it wouldn't.
What it will make sense is to test that the request contains the parameters you want (as you're doing in many of the tests), either in the body or in the query. Also the http method and the full url path. And if you want, test that the response object/array is of type X (e.g. JSON has a "user" schema, ignoring the properties content such as name matches "Juan"). Don't go further than that 👌
protected static $mockJson; | ||
|
||
/** | ||
* Instance of MockJsonBody. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copy-paste
|
||
// Test an explicit true for includeFields. | ||
$api->call()->getAll(null, $fields, true); | ||
$this->assertContains( 'include_fields=true', $api->getHistoryQuery(1) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe copy this line into the checks for the call right before this one? assertDoesntContain(include_fields)
new Response( 200, self::$headers, self::$mockJson->withPages( 1, 4 )->getClean() ), | ||
] ); | ||
|
||
$response1 = $api->call()->getAll(null, null, null, 0, 5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null, null, null, 0, 5
is getting difficult to follow. Consider defining vars above the call, using named args (if that's a thing for PHP) or passing a Map/Dict instead for the query params and another for the body. GET methods don't get a body, though.
$this->assertArrayHasKey( 'id', $response ); | ||
$this->assertEquals( $id, $response['id'] ); | ||
$this->assertEquals( 'GET', $api->getHistoryMethod() ); | ||
$this->assertEquals( $api::API_BASE_URL.self::ENDPOINT.'/'.$id, $api->getHistoryUrl() ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm against using constants references on the tests. What if you change the constant value by mistake adding a typo? Test would still pass. It's fine to use them to initialize the MockApi
above but I rather hardcode the full expected path inside these tests in the asserts section e.g. https://myapi.auth0.com/api/v2/connections/con_testConnection10
. You'll find it easier to read and follow when you come back as well.
tests/MockApi.php
Outdated
* @param string $endpoint Endpoint property name to call. | ||
* @param array $responses Responses to be loaded, an array of Response objects. | ||
*/ | ||
public function __construct($endpoint, array $responses = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider instead of receiving an array of responses here, having a method like enqueue(response)
that would add a single response or an array of responses to the api queue. This way you could instantiate just once the MockApi
at the setup step of the test file, since the endpoint is common to the test file, and then just enqueue/write and take/read responses. I think it would simplify a lot the file and would also be more reusable.
You could as well forget about indexes and always keep the "last response". e.g. public api:
MockApi mockApi = new MockApi(endpoint);
mockApi.enqueue(response1);
mockApi.enqueue(response2);
response1 = mockApi.take();
mockApi.lastUrl() == response1.url;
response2 = mockApi.take();
mockApi.lastUrl() == response2.url;
mockApi.take(); //throws error, queue empty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tinkered with this quite a bit before I submitted it and, the way it all works, those responses have to be passed to the client before it can be created. I'd either have to do:
$mockApi = new MockApi(endpoint);
$mockApi->enqueue(response1);
$mockApi->enqueue(response2);
$mockApi->build();
... or have enqueue keep track of what's been added so far and re-instantiate the Management API each time. Neither one seemed great. I think the facade for the history API I'm using could be better but this is clear/simple enough for now 👍
Keeping track of the index internally is a great suggestion, thank you.
tests/MockApi.php
Outdated
/** | ||
* Return the endpoint being used. | ||
* | ||
* @return BL | Cl | CG | Co | DC | Em | ET | J | L | R | RS | S | Te | Ti | UB | U | UE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aliases for the classes at the top of the class. They're too long for a single line but without them (@return mixed
or something) IDEs won't recognize the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gone now 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @return
line can be multiline. Anyway, never minify classes' names. People shouldn't be trying to guess what you mean. Is fine if you want to add them back instead of removing them, just use the full name. 👌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @return line can be multiline.
IDE won't parse it. Trust me, I don't like this either but it retained the functionality needed and is "legal" according to PHP :)
Either way, not needed now.
tests/MockApi.php
Outdated
*/ | ||
public function call() | ||
{ | ||
return $this->client->{$this->endpoint}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why a method named call()
would just return the base url / endpoint called? Since this is passed as part of the constructor, it should already be available, known and in scope for whoever is using this MockApi instance, IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm now using that to keep track of the history index (see above) but the idea behind it was to give the right endpoint without being able to re-assign it somehow. I altered that a bit to keep the same IDE suggestions and index tracking.
tests/MockJsonBody.php
Outdated
$this->dataBackup = $this->data; | ||
} | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading from a JSON file is fine. What's not good is having the data being manipulated (filtered, split in pages, etc) here internally and only in the tests environment, because you're adding your own logic that might not be what the server is doing. Either do real api calls or read JSON files with real api responses content.
I'll leave you a general comment detailing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class is gone 👍
|
||
$this->assertContains( 'page=0', $api->getHistoryQuery() ); | ||
$this->assertContains( 'per_page=5', $api->getHistoryQuery() ); | ||
$this->assertCount( 5, $response1 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens when include_totals=true
is sent in the query? Can your code parse that JSON structure that the server will send as well? Your method should receive the JSON body and return the expected array, or a "page" object if you don't want the user to loose the page totals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens when include_totals=true is sent in the query?
@joshcanhelp you're still missing this use case I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lbalmaceda - Just passes it straight through as JSON, nothing special to test there.
f3d7428
to
f976b2e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved but left one comment! 👍
$this->assertEquals( 'GET', $api->getHistoryMethod() ); | ||
$this->assertStringStartsWith( 'https://api.test.local/api/v2/connections/'.$id, $api->getHistoryUrl() ); | ||
$this->assertContains( 'fields='.implode(',', $fields), $api->getHistoryQuery() ); | ||
$this->assertNotContains( 'include_fields=true', $api->getHistoryQuery() ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is supposed to assert that by default this property is not sent, correct? I would check against include_fields
(without any value) so that if the property is set to false this would fail as well.
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
All changes are in the test suite, no SDK functionality changes.
Auth0\Tests\MockApi
to generate mocked API endpoints using Guzzle mocks and historyAuth0\Tests\MockJsonBody
to generate JSON body responses from the mock serverAuth0\Tests\API\Management\ConnectionsTestMocked
andmanagement-api--connections.json
to show how the mocked server is used (pushes Connection coverage to 100% 🎉 )EmailTemplateTest
->EmailTemplatesTest
to match endpoint name