Compare commits

...

18 Commits

Author SHA1 Message Date
dd2921fd26 tests: fixes snapshots 2021-06-10 19:26:42 +01:00
977dbb5bcb docs: updates changelog 2021-06-10 19:21:02 +01:00
49de462250 Adds support for incompleted tests 2021-06-10 19:20:16 +01:00
95e8add29b Merge pull request #303 from def-studio/matrix-datasets
Matrix datasets
2021-06-10 18:43:31 +01:00
b682fe631d chore: bumps expectation plugin 2021-06-10 18:40:38 +01:00
d32a648af5 updated test snapshots after merge from master 2021-06-10 09:03:32 +02:00
50e9978dc3 Merge branch 'master' into matrix-datasets
# Conflicts:
#	tests/.snapshots/success.txt
2021-06-10 09:01:09 +02:00
7408999b0e Merge branch 'master' into matrix-datasets 2021-06-06 23:59:54 +02:00
36c2a985a6 Merge branch 'pestphp:master' into matrix-datasets 2021-06-03 12:19:15 +02:00
ea0be9e7a4 Refactored Datasets::resolve() to make it more readable 2021-05-27 08:46:38 +02:00
838ac273ab writed tests with multiple datasets
Took 1 hour 6 minutes
2021-05-25 23:56:46 +02:00
296e1c37e8 updates snapshots
Took 7 minutes
2021-05-24 23:43:53 +02:00
3117f11fae phpstan fixes
Took 3 minutes
2021-05-24 23:36:51 +02:00
294c41f0dc lint fixes
Took 3 minutes
2021-05-24 23:33:43 +02:00
60afbb2c20 adds new test to check dataset matrix generation
Took 39 seconds
2021-05-24 23:30:50 +02:00
19a45c856e updates Dataset::resolve to generate a matrix of values from multiple datasets
Took 42 seconds
2021-05-24 23:30:11 +02:00
3b784060b8 updated TestCaseFactory.php to store multiple datasets
Took 44 seconds
2021-05-24 23:29:29 +02:00
dd5a11a61f updated TestCall.php to store multiple datasets
Took 1 hour 59 minutes
2021-05-24 23:28:46 +02:00
12 changed files with 318 additions and 36 deletions

View File

