Add some test components that were missing
This commit is contained in:
parent
fd4239ae57
commit
1628c2f178
8 changed files with 499 additions and 0 deletions
108
tests/BaseTestCase.php
Normal file
108
tests/BaseTestCase.php
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests;
|
||||
|
||||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator;
|
||||
use Faker\Provider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Benzine\App;
|
||||
|
||||
abstract class BaseTestCase extends TestCase
|
||||
{
|
||||
// Set this to true if you want to see whats going on inside some unit tests..
|
||||
public const DEBUG_MODE = false;
|
||||
/**
|
||||
* @see https://github.com/fzaninotto/Faker
|
||||
*
|
||||
* @var Generator
|
||||
*/
|
||||
private static $faker;
|
||||
|
||||
private $app;
|
||||
|
||||
private $container;
|
||||
|
||||
private $singleTestTime;
|
||||
|
||||
private $waypoint_count;
|
||||
private $waypoint_last_time;
|
||||
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
// @var \Slim\App $app
|
||||
if (!defined('APP_CORE_NAME')) {
|
||||
throw new \Exception('You must define APP_CORE_NAME in bootstrap.php. This must be the same as the core app container in /src');
|
||||
}
|
||||
|
||||
// Force Kint into CLI mode.
|
||||
\Kint::$mode_default = \Kint::MODE_CLI;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public static function getFaker()
|
||||
{
|
||||
if (!self::$faker) {
|
||||
self::$faker = FakerFactory::create();
|
||||
self::$faker->addProvider(new Provider\Base(self::$faker));
|
||||
self::$faker->addProvider(new Provider\DateTime(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Lorem(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Internet(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Payment(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Person(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Address(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\PhoneNumber(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Company(self::$faker));
|
||||
}
|
||||
|
||||
return self::$faker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call protected/private method of a class.
|
||||
*
|
||||
* @param object &$object Instantiated object that we will run method on
|
||||
* @param string $methodName Method name to call
|
||||
* @param array $parameters array of parameters to pass into method
|
||||
*
|
||||
* @return mixed method return
|
||||
*/
|
||||
public function invokeMethod(&$object, $methodName, array $parameters = [])
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$method = $reflection->getMethod($methodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invokeArgs($object, $parameters);
|
||||
}
|
||||
|
||||
public function setProtectedProperty(&$object, $property, $value)
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$prop = $reflection->getProperty($property);
|
||||
$prop->setAccessible(true);
|
||||
|
||||
return $prop->setValue($object, $value);
|
||||
}
|
||||
|
||||
public function getProtectedProperty(&$object, $property)
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$prop = $reflection->getProperty($property);
|
||||
$prop->setAccessible(true);
|
||||
|
||||
return $prop->getValue($object);
|
||||
}
|
||||
|
||||
public function assertArraysEquitable($expected, $actual): void
|
||||
{
|
||||
sort($expected);
|
||||
sort($actual);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
}
|
||||
128
tests/RoutesTestCase.php
Normal file
128
tests/RoutesTestCase.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Slim\Http\Environment;
|
||||
use Slim\Http\Headers;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\RequestBody;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Http\Uri;
|
||||
use ⌬\Router\Router;
|
||||
|
||||
abstract class RoutesTestCase extends BaseTestCase
|
||||
{
|
||||
private $defaultEnvironment = [];
|
||||
private $defaultHeaders = [];
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->defaultEnvironment = [
|
||||
'SCRIPT_NAME' => '/index.php',
|
||||
'RAND' => rand(0, 100000000),
|
||||
];
|
||||
$this->defaultHeaders = [];
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $post
|
||||
* @param bool $isJsonRequest
|
||||
* @param array $extraHeaders
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function request(
|
||||
string $method,
|
||||
string $path,
|
||||
$post = null,
|
||||
$isJsonRequest = true,
|
||||
$extraHeaders = []
|
||||
) {
|
||||
/*
|
||||
* @var \Slim\App $app
|
||||
* @var \Gone\AppCore\App $applicationInstance
|
||||
*/
|
||||
$this->waypoint('Before App Fetch');
|
||||
$applicationInstance = $this->getApp();
|
||||
$this->waypoint('After App Fetch');
|
||||
$calledClass = get_called_class();
|
||||
|
||||
$app = $applicationInstance->getApp();
|
||||
|
||||
if (defined("{$calledClass}")) {
|
||||
$modelName = $calledClass::MODEL_NAME;
|
||||
if (file_exists(APP_ROOT."/src/Routes/{$modelName}Route.php")) {
|
||||
require APP_ROOT."/src/Routes/{$modelName}Route.php";
|
||||
}
|
||||
} else {
|
||||
if (file_exists(APP_ROOT.'/src/Routes.php')) {
|
||||
require APP_ROOT.'/src/Routes.php';
|
||||
}
|
||||
}
|
||||
if (file_exists(APP_ROOT.'/src/RoutesExtra.php')) {
|
||||
require APP_ROOT.'/src/RoutesExtra.php';
|
||||
}
|
||||
Router::Instance()->populateRoutes($app);
|
||||
$this->waypoint('Loaded Routes');
|
||||
$headers = array_merge($this->defaultHeaders, $extraHeaders);
|
||||
|
||||
$envArray = array_merge($this->defaultEnvironment, $headers);
|
||||
$envArray = array_merge($envArray, [
|
||||
'REQUEST_URI' => $path,
|
||||
'REQUEST_METHOD' => $method,
|
||||
]);
|
||||
|
||||
$env = Environment::mock($envArray);
|
||||
$uri = Uri::createFromEnvironment($env);
|
||||
$headers = Headers::createFromEnvironment($env);
|
||||
|
||||
$cookies = [];
|
||||
$serverParams = $env->all();
|
||||
$body = new RequestBody();
|
||||
if (!is_array($post) && null != $post) {
|
||||
$body->write($post);
|
||||
$body->rewind();
|
||||
} elseif (is_array($post) && count($post) > 0) {
|
||||
$body->write(json_encode($post));
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
$request = new Request($method, $uri, $headers, $cookies, $serverParams, $body);
|
||||
if ($isJsonRequest) {
|
||||
foreach ($extraHeaders as $k => $v) {
|
||||
$request = $request->withHeader($k, $v);
|
||||
}
|
||||
$request = $request->withHeader('Content-type', 'application/json');
|
||||
$request = $request->withHeader('Accept', 'application/json');
|
||||
}
|
||||
$this->waypoint('Before Response');
|
||||
$response = new Response();
|
||||
|
||||
// Invoke app
|
||||
$response = $applicationInstance
|
||||
->makeClean()
|
||||
->getApp()
|
||||
->process($request, $response)
|
||||
;
|
||||
$response->getBody()->rewind();
|
||||
$this->waypoint('After Response');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function setEnvironmentVariable($key, $value): self
|
||||
{
|
||||
$this->defaultEnvironment[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setRequestHeader($header, $value): self
|
||||
{
|
||||
$this->defaultHeaders[$header] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
48
tests/SeleniumTestCase.php
Normal file
48
tests/SeleniumTestCase.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests;
|
||||
|
||||
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
||||
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
|
||||
|
||||
abstract class SeleniumTestCase
|
||||
{
|
||||
/** @var RemoteWebDriver */
|
||||
protected static $webDriver;
|
||||
|
||||
protected static $screenshotsDir;
|
||||
|
||||
protected static $screenshotIndex = 0;
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
$capabilities = [WebDriverCapabilityType::BROWSER_NAME => 'chrome'];
|
||||
self::$webDriver = RemoteWebDriver::create(
|
||||
'http://'.$_SERVER['SELENIUM_HOST'].':'.$_SERVER['SELENIUM_PORT'].'/wd/hub',
|
||||
$capabilities,
|
||||
60000,
|
||||
60000
|
||||
);
|
||||
|
||||
self::$webDriver->manage()->timeouts()->implicitlyWait(3);
|
||||
|
||||
self::$screenshotsDir = APP_ROOT.'/build/Screenshots/'.date('Y-m-d H-i-s').'/';
|
||||
if (!file_exists(self::$screenshotsDir)) {
|
||||
mkdir(self::$screenshotsDir, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
self::$webDriver->close();
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
protected function takeScreenshot($name)
|
||||
{
|
||||
self::$webDriver->takeScreenshot(self::$screenshotsDir.self::$screenshotIndex."_{$name}.jpg");
|
||||
++self::$screenshotIndex;
|
||||
}
|
||||
}
|
||||
45
tests/TestCase.php
Normal file
45
tests/TestCase.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests;
|
||||
|
||||
abstract class TestCase extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
use Traits\OverrideProtectionTrait;
|
||||
use Traits\ArrayEquitabilityTrait;
|
||||
|
||||
private $singleTestTime;
|
||||
|
||||
private $waypoint_count;
|
||||
private $waypoint_last_time;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->singleTestTime = microtime(true);
|
||||
$this->waypoint_count = 0;
|
||||
$this->waypoint_last_time = $this->singleTestTime;
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
if (defined('DEBUG') && DEBUG) {
|
||||
$time = microtime(true) - $this->singleTestTime;
|
||||
echo ''.get_called_class().':'.$this->getName().': Took '.number_format($time, 3)." seconds\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function waypoint($message = ''): void
|
||||
{
|
||||
if (defined('DEBUG') && DEBUG) {
|
||||
$time_since_last_waypoint = number_format((microtime(true) - $this->waypoint_last_time) * 1000, 2, '.', '');
|
||||
$time_since_begin = number_format((microtime(true) - $this->singleTestTime) * 1000, 2, '.', '');
|
||||
++$this->waypoint_count;
|
||||
if (1 == $this->waypoint_count) {
|
||||
echo "\n";
|
||||
}
|
||||
echo " > Waypoint {$this->waypoint_count} - {$time_since_last_waypoint}ms / {$time_since_begin}ms {$message}\n";
|
||||
$this->waypoint_last_time = microtime(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
tests/Traits/ArrayEquitabilityTrait.php
Normal file
13
tests/Traits/ArrayEquitabilityTrait.php
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests\Traits;
|
||||
|
||||
trait ArrayEquitabilityTrait
|
||||
{
|
||||
public function assertArraysEquitable($expected, $actual): void
|
||||
{
|
||||
sort($expected);
|
||||
sort($actual);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
80
tests/Traits/DatabaseAccessTrait.php
Normal file
80
tests/Traits/DatabaseAccessTrait.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests\Traits;
|
||||
|
||||
use Slim\Container;
|
||||
use ⌬\Database\Db;
|
||||
use ⌬\Services\EnvironmentService;
|
||||
use ⌬\⌬ as App;
|
||||
|
||||
trait DatabaseAccessTrait
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
// If MySQL has been configured, enable a transaction that we can rollback later.
|
||||
if (self::isTestDatabaseEnabled()) {
|
||||
/** @var Db $db */
|
||||
$db = App::Instance()->getContainer()->get(Db::class);
|
||||
|
||||
// If MySQL has been configured, begin transaction.
|
||||
if ($db->isMySQLConfigured()) {
|
||||
foreach ($db->getDatabases() as $name => $database) {
|
||||
$database->driver->getConnection()->beginTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue setup.
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
// If MySQL has been configured, roll back transaction.
|
||||
if (self::isTestDatabaseEnabled()) {
|
||||
/** @var Db $db */
|
||||
$db = App::Instance()->getContainer()->get(Db::class);
|
||||
if ($db->isMySQLConfigured()) {
|
||||
foreach ($db->getDatabases() as $name => $database) {
|
||||
$database->driver->getConnection()->rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue Teardown.
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
public function assertArraysEquitable($expected, $actual): void
|
||||
{
|
||||
sort($expected);
|
||||
sort($actual);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
private static function isTestDatabaseEnabled(): bool
|
||||
{
|
||||
/** @var EnvironmentService $environment */
|
||||
$environment = self::getAppContainer()->get(EnvironmentService::class);
|
||||
|
||||
return $environment->isSet('MYSQL_HOST') || $environment->isSet('POSTGRES_HOST');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return App
|
||||
*/
|
||||
private static function getAppObject()
|
||||
{
|
||||
$coreAppName = APP_CORE_NAME;
|
||||
|
||||
return $coreAppName::Instance(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Container
|
||||
*/
|
||||
private static function getAppContainer()
|
||||
{
|
||||
return self::getAppObject()->getContainer();
|
||||
}
|
||||
}
|
||||
35
tests/Traits/FakeDataTrait.php
Normal file
35
tests/Traits/FakeDataTrait.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests\Traits;
|
||||
|
||||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator;
|
||||
use Faker\Provider;
|
||||
|
||||
trait FakeDataTrait
|
||||
{
|
||||
/** @var Generator */
|
||||
protected static $faker;
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
self::$faker = FakerFactory::create();
|
||||
self::$faker->addProvider(new Provider\Base(self::$faker));
|
||||
self::$faker->addProvider(new Provider\DateTime(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Lorem(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Internet(self::$faker));
|
||||
self::$faker->addProvider(new Provider\Payment(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Person(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Address(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\PhoneNumber(self::$faker));
|
||||
self::$faker->addProvider(new Provider\en_US\Company(self::$faker));
|
||||
|
||||
// Continue setup.
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
public function faker(): Generator
|
||||
{
|
||||
return self::$faker;
|
||||
}
|
||||
}
|
||||
42
tests/Traits/OverrideProtectionTrait.php
Normal file
42
tests/Traits/OverrideProtectionTrait.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Benzine\Tests\Traits;
|
||||
|
||||
trait OverrideProtectionTrait
|
||||
{
|
||||
/**
|
||||
* Call protected/private method of a class.
|
||||
*
|
||||
* @param object &$object Instantiated object that we will run method on
|
||||
* @param string $methodName Method name to call
|
||||
* @param array $parameters array of parameters to pass into method
|
||||
*
|
||||
* @return mixed method return
|
||||
*/
|
||||
public function invokeMethod(&$object, $methodName, array $parameters = [])
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$method = $reflection->getMethod($methodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invokeArgs($object, $parameters);
|
||||
}
|
||||
|
||||
public function setProtectedProperty(&$object, $property, $value)
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$prop = $reflection->getProperty($property);
|
||||
$prop->setAccessible(true);
|
||||
|
||||
return $prop->setValue($object, $value);
|
||||
}
|
||||
|
||||
public function getProtectedProperty(&$object, $property)
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$prop = $reflection->getProperty($property);
|
||||
$prop->setAccessible(true);
|
||||
|
||||
return $prop->getValue($object);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue