Skip to content

Commit

Permalink
Enable named data sets with the #[TestWith*] attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
HypeMC committed Nov 16, 2023
1 parent a205409 commit cbc9765
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 8 deletions.
9 changes: 8 additions & 1 deletion src/Framework/Attributes/TestWith.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@
final readonly class TestWith
{
private array $data;
private ?string $name;

public function __construct(array $data)
public function __construct(array $data, ?string $name = null)
{
$this->data = $data;
$this->name = $name;
}

public function data(): array
{
return $this->data;
}

public function name(): ?string
{
return $this->name;
}
}
9 changes: 8 additions & 1 deletion src/Framework/Attributes/TestWithJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
* @psalm-var non-empty-string
*/
private string $json;
private ?string $name;

/**
* @psalm-param non-empty-string $json
*/
public function __construct(string $json)
public function __construct(string $json, ?string $name = null)
{
$this->json = $json;
$this->name = $name;
}

/**
Expand All @@ -39,4 +41,9 @@ public function json(): string
{
return $this->json;
}

public function name(): ?string
{
return $this->name;
}
}
15 changes: 14 additions & 1 deletion src/Metadata/Api/DataProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,20 @@ private function dataProvidedByMetadata(MetadataCollection $testWith): array
foreach ($testWith as $_testWith) {
assert($_testWith instanceof TestWith);

$result[] = $_testWith->data();
$key = $_testWith->name();

if (null === $key) {
$result[] = $_testWith->data();
} elseif (array_key_exists($key, $result)) {
throw new InvalidDataProviderException(
sprintf(
'The key "%s" has already been defined by a previous TestWith attribute',
$key,
),
);
} else {
$result[$key] = $_testWith->data();
}
}

return $result;
Expand Down
4 changes: 2 additions & 2 deletions src/Metadata/Metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,9 @@ public static function testDoxOnMethod(string $text): TestDox
return new TestDox(self::METHOD_LEVEL, $text);
}

public static function testWith(array $data): TestWith
public static function testWith(array $data, ?string $name = null): TestWith
{
return new TestWith(self::METHOD_LEVEL, $data);
return new TestWith(self::METHOD_LEVEL, $data, $name);
}

/**
Expand Down
7 changes: 5 additions & 2 deletions src/Metadata/Parser/AttributeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,17 @@ public function forMethod(string $className, string $methodName): MetadataCollec
case TestWith::class:
assert($attributeInstance instanceof TestWith);

$result[] = Metadata::testWith($attributeInstance->data());
$result[] = Metadata::testWith($attributeInstance->data(), $attributeInstance->name());

break;

case TestWithJson::class:
assert($attributeInstance instanceof TestWithJson);

$result[] = Metadata::testWith(json_decode($attributeInstance->json(), true, 512, JSON_THROW_ON_ERROR));
$result[] = Metadata::testWith(
json_decode($attributeInstance->json(), true, 512, JSON_THROW_ON_ERROR),
$attributeInstance->name(),
);

break;

Expand Down
9 changes: 8 additions & 1 deletion src/Metadata/TestWith.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
final class TestWith extends Metadata
{
private readonly array $data;
private readonly ?string $name;

/**
* @psalm-param 0|1 $level
*/
protected function __construct(int $level, array $data)
protected function __construct(int $level, array $data, ?string $name = null)
{
parent::__construct($level);

$this->data = $data;
$this->name = $name;
}

