Add some test components that were missing

This commit is contained in:
Greyscale 2020-07-01 14:23:40 +02:00
parent fd4239ae57
commit 1628c2f178
8 changed files with 499 additions and 0 deletions

108
tests/BaseTestCase.php Normal file
View 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
View 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;
}
}

View 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
View 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);
}
}
}

View 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);
}
}

View 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();
}
}

View 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;
}
}

View 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);
}
}