Skip to content

ksamborski/phpintegration

Repository files navigation

phpintegration

PHP library for writing automated tests. It can help you get started especially when you do TDD and value randomized testing.

Build Status

Installation

composer install ksamborski/php-integration

Basic usage

You write test. That's cool. And when you write test that uses random data that's even cooler. But what happens if your test find a bug? You fix it and tries again but the test is random and you cannot simple rerun it. You need to change the test's code, run it and when everything's ok hopefully not forget to remove your changes in test's code. You can also use this library.

First of all let's define some tests:

use PHPIntegration\TestParameter;
use PHPIntegration\Test;
use PHPIntegration\TestGroup;
use PHPIntegration\Console;

$groups = [
    new TestGroup(
        "Basic tests",
        [
            new Test(
                "Test1",
                "Simple test 1",
                function ($p) {
                    usleep(rand(10000, 100000));
                    return true;
                }
            ),
            new Test(
                "Test2",
                "Failing test",
                function ($p) {
                    return "this is a test that always fails";
                }
            )
        ]
    )
];

Test is a simple object that has a name, a description and a function that receives parameters. Don't worry about it now, we will cover it later. That function is your test, it should return true when everything's ok and some message explaining what is wrong otherwise.

Now to avoid changing test code when something fails let's introduce dynamic parameters:

$params = function() {
    return [
        TestParameter::manyFromParameter("departments", ["Warsaw", "Berlin"], ["Warsaw", "Berlin", "Cracow"]),
        TestParameter::stringParameter("currency", "PLN"),
        TestParameter::regexParameter("date", "2015-01-01", "/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/"),
        TestParameter::arrayOfParameter("hours", [12], '\PHPIntegration\TestParameter::intParameter')
    ];
};

These parameters are of course objects. Depending of your needs you can define a string parameter, regex one, parameter of predefined values etc. or custom one. Parameter usually takes a name and default value. This default value can be override at run time. I'll show you later.

One thing lacking is console. We need to initialize console interface (CLI) to get some way to use it.

Console::main($groups, $params);

It takes two arguments: array of tests and function generating dynamic parameters. It is a function in the latter case because we need some way to randomize them again after each iteration when we will run some test n times.

Now let's run it:

php basic_example.php

Basic tests [pre] [ OK ] 
>> Test1 [ OK ] 48.12 ms
>> Test2 [ FAILED ] 0.00 ms
Test description: 
Failing test
Parameters: 
- departments:[Warsaw,Berlin]
- currency:PLN
- date:2015-01-01
- hours:[12]
Message: 
this is a test that always fails
Basic tests [post] [ OK ] 

Now let's override some parameter:

php basic_example.php -p "currency:EUR"
Basic tests [pre] [ OK ] 
>> Test1 [ OK ] 24.58 ms
>> Test2 [ FAILED ] 0.00 ms
Test description: 
Failing test
Parameters: 
- departments:[Warsaw,Berlin]
- currency:EUR
- date:2015-01-01
- hours:[12]
Message: 
this is a test that always fails
Basic tests [post] [ OK ] 

OK, but how to use them in a test? Remember the $p argument in tests that we defined previously? That's the parameters map. To read for example the currency you can write:

new Test(
        "Test1",
        "Simple test 1",
        function($p) {
            return $p['currency'];
        }
    )

What if we forget what parameters we can pass? CLI for the rescue!

php basic_example.php -h
Usage: php basic_example.php [OPTIONS]

  -g, --group GROUP_NAME 				 Run only tests from given groups (you can pass multiple -g option) 
  -t, --test TEST_NAME 					 Run only given tests (you can pass multiple -t option) 
  -p, --parameter PARAMETER_NAME:PARAMETER_VALUE 	 Set test parameter (you can pass multiple -p option) 
  -n 							 Number of repeats 

  -h, --help 						 Show this help

Available tests:
- Basic tests: 
  - Test1: Simple test 1
  - Test2: Failing test

Available parameters:
- departments 
  Default: [Warsaw,Berlin]
- currency 
  Default: PLN
- date 
  Default: 2015-01-01
- hours 
  Default: [12]

As you see we can do many things. Isn't it great?

Random

