ksamborski / php-integration
Installs: 2 671
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 1
Open Issues: 0
Requires
- php: >=7.0.0
Requires (Dev)
- phpunit/phpunit: 5.5.*
README
PHP library for writing automated tests. It can help you get started especially when you do TDD and value randomized testing.
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.