@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [v.1.3.2 (2021-06-07)](https://github.com/pestphp/pest/compare/v1.3.1...v1.3.2)
## [v1.4.0 (2021-06-10)](https://github.com/pestphp/pest/compare/v1.3.2...v1.4.0)
### Added
- Support for multiple datasets (Matrix) on the `with` method ([#303](https://github.com/pestphp/pest/pull/303))
- Support for incompleted tests ([49de462](https://github.com/pestphp/pest/commit/49de462250cf9f65f09e13eaf6dcc0e06865b930))
## [v1.3.2 (2021-06-07)](https://github.com/pestphp/pest/compare/v1.3.1...v1.3.2)
### Fixed
- Test cases with the @ symbol in the directory fail ([#308](https://github.com/pestphp/pest/pull/308))

View File

@ -21,7 +21,7 @@
"nunomaduro/collision": "^5.0",
"pestphp/pest-plugin": "^1.0",
"pestphp/pest-plugin-coverage": "^1.0",
"pestphp/pest-plugin-expectations": "^1.3",
"pestphp/pest-plugin-expectations": "^1.6",
"pestphp/pest-plugin-init": "^1.1",
"phpunit/phpunit": ">= 9.3.7 <= 9.5.5"
},

View File

@ -51,36 +51,34 @@ final class Datasets
/**
* Resolves the current dataset to an array value.
*
* @param Traversable<int|string, mixed>|Closure|iterable<int|string, mixed>|string|null $data
* @param array<Closure|iterable<int|string, mixed>|string> $datasets
*
* @return array<string, mixed>
*/
public static function resolve(string $description, $data): array
public static function resolve(string $description, array $datasets): array
{
/* @phpstan-ignore-next-line */
if (is_null($data) || empty($data)) {
if (empty($datasets)) {
return [$description => []];
}
if (is_string($data)) {
$data = self::get($data);
}
$datasets = self::processDatasets($datasets);
if (is_callable($data)) {
$data = call_user_func($data);
}
if ($data instanceof Traversable) {
$data = iterator_to_array($data);
}
$datasetCombinations = self::getDataSetsCombinations($datasets);
$dataSetDescriptions = [];
$dataSetValues = [];
foreach ($data as $key => $values) {
$values = is_array($values) ? $values : [$values];
foreach ($datasetCombinations as $datasetCombination) {
$partialDescriptions = [];
$values = [];
$dataSetDescriptions[] = $description . self::getDataSetDescription($key, $values);
foreach ($datasetCombination as $dataset_data) {
$partialDescriptions[] = $dataset_data['label'];
$values = array_merge($values, $dataset_data['values']);
}
$dataSetDescriptions[] = $description . ' with ' . implode(' / ', $partialDescriptions);
$dataSetValues[] = $values;
}
@ -103,6 +101,65 @@ final class Datasets
return $namedData;
}
/**
* @param array<Closure|iterable<int|string, mixed>|string> $datasets
*
* @return array<array>
*/
private static function processDatasets(array $datasets): array
{
$processedDatasets = [];
foreach ($datasets as $index => $data) {
$processedDataset = [];
if (is_string($data)) {
$datasets[$index] = self::get($data);
}
if (is_callable($datasets[$index])) {
$datasets[$index] = call_user_func($datasets[$index]);
}
if ($datasets[$index] instanceof Traversable) {
$datasets[$index] = iterator_to_array($datasets[$index]);
}
foreach ($datasets[$index] as $key => $values) {
$values = is_array($values) ? $values : [$values];
$processedDataset[] = [
'label' => self::getDataSetDescription($key, $values),
'values' => $values,
];
}
$processedDatasets[] = $processedDataset;
}
return $processedDatasets;
}
/**
* @param array<array> $combinations
*
* @return array<array>
*/
private static function getDataSetsCombinations(array $combinations): array
{
$result = [[]];
foreach ($combinations as $index => $values) {
$tmp = [];
foreach ($result as $resultItem) {
foreach ($values as $value) {
$tmp[] = array_merge($resultItem, [$index => $value]);
}
}
$result = $tmp;
}
return $result;
}
/**
* @param int|string $key
* @param array<int, mixed> $data
@ -112,9 +169,9 @@ final class Datasets
$exporter = new Exporter();
if (is_int($key)) {
return \sprintf(' with (%s)', $exporter->shortenedRecursiveExport($data));
return \sprintf('(%s)', $exporter->shortenedRecursiveExport($data));
}
return \sprintf(' with data set "%s"', $key);
return \sprintf('data set "%s"', $key);
}
}

View File

@ -11,9 +11,9 @@ use Pest\Contracts\HasPrintableTestCaseName;
use Pest\Datasets;
use Pest\Exceptions\ShouldNotHappen;
use Pest\Support\HigherOrderMessageCollection;
use Pest\Support\NullClosure;
use Pest\Support\Str;
use Pest\TestSuite;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
use RuntimeException;
@ -62,9 +62,9 @@ final class TestCaseFactory
/**
* Holds the dataset, if any.
*
* @var Closure|iterable<int|string, mixed>|string|null
* @var array<Closure|iterable<int|string, mixed>|string>
*/
public $dataset;
public $datasets = [];
/**
* The FQN of the test case class.
@ -113,7 +113,11 @@ final class TestCaseFactory
{
$this->filename = $filename;
$this->description = $description;
$this->test = $closure ?? NullClosure::create();
$this->test = $closure ?? function (): void {
if (Assert::getCount() === 0) {
self::markTestIncomplete(); // @phpstan-ignore-line
}
};
$this->factoryProxies = new HigherOrderMessageCollection();
$this->proxies = new HigherOrderMessageCollection();
@ -155,7 +159,7 @@ final class TestCaseFactory
return $testCase;
};
$datasets = Datasets::resolve($this->description, $this->dataset);
$datasets = Datasets::resolve($this->description, $this->datasets);
return array_map($createTest, array_keys($datasets), $datasets);
}

View File

@ -77,11 +77,13 @@ final class TestCall
* Runs the current test multiple times with
* each item of the given `iterable`.
*
* @param \Closure|iterable<int|string, mixed>|string $data
* @param array<\Closure|iterable<int|string, mixed>|string> $data
*/
public function with($data): TestCall
public function with(...$data): TestCall
{
$this->testCaseFactory->dataset = $data;
foreach ($data as $dataset) {
$this->testCaseFactory->datasets[] = $dataset;
}
return $this;
}

View File

@ -51,6 +51,45 @@
✓ it creates unique test case names with ('Name 2', Pest\Plugin Object (), true)
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #3
✓ it creates unique test case names - count
✓ lazy multiple datasets with (1) / (3)
✓ lazy multiple datasets with (1) / (4)
✓ lazy multiple datasets with (2) / (3)
✓ lazy multiple datasets with (2) / (4)
✓ lazy multiple datasets did the job right
✓ eager multiple datasets with (1) / (3)
✓ eager multiple datasets with (1) / (4)
✓ eager multiple datasets with (2) / (3)
✓ eager multiple datasets with (2) / (4)
✓ eager multiple datasets did the job right
✓ lazy registered multiple datasets with (1) / (1)
✓ lazy registered multiple datasets with (1) / (2)
✓ lazy registered multiple datasets with (2) / (1)
✓ lazy registered multiple datasets with (2) / (2)
✓ lazy registered multiple datasets did the job right
✓ eager registered multiple datasets with (1) / (1)
✓ eager registered multiple datasets with (1) / (2)
✓ eager registered multiple datasets with (2) / (1)
✓ eager registered multiple datasets with (2) / (2)
✓ eager registered multiple datasets did the job right
✓ eager wrapped registered multiple datasets with (1) / (1)
✓ eager wrapped registered multiple datasets with (1) / (2)
✓ eager wrapped registered multiple datasets with (2) / (1)
✓ eager wrapped registered multiple datasets with (2) / (2)
✓ eager wrapped registered multiple datasets did the job right
✓ named multiple datasets with data set "one" / data set "three"
✓ named multiple datasets with data set "one" / data set "four"
✓ named multiple datasets with data set "two" / data set "three"
✓ named multiple datasets with data set "two" / data set "four"
✓ named multiple datasets did the job right
✓ more than two datasets with (1) / (3) / (5)
✓ more than two datasets with (1) / (3) / (6)
✓ more than two datasets with (1) / (4) / (5)
✓ more than two datasets with (1) / (4) / (6)
✓ more than two datasets with (2) / (3) / (5)
✓ more than two datasets with (2) / (3) / (6)
✓ more than two datasets with (2) / (4) / (5)
✓ more than two datasets with (2) / (4) / (6)
✓ more than two datasets did the job right
PASS Tests\Features\Exceptions
✓ it gives access the the underlying expectException
@ -69,6 +108,15 @@
✓ it proxies calls to object
✓ it is capable doing multiple assertions
WARN Tests\Features\Incompleted
… incompleted
… it is incompleted
… it is incompleted even with method calls like skip
… it is incompleted even with method calls like group
✓ it is not incompleted because of expect
✓ it is not incompleted because of assert
✓ it is not incompleted because of test with assertions
PASS Tests\Features\It
✓ it is a test
✓ it is a higher order message test
@ -78,6 +126,7 @@
✓ it will throw exception from call if no macro exists
PASS Tests\Features\PendingHigherOrderTests
✓ get 'foo'
✓ get 'foo' → get 'bar' → expect true → toBeTrue
✓ get 'foo' → expect true → toBeTrue
@ -174,6 +223,9 @@
PASS Tests\Unit\Datasets
✓ it show only the names of named datasets in their description
✓ it show the actual dataset of non-named datasets in their description
✓ it show only the names of multiple named datasets in their description
✓ it show the actual dataset of multiple non-named datasets in their description
✓ it show the correct description for mixed named and not-named datasets
PASS Tests\Unit\Plugins\Version
✓ it outputs the version when --version is used
@ -224,5 +276,5 @@
✓ it is a test
✓ it uses correct parent class
Tests: 7 skipped, 122 passed
Tests: 4 incompleted, 7 skipped, 168 passed

View File

@ -8,11 +8,11 @@ beforeAll(function () use ($foo) {
});
it('gets executed before tests', function () use ($foo) {
expect($foo->bar)->toBe(1);
expect($foo)->bar->toBe(1);
$foo->bar = 'changed';
});
it('do not get executed before each test', function () use ($foo) {
expect($foo->bar)->toBe('changed');
expect($foo)->bar->toBe('changed');
});

View File

@ -137,3 +137,89 @@ it('creates unique test case names', function (string $name, Plugin $plugin, boo
it('creates unique test case names - count', function () use (&$counter) {
expect($counter)->toBe(6);
});
$datasets_a = [[1], [2]];
$datasets_b = [[3], [4]];
test('lazy multiple datasets', function ($text_a, $text_b) use ($state, $datasets_a, $datasets_b) {
$state->text .= $text_a . $text_b;
expect($datasets_a)->toContain([$text_a]);
expect($datasets_b)->toContain([$text_b]);
})->with($datasets_a, $datasets_b);
test('lazy multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('12121212121213142324');
});
$state->text = '';
test('eager multiple datasets', function ($text_a, $text_b) use ($state, $datasets_a, $datasets_b) {
$state->text .= $text_a . $text_b;
expect($datasets_a)->toContain([$text_a]);
expect($datasets_b)->toContain([$text_b]);
})->with(function () use ($datasets_a) {
return $datasets_a;
})->with(function () use ($datasets_b) {
return $datasets_b;
});
test('eager multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('1212121212121314232413142324');
});
test('lazy registered multiple datasets', function ($text_a, $text_b) use ($state, $datasets) {
$state->text .= $text_a . $text_b;
expect($datasets)->toContain([$text_a]);
expect($datasets)->toContain([$text_b]);
})->with('numbers.array')->with('numbers.array');
test('lazy registered multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212131423241314232411122122');
});
test('eager registered multiple datasets', function ($text_a, $text_b) use ($state, $datasets) {
$state->text .= $text_a . $text_b;
expect($datasets)->toContain([$text_a]);
expect($datasets)->toContain([$text_b]);
})->with('numbers.array')->with('numbers.closure');
test('eager registered multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('12121212121213142324131423241112212211122122');
});
test('eager wrapped registered multiple datasets', function ($text_a, $text_b) use ($state, $datasets) {
$state->text .= $text_a . $text_b;
expect($datasets)->toContain([$text_a]);
expect($datasets)->toContain([$text_b]);
})->with('numbers.closure.wrapped')->with('numbers.closure');
test('eager wrapped registered multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('1212121212121314232413142324111221221112212211122122');
});
test('named multiple datasets', function ($text_a, $text_b) use ($state, $datasets_a, $datasets_b) {
$state->text .= $text_a . $text_b;
expect($datasets_a)->toContain([$text_a]);
expect($datasets_b)->toContain([$text_b]);
})->with([
'one' => [1],
'two' => [2],
])->with([
'three' => [3],
'four' => [4],
]);
test('named multiple datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324');
});
test('more than two datasets', function ($text_a, $text_b, $text_c) use ($state, $datasets_a, $datasets_b) {
$state->text .= $text_a . $text_b . $text_c;
expect($datasets_a)->toContain([$text_a]);
expect($datasets_b)->toContain([$text_b]);
expect([5, 6])->toContain($text_c);
})->with($datasets_a, $datasets_b)->with([5, 6]);
test('more than two datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324135136145146235236245246');
});

