Skip to content

Commit

Permalink
feat(Testing): AssertExpectations automatically checks if expectation…
Browse files Browse the repository at this point in the history
…s were called

BREAKING CHANGE: Due the changes the expectation logic has been changed and you need to update your code:

- `LaraStrict\Testing\AbstractExpectationCallsMap` moved to `LaraStrict\Testing\Assert\AbstractExpectationCallsMap`
- Every generated Assert needs to be updated (regenerate or manually change it):
     - Add `parent::construct()` the the constructor
     - Add missing `|null` to expectations phpdoc if missing
     - Remove `array_values(array_filter(` usage (not required)
     - Example src/Testing/Context/Contracts/ContextServiceContractAssert.php
- `LaraStrict\Testing\AbstractExpectationCallMap` was removed in favor of `AbstractExpectationCallsMap`
     - example of change: `src/Testing/Database/Contracts/SafeUniqueSaveActionContractAssert.php`
     - Extend AbstractExpectationCallsMap
     - Add constructor method and call parent
     - Add `array $expectations = []` to the constructor
     - Move template value from `@extends` to phpdoc for $expectations
     - Remove `@extends` phpdoc
     - Add expectation class to `getExpectation call`
```php
    /**
     * @param array<SafeUniqueSaveActionContractExpectation|null> $expectations
     */
    public function __construct(array $expectations = [])
    {
        parent::__construct();

        $this->setExpectations(AppConfigContractGetVersionExpectation::class, $expectations);
    }
….
$expectation = $this->getExpectation(AppConfigContractGetVersionExpectation::class);
```
- To automatically assert if expectations were used use `AssertExpectationTestCase` or `AssertExpectationManagerTrait`
  • Loading branch information
pionl committed Jun 27, 2023
1 parent 21d7ab2 commit acc90a9
Show file tree
Hide file tree
Showing 56 changed files with 554 additions and 375 deletions.
84 changes: 0 additions & 84 deletions src/Testing/AbstractExpectationCallMap.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace LaraStrict\Testing;
namespace LaraStrict\Testing\Assert;

use LogicException;

Expand All @@ -23,6 +23,11 @@ abstract class AbstractExpectationCallsMap
*/
private int $_currentDebugStep = 0;

public function __construct()
{
AssertExpectationManager::getInstance()->register($this);
}

public function addExpectation(object $expectation): self
{
$this->_expectationMap[$expectation::class][] = $expectation;
Expand All @@ -34,28 +39,34 @@ public function addExpectation(object $expectation): self
* @template TExpectation
*
* @param class-string<TExpectation> $class
* @param array<TExpectation> $expectations
* @param array<TExpectation|null> $expectations
*/
public function setExpectations(string $class, array $expectations): self
{
$this->_expectationMap[$class] = $expectations;
$this->_expectationMap[$class] = array_values(array_filter($expectations));
$this->_callStep[$class] = 0;

return $this;
}

public function assertCalled(): void
{
$errors = [];
foreach ($this->_expectationMap as $class => $expectations) {
$called = $this->_callStep[$class] ?? 0;
$expected = count($expectations);
if ($expected === $called) {
continue;
}

throw new LogicException(
sprintf('[%s] expected %d call/s but was called <%d> time/s', $class, $expected, $called)
);
$errors[] = sprintf('[%s] expected %d call/s but was called <%d> time/s', $class, $expected, $called);
}

if ($errors === []) {
return;
}

throw new LogicException(implode(PHP_EOL, array_map(static fn (string $e) => $e, $errors)));
}

/**
Expand Down
62 changes: 62 additions & 0 deletions src/Testing/Assert/AssertExpectationManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace LaraStrict\Testing\Assert;

use PHPUnit\Framework\Assert;
use Throwable;

final class AssertExpectationManager
{
private static ?self $singleton = null;

/**
* @var array<AbstractExpectationCallsMap>
*/
private array $currentExpectations = [];

public static function getInstance(): self
{
if (self::$singleton === null) {
self::$singleton = new self();
}

return self::$singleton;
}

public function register(AbstractExpectationCallsMap $map): void
{
$this->currentExpectations[] = $map;
}

public function assertCalled(): void
{
$errors = [];
foreach ($this->currentExpectations as $map) {
try {
$map->assertCalled();
} catch (Throwable $throwable) {
$errors[] = $throwable;
}
}

// We must some assert due the assertion count
if ($errors === []) {
Assert::assertEmpty($errors);
return;
}

Assert::fail(implode(PHP_EOL, array_map(static fn ($e) => $e->getMessage(), $errors)));
}

public function hasExpectations(): bool
{
return $this->currentExpectations !== [];
}

public function reset(): void
{
$this->currentExpectations = [];
}
}
13 changes: 13 additions & 0 deletions src/Testing/Assert/AssertExpectationTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace LaraStrict\Testing\Assert;

