cdn77/test-utils

Cdn77 Test Utils for PHP

v0.2.0 2021-02-10 09:47 UTC

README

GitHub Actions Shepherd Type Code Coverage Downloads Packagist Infection MSI

Contents

Installation

  • Require this project as composer dev dependency:
composer require --dev cdn77/test-utils

Features

Stub

Factory to create object through Reflection in order to bypass the constructor.

<?php

class MyEntity 
{
    /** @var string */
    private $property1;

    /** @var string */
    private $property2;

    public function __construct(string $property1, string $property2) 
    {
        $this->property1 = $property1;
        $this->property2 = $property2;
    }

    public function salute() : string 
    {
        return sprintf('Hello %s!', $this->property2);
    }
}

When testing method salute(), you only need the tested class to have property2 set, you don't want to worry about property1. Therefore in your test you can initialize MyEntity using Stub::create() like this:

$myEntity = Stub::create(MyEntity::class, ['property2' => 'world']);

self::assertSame('Hello world!', $myEntity->salute());

It comes handy when class constructor has more arguments and most of them are not required for your test.

It is possible to extend stubs:

$myEntity = Stub::create(MyEntity::class, ['property2' => 'world']);
$myEntity = Stub::extends($myEntity, ['property1' => 'value']);

// property 1 and 2 are set now
self::assertSame('Hello world!', $myEntity->salute());

Test Checks

Test Checks are used to assert that tests comply with your suite's standards (are final, extend correct TestCaseBase etc.)

To run them, eg. create a test case like in the following example:

<?php

use Cdn77\TestUtils\TestCheck\TestCheck;
use PHPUnit\Framework\TestCase;

/**
 * @group       integration
 * @testedClass none
 */
final class SuiteComplianceTest extends TestCaseBase
{
    /** @dataProvider providerChecks */
    public function testChecks(TestCheck $check) : void
    {
        $check->run($this);
    }

    /** @return Generator<string, array{callable(self): TestCheck}> */
    public function providerChecks() : Generator
    {
        $testDir = ROOT_PROJECT_DIR . '/tests';
        $testFilePathNames = \Symfony\Component\Finder\Finder::create()
            ->in($testDir)
            ->files()
            ->name('*Test.php');

        yield 'Every test has group' => [
            new EveryTestHasGroup($testFilePathNames),
        ];
        
        ...
    }
}

Every test has group

Asserts that all tests have a @group annotation

final class FooTest extends TestCase

✔️

/** @group unit */
final class FooTest extends TestCase

Configured in test provider as

yield 'Every test has group' => [
    new EveryTestHasGroup($testFiles),
];

Every test has same namespace as tested class

Asserts that all test share same namespace with class they're testing.
Consider src namespace Ns and test namespace Ns/Tests then for test Ns/Tests/UnitTest must exist class Ns/Unit.

You can use @testedClass annotation to link test with tested class

namespace Ns;

final class Unit {} 

namespace Ns\Tests;

final class NonexistentUnitTest extends TestCase {}
namespace Ns\Tests\Sub;

final class UnitTest extends TestCase {}

✔️

namespace Ns\Tests;

final class UnitTest extends TestCase {}
namespace Ns\Tests\Sub;

/** @testedClass \Ns\Unit */
final class UnitTest extends TestCase {}

Configured in test provider as

yield 'Every test has same namespace as tested class' => [
    new EveryTestHasSameNamespaceAsTestedClass($testFiles),
];

Every test inherits from testcase base class

Consider you have a base for all tests and want each of them extend it.

abstract class TestCaseBase extends \PHPUnit\Framework\TestCase {}

final class FooTest extends \PHPUnit\Framework\TestCase

✔️

final class FooTest extends TestCaseBase

Configured in test provider as

yield 'Every test inherits from TestCase Base Class' => [
    new EveryTestInheritsFromTestCaseBaseClass(
        $testFiles,
        TestCaseBase::class
    ),
];

Every test is final

Asserts all tests are final so they cannot be extended

class FooTest extends TestCase

✔️

final class FooTest extends TestCase

Configured in test provider as

yield 'Every test is final' => [
    new EveryTestIsFinal($testFiles),
];