Next thing we should look at is random_example.php from the examples directory. Let's take a look at parameters:

use PHPIntegration\Utils\RandomHelper;

$params = function() {
    return [
        TestParameter::manyFromParameter(
            "departments",
            RandomHelper::randomArray(["Warsaw", "Berlin", "Cracow"], false),
            ["Warsaw", "Berlin", "Cracow"]
        ),
        TestParameter::stringParameter("currency", RandomHelper::randomString(3)),
        TestParameter::arrayOfParameter(
            "hours",
            RandomHelper::randomMany(function() { return rand(1,24); },1),
            '\PHPIntegration\TestParameter::intParameter'
        )
    ];
};

You can see the same old TestParameter class but there is also RandomHelper. It contains many useful functions for generating random data. For example the randomArray function just generates array containing random elements from the provided list. The last argument decides whether it can contain duplicate values or not. Of course you can generate random string with randomString function and random array with randomMany.

But real the beauty is the CLI:

Random tests [pre] [ OK ] 
>> Test1 4/100 [ FAILED ] 0.00 ms
Test description: 
Warsaw test
Parameters: 
- departments:[]
- currency:b X
- hours:[14,12]
Message: 
this test succeeds only if Warsaw is passed
>> Test2 1/100 [ FAILED ] 20.10 ms > 10 ms limit
Test description: 
Failing test
Parameters: 
- departments:[Cracow,Warsaw,Berlin]
- currency:Mwy
- hours:[6,18,13,2]
Message: 
this is a test that always fails
Random tests [post] [ OK ] 

The n parameter to the script tells it to repeat execution of every test n times. Whenever one fails it stops repeating it and goes to next test. You can spot the "> 10 ms limit" in the second test case. This happened because it this test time limit was set. You can do it by providing third parameter to the Test class:

    new Test(
        "Test2",
        "Failing test",
        function($p) {
            usleep(20000);
            return "this is a test that always fails";
        },
        10
    )

10 means the test should finish within 10 ms. You can measure other things within tests. The test function's argument is an array of parameters and a special measure function called measure. See examples/measurement_example.php

Objects as parameters

So far we defined only string, int and array parameters. But we can do better. We can define objects! Unfortunately to do this we need to implement an interface. Take a look at object_example.php from the examples directory.

use PHPIntegration\Testable;
use PHPIntegration\Utils\RandomHelper;
use PHPIntegration\Randomizable;

class TestObject implements Randomizable, Testable
{
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public static function build(string $value) : Testable
    {
        return new TestObject($value);
    }

    public static function validate(string $value, bool $valid = true)
    {
        $fstLetter = substr($value, 0, 1);
        if ($valid === true) {
            if (strtolower($fstLetter) == $fstLetter) {
                return "Value must start from upper case.\n";
            } else {
                return true;
            }
        } else {
            if (strtolower($fstLetter) == $fstLetter) {
                return true;
            } else {
                return "Value must not start from upper case.\n";
            }
        }
    }

    public function asStringParameter() : string
    {
        return $this->name;
    }

    public static function randomValid()
    {
        return new TestObject(strtoupper(RandomHelper::randomString()));
    }

    public static function randomInvalid()
    {
        return new TestObject(strtolower(RandomHelper::randomString()));
    }
}

To use object as parameter we need to implement only Testable interface. To make it random we need also implement Randomizable interface. There are 3 methods in the Testable interface: build, validate and asStringParameter. Build is easy, it just takes whatever user wrote in the -p option and must create an object from it. Validate method is executed just before it to make sure that this string makes sense. When not CLI will display error. And asStringParameter is used when test fails to show the parameter value that user can pass again (useful when object is not provided but randomized).

php object_example.php -p "first name:john"    
Bad param `first name` value `john`
Value must start from upper case.

Randomizable is much simpler. There are only 2 methods. One for generating object with valid data. For example when it would be a database connection string it would point to the existing database. And the other one for invalid data (for instance connection string to not existing database).

You can randomize object with randomObject method from the RandomHelper class. To use object as a parameter you need to use objectParameter method from the TestParameter class.

Other things

You should definitely check the examples folder.

About

PHP library for writing automated tests

Resources

License

Stars

Watchers

Forks

Packages

No packages published