Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Create a GitHub Service Test

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Now that we can see if a Dinosaur is accepting visitors on our dashboard, we need to keep the dashboard updated in real-time by using the health status labels that GenLab has applied to several dino issues on GitHub. To do that we'll create a service that will grab those labels using GitHub's API.

Test for our Service First

To test our new service... which doesn't exist yet, inside of tests/Unit/ create a new Service/ directory and then a new class: GithubServiceTest... which will extend TestCase:

... lines 1 - 3
<div class="container volcano mt-4" style="flex-grow: 1;">
... line 5
<div class="dino-stats-container mt-2 p-3">
<table class="table table-striped">
<thead>
<tr>
... lines 10 - 13
<th>Accepting Visitors</th>
</tr>
</thead>
<tbody>
{% for dino in dinos %}
<tr>
... lines 20 - 23
<td>{{ dino.acceptingVisitors ? 'Yes' : 'No' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
... lines 31 - 53

I'm creating this in a Service/ sub-directory because I'm planning to put the class in the src/Service/ directory. Add method testGetHealthReportReturnsCorrectHealthStatusForDino and inside, $service = new GithubService(). Yup, that doesn't exist yet either...

Our service will return a HealthStatus enum that's created from the health status label on GitHub, so we'll assertSame() that $expectedStatus is identical to $service->getHealthReport() and then pass $dinoName. Yup, we'll be using a data provider for this test... where we accept the name of the dino to check for their expected health status.

Let's go create that: public function dinoNameProvider() that returns a \Generator. Our first dataset for the provider will have the key Sick Dino, which returns an array with HealthStatus::SICK and Daisy for the dino's name... because when we checked GitHub a minute ago, Daisy was sick!

Next up is a Healthy Dino with HealthStatus::HEALTHY who happens to be the one and only Maverick. Up on the test method, add a @dataProvider annotation so the test uses dinoNameProvider... and then add HealthStatus $expectedStatus and string $dinoName arguments.

... lines 1 - 2
namespace App\Tests\Unit\Service;
use App\Enum\HealthStatus;
use PHPUnit\Framework\TestCase;
class GithubServiceTest extends TestCase
{
/**
* @dataProvider dinoNameProvider
*/
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
$service = new GithubService();
self::assertSame($expectedStatus, $service->getHealthReport($dinoName));
}
public function dinoNameProvider(): \Generator
{
yield 'Sick Dino' => [
HealthStatus::SICK,
'Daisy',
];
yield 'Healthy Dino' => [
HealthStatus::HEALTHY,
'Maverick',
];
}
}

Let's do this! Find your terminal and run:

./vendor/bin/phpunit

And... Yup! Just as we expected, we have two errors because:

The GithubService class cannot be found

Create the service that will call GitHub

To fix that, find a teammate and ask them nicely to create this class for you! TDD - team-driven-development!

I'm kidding: we got this! Inside of src/, create a new Service/ directory. Then we'll need the new class: GithubService and inside, add a method: getHealthReport() which takes a string $dinosaurName and gives back a HealthStatus object.

... lines 1 - 2
namespace App\Service;
use App\Enum\HealthStatus;
class GithubService
{
public function getHealthReport(string $dinosaurName): HealthStatus
{
... lines 11 - 15
}
}

Here's the plan: we'll call GitHub's API to get the list of issues for the dino-park repository. Then we'll filter those issues to pick the one that matches $dinosaurName. Finally, we'll return HealthStatus::HEALTHY, unless the issue has a Status: Sick label.

Add the use statement in our test

Before we dive into writing that method, jump back into our test and chop off the last couple of letters for GithubService. With a little PHPStorm Magic... as soon as I type the letter i and hit enter, the use statement is automatically added to the test. Thank you JetBrains!

... lines 1 - 5
use App\Service\GithubService;
... lines 7 - 8
class GithubServiceTest extends TestCase
{
... lines 11 - 13
public function testGetHealthReportReturnsCorrectHealthStatusForDino(HealthStatus $expectedStatus, string $dinoName): void
{
$service = new GithubService();
... lines 17 - 18
}
... lines 20 - 32
}

Let's see how the tests are looking:

./vendor/bin/phpunit

And... Ha! Instead of two failures, we now only have one...

Sick Dino failed asserting that the two variables reference the same object.

Coming up next, we'll add some logic to our GithubService to make this test pass!

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "symfony/asset": "6.1.*", // v6.1.0
        "symfony/console": "6.1.*", // v6.1.4
        "symfony/dotenv": "6.1.*", // v6.1.0
        "symfony/flex": "^2", // v2.2.3
        "symfony/framework-bundle": "6.1.*", // v6.1.4
        "symfony/http-client": "6.1.*", // v6.1.4
        "symfony/runtime": "6.1.*", // v6.1.3
        "symfony/twig-bundle": "6.1.*", // v6.1.1
        "symfony/yaml": "6.1.*" // v6.1.4
    },
    "require-dev": {
        "phpunit/phpunit": "^9.5", // 9.5.23
        "symfony/browser-kit": "6.1.*", // v6.1.3
        "symfony/css-selector": "6.1.*", // v6.1.3
        "symfony/phpunit-bridge": "^6.1" // v6.1.3
    }
}