Handy require-dev testing tool for PhpUnit. It allows to manage data providers
with zip(), join(), cross(), pairs(), slice(), map() and more.
Installation for PHP 7.1 and later:
composer require --dev rawr/phpunit-data-providerDataProvider can be used to build, compose and edit data providers to be used with PhpUnit
by @sebastianbergmann.
DataProvider::list() provides a list of elements. Test is invoked each time with a single argument.
/**
* @test
* @dataProvider colors
*/
public function test(string $color): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::list('blue', 'yellow', 'red');
}Additionally, DataProvider::list() names rows based on values.
Vertically join data providers together.
đź’ˇ Useful when two data providers are used in other tests, and a new test should use both of them.
/**
* @test
* @dataProvider colors
*/
public function test(string $color, string $thing): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::join($this->blueColors(), $this->yellowColors(), $this->redColors());
}
public function blueColors(): DataProvider {
return DataProvider::tuples(
['blue', 'sky'],
['deep blue', 'ocean']
);
}
public function yellowColors(): iterable {
yield 'sun' => ['yellow', 'sun'];
}
public function redColors(): array {
return [
'apple' => ['red', 'apple']
];
}Note:
- Only data provider with equal amounts of arguments in rows can be joined.
DataProvider.drop()can be used to trim overflowing columns, orDataProvider::zip()to widen data provider with less rows.
Horizontally join data providers together.
đź’ˇ Useful for keeping data providers clean and simple.
/**
* @test
* @dataProvider colors
*/
public function test($blueColor, $blueThing, $adjective, Factory $factory): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::zip($this->blueThings(), $this->adjectives(), $this->factories());
}
public function blueThings(): DataProvider {
return DataProvider::tuples(
['blue', 'ink'],
['light blue', 'shirt'],
['deep blue', 'ocean']
);
}
public function adjectives(): iterable {
return DataProvider::list('liquid', 'comfortable', 'majestic');
}
public function factories(): iterable {
yield [new InkFactory()];
yield [new ShirtFactory()];
yield [new OceanFactory()];
}Note:
- Only data provider with equal amounts of rows can be zipped.
DataProvider.slice()can be used to trim overflowing rows, orDataProvider::join()to prolong a shorter data provider.
Creates a square matrix of given data providers.
đź’ˇ Useful for testing all combinations of arguments.
/**
* @test
* @dataProvider colorsAndThings
*/
public function test(string $color, string $shade): void {
// your test here
}
public function colorsAndThings(): DataProvider {
return DataProvider::cross($this->colors(), $this->things());
}
public function colors(): array {
return [
['blue'], ['yellow'], ['red']
];
}
public function things(): iterable {
return DataProvider::list('sky', 'sun', 'apple');
}Calls test with two arguments. Each argument is paired with all of the other arguments. All rows are named according to the arguments.
Example shows a test paring image formats:
/**
* @test
* @dataProvider formats
*/
public function shouldConvertFile(string $from, string $to): void {
// your test here
}
public function formats(): array {
return DataProviders::distinctPairs('png', 'jpg', 'bmp');
}Instantiates a DataProvider from a raw-array accepted by PhpUnit.
public function example(): DataProvider {
return DataProvider::of($this->rawArray());
}
public function rawArrayDataProvider(): array {
return [
'key' => ['argument 1', 'argument 2']
];
}Notes:
- Also accepts
iterable,\Generatorand other types accepted by PhpUnit.
Provide multiple arguments for each a test. DataProvider::tuples() names each row based on the values.
/**
* @test
* @dataProvider colors
*/
public function test(string $color, string $thing): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::tuples(
['blue', 'sky'],
['yellow', 'sun'],
['red', 'apple']
);
}Specify a single argument for test. DataProvider::dictionary() names each row based on the
provided array key.
/**
* @test
* @dataProvider colors
*/
public function test(string $color): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::dictionary([
'custom name 1' => 'blue',
'custom name 2' => 'yellow',
'custom name 3' => 'red'
]);
}In most cases, DataProvider::list() or DataProvider::tuples() should be used to name
rows based on arguments. Method DataProvider::dictionary() is useful when the arguments are not self-explanatory:
public function ports(): DataProvider {
return DataProvider::dictionary([
'http' => 80,
'https' => 443,
'ftp' => 21
]);
}Transform each row's values in DataProvider to any other set of values.
đź’ˇ Useful for separating providers content and their form.
/**
* @test
* @dataProvider folderIterators
*/
public function test(\Iterator $iterator, string $name): void {
// your test here
}
public function folderIterators(): DataProvider {
return $this->folders()->map(function (string $name, string $path): array {
return [
new \DirectoryIterator($path),
$name
];
});
}
public function folders(): DataProvider {
return DataProvider::tuples(
['temporary', '/tmp'],
['user directory', '/home'],
['system resources', '/usr/bin']);
}Notes:
- Names in
DataProviderwill be preserved.
Remove leading or trailing rows from DataProvider.
đź’ˇ Useful for adapting DataProvider to be zipped or limit provided values.
/**
* @test
* @dataProvider limitedColors
*/
public function test(string $color, string $thing): void {
// your test here
}
public function limitedColors(): DataProvider {
return $this->colors()->slice(0, 2);
}
public function colors(): DataProvider {
return DataProvider::tuples(
['blue', 'sky'],
['yellow', 'sun'],
['red', 'apple']
);
}Provide two arguments for each a test, from key-value pairs.
DataProvider::entries() names each row based on the key-value pair.
/**
* @test
* @dataProvider colors
*/
public function test(string $color, string $thing): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::entries(
'blue' => 'sky',
'yellow' => 'sun',
'red' => 'apple',
);
}-
Creating new data providers:
-
Composing existing providers:
-
Editing existing providers:
DataProvider.map(),DataProvider.slice(),DataProvider.drop(). These methods don't modifyDataProviderinstance, but return a new instance.
- Clear naming
- Each
DataProviderbuilder sets proper names for rows, based on values.
- Each
- Duplicate keys:
- Duplicate keys are properly handled and formatted in an informative manner. No rows are ever being "lost" when editing.
- Lazy evaluation:
- Iterators are being evaluated only when called. Argument iterators are only called once, even if
DataProvideris called multiple times.
- Iterators are being evaluated only when called. Argument iterators are only called once, even if
DataProvideraccepts many provider types.
DataProvider sets proper names for each row based on values.
/**
* @test
* @dataProvider colors
*/
public function test(string $color, string $thing): void {
// your test here
}
public function colors(): DataProvider {
return DataProvider::tuples(
['blue', 'sky'],
['yellow', 'sun'],
['red', 'apple']
);
}DataProvider::cross() returns an instance of DataProvider which is a square matrix of input data providers.
/**
* @test
* @dataProvider services
*/
public function shouldLogin(string $service, string $method, int $port): void {
// your test here
}
public function services(): DataProvider {
return DataProvider::cross(
[
['github.com'], ['bitbucket.com'], ['gitlab.com'], ['sourceforge.net']
],
[
['http', 80],
['https', 443],
['ssh', 22]
]);
}This is equivalent of having a regular data provider that is composed of 12 entries, similar to:
public function services(): array {
return [
['github.com', 'http', 80],
['github.com', 'https', 443],
['github.com', 'ssh', 22],
['bitbucket.com', 'http', 80],
['bitbucket.com', 'https', 443],
['bitbucket.com', 'ssh', 22],
['gitlab.com', 'http', 80],
['gitlab.com', 'https', 443],
['gitlab.com', 'ssh', 22],
['sourceforge.net', 'http', 80],
['sourceforge.net', 'https', 443],
['sourceforge.net', 'ssh', 22],
];
}DataProvider::cross() accepts data providers of different
types: array, \Iterator, \IteratorAggregate, \Traversable, \Generator,
iterable and DataProvider.
That means DataProvider can be composed together.
public function services(): DataProvider {
return DataProvider::cross(
DataProvider::list('github.com', 'bitbucket.com', 'gitlab.com', 'sourceforge.net'),
DataProvider::tuples(['http', 80], ['https', 443], ['ssh', 22]));
}DataProvider can be combined with other DataProviders as well as regular PhpUnit data providers.
/**
* @test
* @dataProvider urls
*/
public function test0(string $url): void {
// your test here
}
/**
* @test
* @dataProvider services
*/
public function test1(string $url, string $name, string $method, int $port): void {
// your test here
}
/**
* @test
* @dataProvider allServices
*/
public function test2(string $url, string $name, string $method, int $port): void {
// your test here
}
public function urls(): DataProvider {
return DataProvider::list('github.com', 'bitbucket.com', 'gitlab.com', 'sourceforge.net');
}
public function rawArrayProvider(): array {
return [
['GitHub'],
['BitBucket'],
['GitLab'],
['SourceForge']
];
}
public function services(): DataProvider {
return DataProvider::cross(
DataProvider::zip($this->urls(), $this->rawArrayProvider()),
DataProvider::tuples(
['http', 80],
['https', 443],
['ssh', 22]));
}
public function allServices(): DataProvider {
return DataProvider::join(
$this->services(),
$this->localServices()
);
}
public function localServices(): array {
return [
'my local service' => ['localhost', 'local', 'http', '80']
];
}DataProvider accepts any type of data provider:
- all types allowed
by PhpUnit:
array,iterable,\Traversable,\Iterator,\IteratorAggregate,\Generator DataProvideritself, allowing data providers to be composed together
Notes on DataProvider::join():
DataProvider::join()preserves names of each data provider, and also joins the names vertically. Duplicates in titles are preserved and presented appropriately.DataProvider::join()accepts any type of data-provider.DataProvider::join()is conceptually similar to calling\array_merge()on raw-array providers, but\array_merge()would override duplicate keys, whileDataProvider::join()preserves duplicate keys, and titles them appropriately.DataProvider::join()variadic arguments...iterableand can be used to join many data providersDataProvider::join()can only join data providers with the same amount of arguments in each row, otherwiseIrregularDataProviderExceptionis thrown.DataProvider::join()acceptsDataProvideror otheriterableaccepted by PhpUnit. If improper data-provider is passed,MalformedDataProviderExceptionis thrown.
Notes on DataProvider::zip():
DataProvider::zip()preserves names of each data provider, and also joins them horizontally.DataProvider::zip()accepts any type of data-provider.DataProvider::zip()variadic arguments...iterableand can zip many data providersDataProvider::zip()can only zip data providers with the same amount of rows, otherwiseIrregularDataProviderExceptionis thrown. Additionally, each particular data provider must have the same amount of arguments in each row.DataProvider::zip()acceptsDataProvideror otheriterableaccepted by PhpUnit. If improper data-provider is passed,MalformedDataProviderExceptionis thrown.
Note on DataProvider::pairs():
DataProvider::pairs()produces duplicate pairs (for example'png', 'png'), whileDataProvider::distinctPairs()only makes pairs of different arguments.
Note on DataProvider::tuples():
DataProvider::tuples()is similar toDataProvider::of(), but::of()accepts an explicit name, whileDataProvider::tuples()titles the rows according to the values in the row.
To use version 3.0.0, migrating from 2.4.0 or earlier:
- Library namespace changed from
\TRegx\DataProvider\to\TRegx\PhpUnit\DataProviders\. - Change
\TRegx\DataProvider\DataProviders::cross()to\TRegx\PhpUnit\DataProviders\DataProvider::cross(). - Change
\TRegx\DataProvider\CrossDataProviders::cross()to\TRegx\PhpUnit\DataProviders\DataProvider::cross(). - Change your data providers return type from
arraytoiterableor\TRegx\PhpUnit\DataProviders\DataProvider. - Removed
\TRegx\DataProvider\CrossDataProviders::builder(), use\TRegx\PhpUnit\DataProviders\DataProvider::cross()instead.