View File

@ -0,0 +1,17 @@
<?php
test('incompleted');
it('is incompleted');
it('is incompleted even with method calls like skip')->skip(false);
it('is incompleted even with method calls like group')->group('wtv');
it('is not incompleted because of expect')->expect(true)->toBeTrue();
it('is not incompleted because of assert')->assertTrue(true);
it('is not incompleted because of test with assertions', function () {
expect(true)->toBeTrue();
});

View File

@ -1,7 +1,7 @@
<?php
it('is a test', function () {
$this->assertArrayHasKey('key', ['key' => 'foo']);
expect(['key' => 'foo'])->toHaveKey('key')->key->toBeString();
});
it('is a higher order message test')->expect(true)->toBeTrue();

View File

@ -26,5 +26,6 @@ trait Gettable
}
}
get('foo'); // not incomplete because closure is created...
get('foo')->get('bar')->expect(true)->toBeTrue();
get('foo')->expect(true)->toBeTrue();

View File

@ -4,8 +4,10 @@ use Pest\Datasets;
it('show only the names of named datasets in their description', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
'one' => [1],
'two' => [[2]],
[
'one' => [1],
'two' => [[2]],
],
]));
expect($descriptions[0])->toBe('test description with data set "one"');
@ -14,10 +16,66 @@ it('show only the names of named datasets in their description', function () {
it('show the actual dataset of non-named datasets in their description', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
[1],
[[2]],
[
[1],
[[2]],
],
]));
expect($descriptions[0])->toBe('test description with (1)');
expect($descriptions[1])->toBe('test description with (array(2))');
});
it('show only the names of multiple named datasets in their description', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
[
'one' => [1],
'two' => [[2]],
],
[
'three' => [3],
'four' => [[4]],
],
]));
expect($descriptions[0])->toBe('test description with data set "one" / data set "three"');
expect($descriptions[1])->toBe('test description with data set "one" / data set "four"');
expect($descriptions[2])->toBe('test description with data set "two" / data set "three"');
expect($descriptions[3])->toBe('test description with data set "two" / data set "four"');
});
it('show the actual dataset of multiple non-named datasets in their description', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
[
[1],
[[2]],
],
[
[3],
[[4]],
],
]));
expect($descriptions[0])->toBe('test description with (1) / (3)');
expect($descriptions[1])->toBe('test description with (1) / (array(4))');
expect($descriptions[2])->toBe('test description with (array(2)) / (3)');
expect($descriptions[3])->toBe('test description with (array(2)) / (array(4))');
});
it('show the correct description for mixed named and not-named datasets', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
[
'one' => [1],
[[2]],
],
[
[3],
'four' => [[4]],
],
]));
expect($descriptions[0])->toBe('test description with data set "one" / (3)');
expect($descriptions[1])->toBe('test description with data set "one" / data set "four"');
expect($descriptions[2])->toBe('test description with (array(2)) / (3)');
expect($descriptions[3])->toBe('test description with (array(2)) / data set "four"');
});