Php Data Tester

https://travis-ci.org/mpoiriert/php-data-tester.svg?branch=master

This library is a wrapper around PHPUnit Assert class to be able to use a fluent interface on the data you want to test.

The library can be install via Composer/Packagist.

Here is a quick example of how to use it in a PHPUnit TestCase:

<?php

namespace Your\Project\Name;

use PHPUnit\Framework\TestCase;
use Draw\DataTester\Tester;

class SimpleTest extends TestCase
{
    public function test()
    {
        $data = [
          'key1' => 'value1',
          'key2' => (object)['toto' => 'value']
        ];

        $tester = new Tester($data);
        $tester->assertInternalType('array')
            ->assertCount(2)
            ->path('[key1]')->assertSame('value1');
        $tester->path('[key2].toto')->assertSame('value');
}

There is a lot more features available, just Read the Docs!

Getting Started

Requirements

This library is compatible with currently supported version of PHPUnit versions (^6.0|^5.7) and PHP (^5.6|^7.0). You can check the CI test on travis https://travis-ci.org/mpoiriert/php-data-tester.

Create a Data Tester

From a PHPUnit test case you simply create a new Draw\DataTester\Tester instance:

Example: Simple Test
<?php
namespace Your\Project\Name;

use PHPUnit\Framework\TestCase;
use Draw\DataTester\Tester;

class ExampleTest extends TestCase
{
    public function test()
    {
        $dataToTest = 'A string value';

        $tester = new Tester($dataToTest);
        $tester
            ->assertInternalType('string')
            ->assertSame('A string value');
    }

}

The Tester use a fluent interface by returning himself on all of the assert* methods and most of his methods. This allow to easily make multiple test on the same data.

If you don’t need a reference to the tester you can be even more concise:

Example: New Concise
(new Tester('A string value'))
    ->assertInternalType('string')
    ->assertSame('A string value');

Path

For more complex data (array, object) you can use the path method to test something deeper in the data itself:

Example: Path
(new Tester((object)["key" => "value"]))
    ->path('key')
    ->assertSame('value');

By Using the path method you are making a assertion that the path is accessible. It also return a new Tester instance with the data of the path to be tested.

Example: Path Callable
(new Tester((object)["key" => "value"]))
    ->path('key')
    ->assertSame('value');

The library use behind it is the symfony/property-access, make sure you read the doc https://symfony.com/doc/current/components/property_access.html

Chaining Path

Since the path method return a new Tester you must keep a reference on the original Tester if you want to test other path.

Example: Chain Path
$tester = new Tester((object)["key1" => "value1", "key2" => "value2"]);
$tester->path('key1')->assertSame('value1');
$tester->path('key2')->assertSame('value2');

Deep Path

If you have a deeper object you can call path in chain:

Example: Deeper Path
(new Tester((object)["level1" => (object)["level2" => "value"]]))
    ->path('level1')
    ->path('level2')->assertSame('value');

Each

If your data is iterable trough foreach you can test all the entry via a callable:

Example: Each
(new Tester(['value1', 'value2']))
    ->each(
        function (Tester $tester) {
            $tester->assertInternalType('string');
        }
    );

Advance

Here is a list of advance examples that you can follow to make your test more efficient and maintainable.

Optional Path

Considering you have a complex structure with optional path into it. You can use the method ifPathIsReadable to make some test optional:

Example: If Path Is Readable
(new Tester(null))
    ->ifPathIsReadable(
        'notExistingPath',
        function (Tester $tester) {
            //Will not be call with current data to test
        }
    );

This obviously make more sense with a combination of each. In this more complex example lets say you receive a list of users object that don’t have the same properties available:

Example If Path Is Readable And Each
$users = [
    (object)[
        'firstName' => 'Martin',
        'active' => true,
        'referral' => 'Google'
    ],
    (object)[
        'firstName' => 'Julie',
        'active' => false
    ]
];
(new Tester($users))
    ->each(
        function (Tester $tester) {
            $tester->path('firstName')->assertInternalType('string');
            $tester->path('active')->assertInternalType('boolean');
            $tester->ifPathIsReadable(
                'referral',
                function (Tester $tester) {
                    $tester->assertInternalType('string');
                }
            );
        }
    );

Transform

If you need to transform the data during the test you can call the transform method with a callable as the first argument for the transformation. It will return a new Tester with the transformed data to test.

Let’s you have a json string as data, that you want to test the content, it will look like this:

Example: Transform
(new Tester('{"key":"value"}'))
    ->transform('json_decode')
    ->path('key')->assertSame('value');

Ideally you should test your data before transforming it:

Example: Assert Before Transform
(new Tester('{"key":"value"}'))
    ->assertJson()
    ->transform('json_decode')
    ->path('key')->assertSame('value');

If you would like to transform the data but not with the default values of callable you can simply create a custom callable with the appropriate option. Let say you want json_decode with a associative array:

Example: Assert Before Transform-custom
(new Tester('{"key":"value"}'))
    ->assertJson()
    ->transform(
        function ($data) {
            return json_decode($data, true);
        }
    )->path('[key]')->assertSame('value');

Take a note that since it’s a associative array the path must be change from key to [key].

Reusable Test Callable

Some time you want to execute the same set of test on different source of data. You have a method that return you a user and one that return you a list of users. You can simply create a class that as all the test inside of it.

Example: Class Callable
<?php\nnamespace Your\Project\Name;\nuse Draw\DataTester\Tester;
class UserDataTester
{
    public function __invoke(Tester $tester)
    {
        $tester->path('firstName')->assertInternalType('string');
        $tester->path('active')->assertInternalType('boolean');
        $tester->ifPathIsReadable(
            'referral',
            function (Tester $tester) {
                $tester->assertInternalType('string');
            }
        );
    }
}

And now you can use it to test the data of one user:

Example: Test With Class Callable
$user = (object)[
    'firstName' => 'Martin',
    'active' => true,
    'referral' => 'Google'
];

(new Tester($user))
    ->test(new UserDataTester());

Or with each in case of a list of users:

Example: Each With Class Callable
$users = [
    (object)[
        'firstName' => 'Martin',
        'active' => true,
        'referral' => 'Google'
    ],
    (object)[
        'firstName' => 'Julie',
        'active' => false
    ]
];

(new Tester($users))
    ->each(new UserDataTester());

Asserts

The list of asserts available are a sub-set of the PHPUnit Assert available.

Some of the methods have been remove since they are replicable trough a combination of path and another assert. Other are not available either for compatibility issues. If you think that some must be added just open a issue in the git repository.

For a more exhaustive documentation please refer to PHPUnit Documentation. Do not forgot that all the asserts are not available and that the $this->getData() replace the data you want to test that is normally pass trough the PHPUnit Assert methods.

assertArraySubset

p

/**
 * Asserts that an array has a specified subset.
 *
 * @param array|ArrayAccess $subset
 * @param bool $strict Check for object identity
 * @param string $message
 * @return $this
 */
public function assertArraySubset($subset, $strict = false, $message = '')
{
    Assert::assertArraySubset($subset, $this->getData(), $strict, $message);

    return $this;
}

assertContains

p
/**
 * Asserts that a haystack contains a needle.
 *
 * @param mixed $needle
 * @param string $message
 * @param bool $ignoreCase
 * @param bool $checkForObjectIdentity
 * @param bool $checkForNonObjectIdentity
 * @return $this
 */
public function assertContains($needle, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    Assert::assertContains($needle, $this->getData(), $message, $ignoreCase, $checkForObjectIdentity, $checkForNonObjectIdentity);

    return $this;
}

assertNotContains

p
/**
 * Asserts that a haystack does not contain a needle.
 *
 * @param mixed $needle
 * @param string $message
 * @param bool $ignoreCase
 * @param bool $checkForObjectIdentity
 * @param bool $checkForNonObjectIdentity
 * @return $this
 */
public function assertNotContains($needle, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
{
    Assert::assertNotContains($needle, $this->getData(), $message, $ignoreCase, $checkForObjectIdentity, $checkForNonObjectIdentity);

    return $this;
}

assertContainsOnly

p
/**
 * Asserts that a haystack contains only values of a given type.
 *
 * @param string $type
 * @param bool $isNativeType
 * @param string $message
 * @return $this
 */
public function assertContainsOnly($type, $isNativeType = NULL, $message = '')
{
    Assert::assertContainsOnly($type, $this->getData(), $isNativeType, $message);

    return $this;
}

assertContainsOnlyInstancesOf

p
/**
 * Asserts that a haystack contains only instances of a given classname
 *
 * @param string $classname
 * @param string $message
 * @return $this
 */
public function assertContainsOnlyInstancesOf($classname, $message = '')
{
    Assert::assertContainsOnlyInstancesOf($classname, $this->getData(), $message);

    return $this;
}

assertNotContainsOnly

p
/**
 * Asserts that a haystack does not contain only values of a given type.
 *
 * @param string $type
 * @param bool $isNativeType
 * @param string $message
 * @return $this
 */
public function assertNotContainsOnly($type, $isNativeType = NULL, $message = '')
{
    Assert::assertNotContainsOnly($type, $this->getData(), $isNativeType, $message);

    return $this;
}

assertCount

p
/**
 * Asserts the number of elements of an array, Countable or Traversable.
 *
 * @param int $expectedCount
 * @param string $message
 * @return $this
 */
public function assertCount($expectedCount, $message = '')
{
    Assert::assertCount($expectedCount, $this->getData(), $message);

    return $this;
}

assertNotCount

p
/**
 * Asserts the number of elements of an array, Countable or Traversable.
 *
 * @param int $expectedCount
 * @param string $message
 * @return $this
 */
public function assertNotCount($expectedCount, $message = '')
{
    Assert::assertNotCount($expectedCount, $this->getData(), $message);

    return $this;
}

assertEquals

p
/**
 * Asserts that two variables are equal.
 *
 * @param mixed $expected
 * @param string $message
 * @param float $delta
 * @param int $maxDepth
 * @param bool $canonicalize
 * @param bool $ignoreCase
 * @return $this
 */
public function assertEquals($expected, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    Assert::assertEquals($expected, $this->getData(), $message, $delta, $maxDepth, $canonicalize, $ignoreCase);

    return $this;
}

assertNotEquals

p
/**
 * Asserts that two variables are not equal.
 *
 * @param mixed $expected
 * @param string $message
 * @param float $delta
 * @param int $maxDepth
 * @param bool $canonicalize
 * @param bool $ignoreCase
 * @return $this
 */
public function assertNotEquals($expected, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
    Assert::assertNotEquals($expected, $this->getData(), $message, $delta, $maxDepth, $canonicalize, $ignoreCase);

    return $this;
}

assertEmpty

p
/**
 * Asserts that a variable is empty.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertEmpty($message = '')
{
    Assert::assertEmpty($this->getData(), $message);

    return $this;
}

assertNotEmpty

p
/**
 * Asserts that a variable is not empty.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertNotEmpty($message = '')
{
    Assert::assertNotEmpty($this->getData(), $message);

    return $this;
}

assertGreaterThan

p
/**
 * Asserts that a value is greater than another value.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertGreaterThan($expected, $message = '')
{
    Assert::assertGreaterThan($expected, $this->getData(), $message);

    return $this;
}

assertGreaterThanOrEqual

p
/**
 * Asserts that a value is greater than or equal to another value.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertGreaterThanOrEqual($expected, $message = '')
{
    Assert::assertGreaterThanOrEqual($expected, $this->getData(), $message);

    return $this;
}

assertLessThan

p
/**
 * Asserts that a value is smaller than another value.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertLessThan($expected, $message = '')
{
    Assert::assertLessThan($expected, $this->getData(), $message);

    return $this;
}

assertLessThanOrEqual

p
/**
 * Asserts that a value is smaller than or equal to another value.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertLessThanOrEqual($expected, $message = '')
{
    Assert::assertLessThanOrEqual($expected, $this->getData(), $message);

    return $this;
}

assertTrue

p
/**
 * Asserts that a condition is true.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertTrue($message = '')
{
    Assert::assertTrue($this->getData(), $message);

    return $this;
}

assertNotTrue

p
/**
 * Asserts that a condition is not true.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertNotTrue($message = '')
{
    Assert::assertNotTrue($this->getData(), $message);

    return $this;
}

assertFalse

p
/**
 * Asserts that a condition is false.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertFalse($message = '')
{
    Assert::assertFalse($this->getData(), $message);

    return $this;
}

assertNotFalse

p
/**
 * Asserts that a condition is not false.
 *
 * @param string $message
 *
 * @return $this
 */
public function assertNotFalse($message = '')
{
    Assert::assertNotFalse($this->getData(), $message);

    return $this;
}

assertNull

p
/**
 * Asserts that a variable is null.
 *
 * @param string $message
 * @return $this
 */
public function assertNull($message = '')
{
    Assert::assertNull($this->getData(), $message);

    return $this;
}

assertNotNull

p
/**
 * Asserts that a variable is not null.
 *
 * @param string $message
 * @return $this
 */
public function assertNotNull($message = '')
{
    Assert::assertNotNull($this->getData(), $message);

    return $this;
}

assertFinite

p
/**
 * Asserts that a variable is finite.
 *
 * @param string $message
 * @return $this
 */
public function assertFinite($message = '')
{
    Assert::assertFinite($this->getData(), $message);

    return $this;
}

assertInfinite

p
/**
 * Asserts that a variable is infinite.
 *
 * @param string $message
 * @return $this
 */
public function assertInfinite($message = '')
{
    Assert::assertInfinite($this->getData(), $message);

    return $this;
}

assertNan

p
/**
 * Asserts that a variable is nan.
 *
 * @param string $message
 * @return $this
 */
public function assertNan($message = '')
{
    Assert::assertNan($this->getData(), $message);

    return $this;
}

assertSame

p
/**
 * Asserts that two variables have the same type and value.
 * Used on objects, it asserts that two variables reference
 * the same object.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertSame($expected, $message = '')
{
    Assert::assertSame($expected, $this->getData(), $message);

    return $this;
}

assertNotSame

p
/**
 * Asserts that two variables do not have the same type and value.
 * Used on objects, it asserts that two variables do not reference
 * the same object.
 *
 * @param mixed $expected
 * @param string $message
 * @return $this
 */
public function assertNotSame($expected, $message = '')
{
    Assert::assertNotSame($expected, $this->getData(), $message);

    return $this;
}

assertInstanceOf

p
/**
 * Asserts that a variable is of a given type.
 *
 * @param string $expected
 * @param string $message
 * @return $this
 */
public function assertInstanceOf($expected, $message = '')
{
    Assert::assertInstanceOf($expected, $this->getData(), $message);

    return $this;
}

assertNotInstanceOf

p
/**
 * Asserts that a variable is not of a given type.
 *
 * @param string $expected
 * @param string $message
 * @return $this
 */
public function assertNotInstanceOf($expected, $message = '')
{
    Assert::assertNotInstanceOf($expected, $this->getData(), $message);

    return $this;
}

assertInternalType

p
/**
 * Asserts that a variable is of a given type.
 *
 * @param string $expected
 * @param string $message
 * @return $this
 */
public function assertInternalType($expected, $message = '')
{
    Assert::assertInternalType($expected, $this->getData(), $message);

    return $this;
}

assertNotInternalType

p
/**
 * Asserts that a variable is not of a given type.
 *
 * @param string $expected
 * @param string $message
 * @return $this
 */
public function assertNotInternalType($expected, $message = '')
{
    Assert::assertNotInternalType($expected, $this->getData(), $message);

    return $this;
}

assertRegExp

p
/**
 * Asserts that a string matches a given regular expression.
 *
 * @param string $pattern
 * @param string $message
 * @return $this
 */
public function assertRegExp($pattern, $message = '')
{
    Assert::assertRegExp($pattern, $this->getData(), $message);

    return $this;
}

assertNotRegExp

p
/**
 * Asserts that a string does not match a given regular expression.
 *
 * @param string $pattern
 * @param string $message
 * @return $this
 */
public function assertNotRegExp($pattern, $message = '')
{
    Assert::assertNotRegExp($pattern, $this->getData(), $message);

    return $this;
}

assertSameSize

p
/**
 * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
 * is the same.
 *
 * @param array|Countable|Traversable $expected
 * @param string $message
 * @return $this
 */
public function assertSameSize($expected, $message = '')
{
    Assert::assertSameSize($expected, $this->getData(), $message);

    return $this;
}

assertNotSameSize

p
/**
 * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
 * is not the same.
 *
 * @param array|Countable|Traversable $expected
 * @param string $message
 * @return $this
 */
public function assertNotSameSize($expected, $message = '')
{
    Assert::assertNotSameSize($expected, $this->getData(), $message);

    return $this;
}

assertStringMatchesFormat

p
/**
 * Asserts that a string matches a given format string.
 *
 * @param string $format
 * @param string $message
 * @return $this
 */
public function assertStringMatchesFormat($format, $message = '')
{
    Assert::assertStringMatchesFormat($format, $this->getData(), $message);

    return $this;
}

assertStringNotMatchesFormat

p
/**
 * Asserts that a string does not match a given format string.
 *
 * @param string $format
 * @param string $message
 * @return $this
 */
public function assertStringNotMatchesFormat($format, $message = '')
{
    Assert::assertStringNotMatchesFormat($format, $this->getData(), $message);

    return $this;
}

assertStringStartsNotWith

p
/**
 * Asserts that a string starts not with a given prefix.
 *
 * @param string $prefix
 * @param string $message
 * @return $this
 */
public function assertStringStartsNotWith($prefix, $message = '')
{
    Assert::assertStringStartsNotWith($prefix, $this->getData(), $message);

    return $this;
}

assertStringEndsWith

p
/**
 * Asserts that a string ends with a given suffix.
 *
 * @param string $suffix
 * @param string $message
 * @return $this
 */
public function assertStringEndsWith($suffix, $message = '')
{
    Assert::assertStringEndsWith($suffix, $this->getData(), $message);

    return $this;
}

assertStringEndsNotWith

p
/**
 * Asserts that a string ends not with a given suffix.
 *
 * @param string $suffix
 * @param string $message
 * @return $this
 */
public function assertStringEndsNotWith($suffix, $message = '')
{
    Assert::assertStringEndsNotWith($suffix, $this->getData(), $message);

    return $this;
}

assertJson

p
/**
 * Asserts that a string is a valid JSON string.
 *
 * @param string $message
 * @return $this
 */
public function assertJson($message = '')
{
    Assert::assertJson($this->getData(), $message);

    return $this;
}

assertJsonStringEqualsJsonString

p
/**
 * Asserts that two given JSON encoded objects or arrays are equal.
 *
 * @param string $expectedJson
 * @param string $message
 * @return $this
 */
public function assertJsonStringEqualsJsonString($expectedJson, $message = '')
{
    Assert::assertJsonStringEqualsJsonString($expectedJson, $this->getData(), $message);

    return $this;
}

assertJsonStringNotEqualsJsonString

p
/**
 * Asserts that two given JSON encoded objects or arrays are not equal.
 *
 * @param string $expectedJson
 * @param string $message
 * @return $this
 */
public function assertJsonStringNotEqualsJsonString($expectedJson, $message = '')
{
    Assert::assertJsonStringNotEqualsJsonString($expectedJson, $this->getData(), $message);

    return $this;
}