use LaraStrict\Testing\Assert\Traits\AssertExpectationManagerTrait;
use PHPUnit\Framework\TestCase;

abstract class AssertExpectationTestCase extends TestCase
{
use AssertExpectationManagerTrait;
}
36 changes: 36 additions & 0 deletions src/Testing/Assert/Traits/AssertExpectationManagerTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace LaraStrict\Testing\Assert\Traits;

use LaraStrict\Testing\Assert\AssertExpectationManager;

trait AssertExpectationManagerTrait
{
protected function setUp(): void
{
parent::setUp();

AssertExpectationManager::getInstance()->reset();
}

protected function assertPostConditions(): void
{
$manager = AssertExpectationManager::getInstance();

if ($manager->hasExpectations()) {
$this->addToAssertionCount(1);
$manager->assertCalled();
}

parent::assertPostConditions();
}

protected function tearDown(): void
{
AssertExpectationManager::getInstance()->reset();

parent::tearDown();
}
}
26 changes: 12 additions & 14 deletions src/Testing/Cache/Contracts/CacheMeServiceContractAssert.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
use LaraStrict\Cache\Constants\CacheExpirations;
use LaraStrict\Cache\Contracts\CacheMeServiceContract;
use LaraStrict\Cache\Enums\CacheMeStrategy;
use LaraStrict\Testing\AbstractExpectationCallsMap;
use LaraStrict\Testing\Assert\AbstractExpectationCallsMap;
use PHPUnit\Framework\Assert;

class CacheMeServiceContractAssert extends AbstractExpectationCallsMap implements CacheMeServiceContract
{
/**
* @param array<CacheMeServiceContractGetExpectation> $get
* @param array<CacheMeServiceContractSetExpectation> $set
* @param array<CacheMeServiceContractFlushExpectation> $flush
* @param array<CacheMeServiceContractDeleteExpectation> $delete
* @param array<CacheMeServiceContractObserveAndFlushExpectation> $observeAndFlush
* @param array<CacheMeServiceContractGetExpectation|null> $get
* @param array<CacheMeServiceContractSetExpectation|null> $set
* @param array<CacheMeServiceContractFlushExpectation|null> $flush
* @param array<CacheMeServiceContractDeleteExpectation|null> $delete
* @param array<CacheMeServiceContractObserveAndFlushExpectation|null> $observeAndFlush
*/
public function __construct(
array $get = [],
Expand All @@ -28,14 +28,12 @@ public function __construct(
array $delete = [],
array $observeAndFlush = [],
) {
$this->setExpectations(CacheMeServiceContractGetExpectation::class, array_values(array_filter($get)));
$this->setExpectations(CacheMeServiceContractSetExpectation::class, array_values(array_filter($set)));
$this->setExpectations(CacheMeServiceContractFlushExpectation::class, array_values(array_filter($flush)));
$this->setExpectations(CacheMeServiceContractDeleteExpectation::class, array_values(array_filter($delete)));
$this->setExpectations(
CacheMeServiceContractObserveAndFlushExpectation::class,
array_values(array_filter($observeAndFlush))
);
parent::__construct();
$this->setExpectations(CacheMeServiceContractGetExpectation::class, $get);
$this->setExpectations(CacheMeServiceContractSetExpectation::class, $set);
$this->setExpectations(CacheMeServiceContractFlushExpectation::class, $flush);
$this->setExpectations(CacheMeServiceContractDeleteExpectation::class, $delete);
$this->setExpectations(CacheMeServiceContractObserveAndFlushExpectation::class, $observeAndFlush);
}

/**
Expand Down

0 comments on commit acc90a9

Please sign in to comment.