/**
Expand All @@ -40,4 +42,9 @@ public function data(): array
{
return $this->data;
}

public function name(): ?string
{
return $this->name;
}
}
10 changes: 10 additions & 0 deletions tests/_files/Metadata/Attribute/tests/TestWithTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,19 @@ public function testOne(): void
$this->assertTrue(true);
}

#[TestWith([1, 2, 3], 'Name1')]
public function testOneWithName(): void
{
}

#[TestWithJson('[1, 2, 3]')]
public function testTwo(): void
{
$this->assertTrue(true);
}

#[TestWithJson('[1, 2, 3]', 'Name2')]
public function testTwoWithName(): void
{
}
}
30 changes: 30 additions & 0 deletions tests/_files/TestWithAttributeDataProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TestFixture;

use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;

final class TestWithAttributeDataProviderTest extends TestCase
{
#[TestWith(['a', 'b'], 'foo')]
#[TestWith(['c', 'd'], 'bar')]
#[TestWith(['e', 'f'])]
#[TestWith(['g', 'h'])]
public function testWithAttribute($one, $two): void
{
}

#[TestWith(['a', 'b'], 'foo')]
#[TestWith(['c', 'd'], 'foo')]
public function testWithDuplicateName($one, $two): void
{
}
}
22 changes: 22 additions & 0 deletions tests/unit/Metadata/Api/DataProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PHPUnit\Framework\TestCase;
use PHPUnit\TestFixture\DuplicateKeyDataProviderTest;
use PHPUnit\TestFixture\MultipleDataProviderTest;
use PHPUnit\TestFixture\TestWithAttributeDataProviderTest;
use PHPUnit\TestFixture\VariousIterableDataProviderTest;

#[CoversClass(DataProvider::class)]
Expand Down Expand Up @@ -162,4 +163,25 @@ public function testWithDuplicateKeyDataProviders(): void
/* @noinspection UnusedFunctionResultInspection */
(new DataProvider)->providedData(DuplicateKeyDataProviderTest::class, 'test');
}

public function testTestWithAttribute(): void
{
$dataSets = (new DataProvider)->providedData(TestWithAttributeDataProviderTest::class, 'testWithAttribute');

$this->assertSame([
'foo' => ['a', 'b'],
'bar' => ['c', 'd'],
0 => ['e', 'f'],
1 => ['g', 'h'],
], $dataSets);
}

public function testTestWithAttributeWithDuplicateKey(): void
{
$this->expectException(InvalidDataProviderException::class);
$this->expectExceptionMessage('The key "foo" has already been defined by a previous TestWith attribute');

/* @noinspection UnusedFunctionResultInspection */
(new DataProvider)->providedData(TestWithAttributeDataProviderTest::class, 'testWithDuplicateName');
}
}
24 changes: 24 additions & 0 deletions tests/unit/Metadata/Parser/AttributeParserTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,18 @@ public function test_parses_TestWith_attribute_on_method(): void
$this->assertCount(1, $metadata);
$this->assertTrue($metadata->asArray()[0]->isTestWith());
$this->assertSame([1, 2, 3], $metadata->asArray()[0]->data());
$this->assertNull($metadata->asArray()[0]->name());
}

#[TestDox('Parses #[TestWith] attribute with name on method')]
public function test_parses_TestWith_attribute_with_name_on_method(): void
{
$metadata = $this->parser()->forMethod(TestWithTest::class, 'testOneWithName')->isTestWith();

$this->assertCount(1, $metadata);
$this->assertTrue($metadata->asArray()[0]->isTestWith());
$this->assertSame([1, 2, 3], $metadata->asArray()[0]->data());
$this->assertSame('Name1', $metadata->asArray()[0]->name());
}

#[TestDox('Parses #[TestWithJson] attribute on method')]
Expand All @@ -873,6 +885,18 @@ public function test_parses_TestWithJson_attribute_on_method(): void
$this->assertCount(1, $metadata);
$this->assertTrue($metadata->asArray()[0]->isTestWith());
$this->assertSame([1, 2, 3], $metadata->asArray()[0]->data());
$this->assertNull($metadata->asArray()[0]->name());
}

#[TestDox('Parses #[TestWithJson] attribute with name on method')]
public function test_parses_TestWithJson_attribute_with_name_on_method(): void
{
$metadata = $this->parser()->forMethod(TestWithTest::class, 'testTwoWithName')->isTestWith();

$this->assertCount(1, $metadata);
$this->assertTrue($metadata->asArray()[0]->isTestWith());
$this->assertSame([1, 2, 3], $metadata->asArray()[0]->data());
$this->assertSame('Name2', $metadata->asArray()[0]->name());
}

#[TestDox('Parses #[Ticket] attribute on method')]
Expand Down

0 comments on commit cbc9765

Please sign in to comment.