Skip to content
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

Approve code review #6

Merged
merged 7 commits into from Jun 7, 2018
Merged

Approve code review #6

merged 7 commits into from Jun 7, 2018

Conversation

peter279k
Copy link
Member

@peter279k peter279k commented Jun 1, 2018

@Bilge , this is the second code review after modifying this.

peter279k referenced this pull request Jun 1, 2018
int $count,
ProviderResource $resource
) {
parent::__construct($providerRecords, $count, $resource);
Copy link

Choose a reason for hiding this comment

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

A constructor override that only calls the parent implementation is the same as having no constructor override. That is, this whole constructor can be deleted and have exactly the same effect. However, I also suggest (as previously), that this entire file should be deleted because custom collections are usually to supply metadata but this class is not adding any extra value, let alone supplying metadata.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right. I will remove this collection record.


return new AllRatesRecord($time, $base, $quote, $rate);

$rate = function () use ($rates) {
Copy link

Choose a reason for hiding this comment

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

This closure seems to just copy the array into an iterator. You can replace this entire code block with new ArrayIterator($response['rates']).

{
return $this->rate;
return iterator_to_array($this);
Copy link

Choose a reason for hiding this comment

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

This doesn't seem to add any value, and its implementation encourages using array instead of iterators. If the client want to use arrays it can call iterator_to_array itself, but Porter and her providers should not encourage this because it loads the entire data set into memory. In this case, since we're not streaming the response, it's already loaded into memory anyway, but it's still a bad idea to encourage this. Recommend removing this method.

$time = $response['time'];

return new SpecificRateRecord($time, $base, $quote, $rate);
$rate = function () use ($response) {
Copy link

Choose a reason for hiding this comment

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

Again, this seems to be a very roundabout way of copying an array. Since we don't need the SpecificRateRecord collection, this entire code block (lines 42-51) can be replaced with yield $response.

@@ -21,10 +21,13 @@ public function testGetSpecificRateRecords()
$rates = FixtureFactory::createPorter()->import(new ImportSpecification(new GetSpecificRate($this->apiKey)))
->findFirstCollection();
Copy link

Choose a reason for hiding this comment

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

This should not be necessary.

$this->assertSame('USD', $rates->getQuote());
$this->assertLessThanOrEqual(time(), strtotime($rates->getTime()));
$this->assertInternalType('float', $rates->getRate());
$rateRecords = $rates->toAssociativeArray();
Copy link

Choose a reason for hiding this comment

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

This should not be necessary. Use Porter::importOne() instead of import().

Copy link
Member Author

Choose a reason for hiding this comment

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

I think you add the wrong place for this comment.
I cannot see any code that is related to the import().

Copy link

Choose a reason for hiding this comment

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

They're both related. This line should be deleted because it is not necessary if you use importOne.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you give some sample code about this?
I don't understand Porter::importOne() usage clearly.

Thanks.

Copy link

@Bilge Bilge Jun 1, 2018

Choose a reason for hiding this comment

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

import() returns the collection from the resource. importOne() does the same thing, except it returns the first record from the collection instead of the collection itself (and ensures there is no more than one record in the collection).

@@ -33,16 +36,12 @@ public function testGetAllRateRecords()
$rates = FixtureFactory::createPorter()->import(new ImportSpecification(new GetAllRate($this->apiKey)))
->findFirstCollection();
Copy link

Choose a reason for hiding this comment

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

findFirstCollection() should only be used when you need to get metadata. Call it later when testing getBase(), but do not call it here, before iteration has been tested.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you show some example approach for this?

Thanks.

Copy link

Choose a reason for hiding this comment

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

Just call findFirstCollection() later, after you've done all the other assertions.


$this->assertArrayHasKey('asset_id_quote', $rateRecords[0]);
$this->assertArrayHasKey('rate', $rateRecords[0]);
$this->assertArrayHasKey('time', $rateRecords[0]);
Copy link

Choose a reason for hiding this comment

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

This is a bad pattern for testing iterators: we should ensure the whole iterator looks and works as we expect, instead of only looking at the first record. Use foreach to loop over each record and assert each record is correct. It's a good idea to make assertions on the data too, not just the keys. If you don't know what the data should look like, at least try a type assertion and maybe a not empty assertion if the data should be non-null.

@peter279k
Copy link
Member Author

@Bilge, I almost do the suggestion that you recommend.
I also refer the Steam provider to complete my test approach.
Please review the latest commit.

Thanks.

@Bilge
Copy link

Bilge commented Jun 1, 2018

I'm sorry but this is completely wrong. It seems you are doing your own thing instead of following my guidelines.

@@ -30,7 +32,7 @@ public function getProviderClassName(): string

public function fetch(ImportConnector $connector): \Iterator
{
$response = \json_decode(
$response[] = \json_decode(
Copy link

Choose a reason for hiding this comment

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

This is wrapping your record in an outer array, which is not correct. This should just be $response instead of $response[].

};

return new SpecificRateRecord($rate(), count($response), $this);
return yield $response;
Copy link

Choose a reason for hiding this comment

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

return does nothing here, since yield $response has no return value. Should just be yield $response.


$rateRecords = $rates->toAssociativeArray();
$this->assertCount(1, $rate);
Copy link

Choose a reason for hiding this comment

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

This is unnecessary because importOne guarantees the iterator only contains one result. If it contains more it throws an exception.


$this->assertSame('BTC', $rates->getBase());
$this->assertCount(1267, $rates);
Copy link

Choose a reason for hiding this comment

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

This magic number is too brittle. The test needs to prove the count is correct without breaking whenever a rate is added or removed. You have two options, either:

  1. Count the number of records during iteration and ensure it matches the reported count. To do this, the assertion would have to be moved after the loop.
  2. Estimate the number of expected values, e.g. self::assertGreaterThan(1200, count($rates)).

@peter279k
Copy link
Member Author

@Bilge, I've approved the recommendations that you suggest.
If having any problem, please add the comment to let me know.

Thanks.

@peter279k peter279k merged commit 5dad284 into master Jun 7, 2018
@peter279k peter279k mentioned this pull request Jun 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants