Skip to content
This repository has been archived by the owner on Jan 4, 2022. It is now read-only.

Commit

Permalink
Merge pull request #337 from ergebnis/feature/specification
Browse files Browse the repository at this point in the history
Enhancement: Extract specifications
  • Loading branch information
ergebnis-bot committed Oct 4, 2020
2 parents 71275e0 + 2ebcf46 commit 1832627
Show file tree
Hide file tree
Showing 13 changed files with 344 additions and 218 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integrate.yaml
Expand Up @@ -10,7 +10,7 @@ on: # yamllint disable-line rule:truthy

env:
ERGEBNIS_BOT_NAME: "ergebnis-bot"
MIN_COVERED_MSI: 94
MIN_COVERED_MSI: 93
MIN_MSI: 91
PHP_EXTENSIONS: "mbstring"

Expand Down
2 changes: 1 addition & 1 deletion Makefile
@@ -1,4 +1,4 @@
MIN_COVERED_MSI:=94
MIN_COVERED_MSI:=93
MIN_MSI:=91

.PHONY: it
Expand Down
37 changes: 36 additions & 1 deletion phpstan-baseline.neon
Expand Up @@ -666,7 +666,7 @@ parameters:
path: test/Unit/DataProvider/AbstractProviderTestCase.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertIsArray\\(\\) with array will always evaluate to true\\.$#"
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertIsArray\\(\\) with array and string will always evaluate to true\\.$#"
count: 1
path: test/Unit/DataProvider/AbstractProviderTestCase.php

Expand Down Expand Up @@ -1255,3 +1255,38 @@ parameters:
count: 1
path: test/Unit/HelperTest.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Closure\\:\\:isSatisfiedBy\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Closure.php

-
message: "#^Property Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Identical\\:\\:\\$value has no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Identical.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Identical\\:\\:__construct\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Identical.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Identical\\:\\:create\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Identical.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Identical\\:\\:isSatisfiedBy\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Identical.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Pattern\\:\\:isSatisfiedBy\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Pattern.php

-
message: "#^Method Ergebnis\\\\Test\\\\Util\\\\Test\\\\Util\\\\DataProvider\\\\Specification\\\\Specification\\:\\:isSatisfiedBy\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: test/Util/DataProvider/Specification/Specification.php

43 changes: 37 additions & 6 deletions psalm-baseline.xml
Expand Up @@ -55,18 +55,15 @@
</UnnecessaryVarAnnotation>
</file>
<file src="test/Unit/DataProvider/AbstractProviderTestCase.php">
<MissingClosureParamType occurrences="1">
<code>$value</code>
</MissingClosureParamType>
<MixedAssignment occurrences="1">
<code>$value</code>
</MixedAssignment>
<RedundantConditionGivenDocblockType occurrences="1">
<code>assertIsArray</code>
</RedundantConditionGivenDocblockType>
<RedundantIdentityWithTrue occurrences="1">
<code>true === $test($value)</code>
</RedundantIdentityWithTrue>
<UnnecessaryVarAnnotation occurrences="1">
<code>Util\Test\Util\DataProvider\Specification\Specification</code>
</UnnecessaryVarAnnotation>
</file>
<file src="test/Unit/Exception/InvalidExcludeClassNameTest.php">
<MixedInferredReturnType occurrences="1">
Expand Down Expand Up @@ -118,4 +115,38 @@
<code>\iterator_to_array</code>
</UnusedFunctionCall>
</file>
<file src="test/Util/DataProvider/Specification/Closure.php">
<MissingClosureParamType occurrences="1">
<code>$value</code>
</MissingClosureParamType>
<MissingParamType occurrences="1">
<code>$value</code>
</MissingParamType>
<MixedInferredReturnType occurrences="1">
<code>bool</code>
</MixedInferredReturnType>
<MixedReturnStatement occurrences="1">
<code>$closure($value)</code>
</MixedReturnStatement>
</file>
<file src="test/Util/DataProvider/Specification/Identical.php">
<MissingParamType occurrences="3">
<code>$value</code>
<code>$value</code>
<code>$value</code>
</MissingParamType>
<MissingPropertyType occurrences="1">
<code>$value</code>
</MissingPropertyType>
</file>
<file src="test/Util/DataProvider/Specification/Pattern.php">
<MissingParamType occurrences="1">
<code>$value</code>
</MissingParamType>
</file>
<file src="test/Util/DataProvider/Specification/Specification.php">
<MissingParamType occurrences="1">
<code>$value</code>
</MissingParamType>
</file>
</files>
171 changes: 54 additions & 117 deletions test/Unit/DataProvider/AbstractProviderTestCase.php
Expand Up @@ -27,72 +27,89 @@ abstract class AbstractProviderTestCase extends Framework\TestCase
* @param array<string, mixed> $values
* @param \Generator<string, array<mixed>> $provider
*/
final protected static function assertProvidesDataForValues(array $values, \Generator $provider): void
final protected static function assertProvidesDataSetsForValues(array $values, \Generator $provider): void
{
self::assertExpectedValuesAreNotEmpty($values);

$expected = \iterator_to_array(self::provideDataForValues($values));
$expectedDataSets = \iterator_to_array(self::provideDataForValues($values));
$actualDataSets = \iterator_to_array($provider);

$provided = \iterator_to_array($provider);

self::assertProvidedDataIsNotEmpty($provided);
self::assertDataSetsAreNotEmpty($actualDataSets);

self::assertEquals(
$expected,
$provided,
'Failed asserting that a generator yields data for expected values.'
$expectedDataSets,
$actualDataSets,
'Failed asserting that a generator yields data sets for the expected values.'
);
}

