Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable named data sets with the #[TestWith*] attributes #4964

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 readonly class TestWith extends Metadata
{
private array $data;
private ?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;
}
}
12 changes: 12 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,21 @@ public function testOne(): void
$this->assertTrue(true);
}

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

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

#[TestWithJson('[1, 2, 3]', 'Name2')]
public function testTwoWithName(): void
{
$this->assertTrue(true);
}
}
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
{
}
}
20 changes: 16 additions & 4 deletions tests/end-to-end/event/testwith-attribute.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,37 @@ unlink($traceFile);
--EXPECTF--
PHPUnit Started (PHPUnit %s using %s)
Test Runner Configured
Test Suite Loaded (2 tests)
Test Suite Loaded (4 tests)
Event Facade Sealed
Test Runner Started
Test Suite Sorted
Test Runner Execution Started (2 tests)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 2 tests)
Test Runner Execution Started (4 tests)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 4 tests)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne, 1 test)
Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0)
Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0)
Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0)
Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne#0)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOne, 1 test)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName, 1 test)
Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1)
Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1)
Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1)
Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName#Name1)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testOneWithName, 1 test)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo, 1 test)
Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0)
Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0)
Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0)
Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo#0)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwo, 1 test)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 2 tests)
Test Suite Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName, 1 test)
Test Preparation Started (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2)
Test Prepared (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2)
Test Passed (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2)
Test Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName#Name2)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest::testTwoWithName, 1 test)
Test Suite Finished (PHPUnit\TestFixture\Metadata\Attribute\TestWithTest, 4 tests)
Test Runner Execution Finished
Test Runner Finished
PHPUnit Finished (Shell Exit Code: 0)
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