/**
* @param array<string, \Closure|mixed> $tests
* @param \Generator<string, array<mixed>> $provider
* @param array<string, Util\Test\Util\DataProvider\Specification\Specification> $specifications
* @param \Generator<string, array<mixed>> $provider
*/
final protected static function assertProvidesDataForValuesPassingTests(array $tests, \Generator $provider): void
final protected static function assertProvidesDataSetsForValuesSatisfyingSpecifications(array $specifications, \Generator $provider): void
{
$provided = \iterator_to_array($provider);
self::assertContainsOnly(
'string',
\array_keys($specifications),
true,
'Failed asserting that the keys of specifications are all strings.'
);

self::assertContainsOnly(
Util\Test\Util\DataProvider\Specification\Specification::class,
$specifications,
false,
\sprintf(
'Failed asserting that the values of specifications implement "%s".',
Util\Test\Util\DataProvider\Specification\Specification::class
)
);

$dataSets = \iterator_to_array($provider);

self::assertEquals(
\array_keys($tests),
\array_keys($provided),
'Failed asserting that the provided data has the same keys as the tests.'
\array_keys($specifications),
\array_keys($dataSets),
'Failed asserting that the provided data has the same keys as the specifications.'
);

$normalizedTests = \array_map(static function ($test): \Closure {
if (!$test instanceof \Closure) {
return static function ($value) use ($test): bool {
return $value === $test;
};
}
$keysForDataSetsWhereValueDoesNotSatisfySpecification = \array_filter(\array_keys($dataSets), static function (string $key) use ($dataSets, $specifications): bool {
/** @var Util\Test\Util\DataProvider\Specification\Specification $specification */
$specification = $specifications[$key];

return $test;
}, $tests);
$dataSet = $dataSets[$key];

$keysWhereValueDoesNotPassTest = \array_filter(\array_keys($provided), static function (string $key) use ($provided, $normalizedTests): bool {
$set = $provided[$key];
self::assertIsArray($dataSet, \sprintf(
'Failed asserting that the data set provided for key "%s" is an array.',
$key
));

self::assertIsArray($set);
self::assertCount(1, $set);
self::assertCount(1, $dataSet, \sprintf(
'Failed asserting that the data set provided for key "%s" contains only one value.',
$key
));

$value = \array_shift($set);
$test = $normalizedTests[$key];
$value = \array_shift($dataSet);

return true !== $test($value);
return !$specification->isSatisfiedBy($value);
});

self::assertEquals(
[],
$keysWhereValueDoesNotPassTest,
'Failed asserting that all values pass the corresponding tests.'
);
self::assertEquals([], $keysForDataSetsWhereValueDoesNotSatisfySpecification, \sprintf(
'Failed asserting that the value for the data sets with the keys "%s" satisfy the corresponding requirements.',
\implode(
'", "',
$keysForDataSetsWhereValueDoesNotSatisfySpecification
)
));
}

/**
* @param array $actual
*/
final protected static function assertProvidedDataIsNotEmpty(array $actual): void
final protected static function assertDataSetsAreNotEmpty(array $actual): void
{
self::assertNotEmpty($actual, 'Failed asserting that provided values are not empty.');
self::assertNotEmpty($actual, 'Failed asserting that provided data sets are not empty.');
}

/**
Expand All @@ -102,84 +119,4 @@ private static function assertExpectedValuesAreNotEmpty(array $values): void
{
self::assertNotEmpty($values, 'Failed asserting that expected values are not empty.');
}

/**
* @param \Closure $test
* @param array<string, mixed> $provided
*/
private static function assertProvidedDataContainsArraysWhereFirstElementPassesTest(\Closure $test, array $provided): void
{
self::assertProvidedDataContainsArraysWithOneElement($provided);

$value = \array_map(static function (array $set) {
return \array_shift($set);
}, $provided);

$tested = \array_filter($value, static function ($value) use ($test): bool {
return true === $test($value);
});

self::assertEquals(
$value,
$tested,
'Failed asserting that the first value in each array passed the test.'
);
}

/**
* @param \Closure $test
* @param array<string, mixed> $provided
*/
private static function assertProvidedDataContainsArraysWhereFirstElementDoesNotPassTest(\Closure $test, array $provided): void
{
self::assertProvidedDataContainsArraysWithOneElement($provided);

$value = \array_map(static function (array $set) {
return \array_shift($set);
}, $provided);

$tested = \array_filter($value, static function ($value) use ($test): bool {
return false === $test($value);
});

self::assertEquals(
$value,
$tested,
'Failed asserting that the first value in each array does not pass the test.'
);
}

/**
* @param array<string, mixed> $provided
*/
private static function assertProvidedDataContainsArraysWithOneElement(array $provided): void
{
self::assertProvidedDataContainsArraysOnly($provided);

$setsWhereNumberOfProvidedArgumentsIsNotOne = \array_filter($provided, static function (array $set): bool {
return 1 !== \count($set);
});

self::assertEquals(
[],
$setsWhereNumberOfProvidedArgumentsIsNotOne,
'Failed asserting that each set in the provided data contains only a single value.'
);
}

/**
* @param array<string, mixed> $provided
*/
private static function assertProvidedDataContainsArraysOnly(array $provided): void
{
$values = \array_filter($provided, static function ($set): bool {
return !\is_array($set);
});

self::assertEquals(
[],
$values,
'Failed asserting that each value is an array.'
);
}
}
21 changes: 11 additions & 10 deletions test/Unit/DataProvider/BoolProviderTest.php
Expand Up @@ -14,6 +14,7 @@
namespace Ergebnis\Test\Util\Test\Unit\DataProvider;

use Ergebnis\Test\Util\DataProvider\BoolProvider;
use Ergebnis\Test\Util\Test\Util;

/**
* @internal
Expand All @@ -34,14 +35,14 @@ public function testArbitraryProvidesBool($value): void

public function testArbitraryReturnsGeneratorThatProvidesBoolValues(): void
{
$values = [
'bool-false' => false,
'bool-true' => true,
$specifications = [
'bool-false' => Util\DataProvider\Specification\Identical::create(false),
'bool-true' => Util\DataProvider\Specification\Identical::create(true),
];

$provider = BoolProvider::arbitrary();

self::assertProvidesDataForValues($values, $provider);
self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider);
}

/**
Expand All @@ -56,13 +57,13 @@ public function testFalseProvidesFalse($value): void

public function testFalseReturnsGeneratorThatProvidesFalse(): void
{
$values = [
'bool-false' => false,
$specifications = [
'bool-false' => Util\DataProvider\Specification\Identical::create(false),
];

$provider = BoolProvider::false();

self::assertProvidesDataForValues($values, $provider);
self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider);
}

/**
Expand All @@ -77,12 +78,12 @@ public function testTrueProvidesTrue($value): void

public function testTrueReturnsGeneratorThatProvidesTrue(): void
{
$values = [
'bool-true' => true,
$specifications = [
'bool-true' => Util\DataProvider\Specification\Identical::create(true),
];

$provider = BoolProvider::true();

self::assertProvidesDataForValues($values, $provider);
self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider);
}
}

0 comments on commit 1832627

Please sign in to comment.