Rebuilding with CodeIgniter 4

This commit is contained in:
Timothy Rogers 2020-07-27 22:22:06 -04:00
parent e6dcc92267
commit 3bce4ad0ac
92 changed files with 7304 additions and 380 deletions

108
tests/README.md Normal file
View file

@ -0,0 +1,108 @@
# Running Application Tests
This is the quick-start to CodeIgniter testing. Its intent is to describe what
it takes to set up your application and get it ready to run unit tests.
It is not intended to be a full description of the test features that you can
use to test your application. Those details can be found in the documentation.
## Resources
* [CodeIgniter 4 User Guide on Testing](https://codeigniter4.github.io/userguide/testing/index.html)
* [PHPUnit docs](https://phpunit.readthedocs.io/en/8.3/index.html)
## Requirements
It is recommended to use the latest version of PHPUnit. At the time of this
writing we are running version 8.5.2. Support for this has been built into the
**composer.json** file that ships with CodeIgniter and can easily be installed
via [Composer](https://getcomposer.org/) if you don't already have it installed globally.
> composer install
If running under OS X or Linux, you can create a symbolic link to make running tests a touch nicer.
> ln -s ./vendor/bin/phpunit ./phpunit
You also need to install [XDebug](https://xdebug.org/index.php) in order
for code coverage to be calculated successfully.
## Setting Up
A number of the tests use a running database.
In order to set up the database edit the details for the `tests` group in
**app/Config/Database.php** or **phpunit.xml**. Make sure that you provide a database engine
that is currently running on your machine. More details on a test database setup are in the
*Docs>>Testing>>Testing Your Database* section of the documentation.
If you want to run the tests without using live database you can
exclude @DatabaseLive group. Or make a copy of **phpunit.dist.xml** -
call it **phpunit.xml** - and comment out the <testsuite> named "database". This will make
the tests run quite a bit faster.
## Running the tests
The entire test suite can be run by simply typing one command-line command from the main directory.
> ./phpunit
You can limit tests to those within a single test directory by specifying the
directory name after phpunit.
> ./phpunit app/Models
## Generating Code Coverage
To generate coverage information, including HTML reports you can view in your browser,
you can use the following command:
> ./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m
This runs all of the tests again collecting information about how many lines,
functions, and files are tested. It also reports the percentage of the code that is covered by tests.
It is collected in two formats: a simple text file that provides an overview as well
as a comprehensive collection of HTML files that show the status of every line of code in the project.
The text file can be found at **tests/coverage.txt**.
The HTML files can be viewed by opening **tests/coverage/index.html** in your favorite browser.
## PHPUnit XML Configuration
The repository has a ``phpunit.xml.dist`` file in the project root that's used for
PHPUnit configuration. This is used to provide a default configuration if you
do not have your own configuration file in the project root.
The normal practice would be to copy ``phpunit.xml.dist`` to ``phpunit.xml``
(which is git ignored), and to tailor it as you see fit.
For instance, you might wish to exclude database tests, or automatically generate
HTML code coverage reports.
## Test Cases
Every test needs a *test case*, or class that your tests extend. CodeIgniter 4
provides a few that you may use directly:
* `CodeIgniter\Test\CIUnitTestCase` - for basic tests with no other service needs
* `CodeIgniter\Test\CIDatabaseTestCase` - for tests that need database access
Most of the time you will want to write your own test cases to hold functions and services
common to your test suites.
## Creating Tests
All tests go in the **tests/** directory. Each test file is a class that extends a
**Test Case** (see above) and contains methods for the individual tests. These method
names must start with the word "test" and should have descriptive names for precisely what
they are testing:
`testUserCanModifyFile()` `testOutputColorMatchesInput()` `testIsLoggedInFailsWithInvalidUser()`
Writing tests is an art, and there are many resources available to help learn how.
Review the links above and always pay attention to your code coverage.
### Database Tests
Tests can include migrating, seeding, and testing against a mock or live<sup>1</sup> database.
Be sure to modify the test case (or create your own) to point to your seed and migrations
and include any additional steps to be run before tests in the `setUp()` method.
<sup>1</sup> Note: If you are using database tests that require a live database connection
you will need to rename **phpunit.xml.dist** to **phpunit.xml**, uncomment the database
configuration lines and add your connection details. Prevent **phpunit.xml** from being
tracked in your repo by adding it to **.gitignore**.

View file

@ -0,0 +1,61 @@
<?php namespace Tests\Support\Database\Migrations;
use CodeIgniter\Database\Migration;
class ExampleMigration extends Migration
{
protected $DBGroup = 'tests';
public function up()
{
$fields = [
'name' => [
'type' => 'varchar',
'constraint' => 31,
],
'uid' => [
'type' => 'varchar',
'constraint' => 31,
],
'class' => [
'type' => 'varchar',
'constraint' => 63,
],
'icon' => [
'type' => 'varchar',
'constraint' => 31,
],
'summary' => [
'type' => 'varchar',
'constraint' => 255,
],
'created_at' => [
'type' => 'datetime',
'null' => true,
],
'updated_at' => [
'type' => 'datetime',
'null' => true,
],
'deleted_at' => [
'type' => 'datetime',
'null' => true,
],
];
$this->forge->addField('id');
$this->forge->addField($fields);
$this->forge->addKey('name');
$this->forge->addKey('uid');
$this->forge->addKey(['deleted_at', 'id']);
$this->forge->addKey('created_at');
$this->forge->createTable('factories');
}
public function down()
{
$this->forge->dropTable('factories');
}
}

View file

@ -0,0 +1,40 @@
<?php namespace Tests\Support\Database\Seeds;
use CodeIgniter\Database\Seeder;
class ExampleSeeder extends Seeder
{
public function run()
{
$factories = [
[
'name' => 'Test Factory',
'uid' => 'test001',
'class' => 'Factories\Tests\NewFactory',
'icon' => 'fas fa-puzzle-piece',
'summary' => 'Longer sample text for testing',
],
[
'name' => 'Widget Factory',
'uid' => 'widget',
'class' => 'Factories\Tests\WidgetPlant',
'icon' => 'fas fa-puzzle-piece',
'summary' => 'Create widgets in your factory',
],
[
'name' => 'Evil Factory',
'uid' => 'evil-maker',
'class' => 'Factories\Evil\MyFactory',
'icon' => 'fas fa-book-dead',
'summary' => 'Abandon all hope, ye who enter here',
],
];
$builder = $this->db->table('factories');
foreach ($factories as $factory)
{
$builder->insert($factory);
}
}
}

View file

@ -0,0 +1,51 @@
<?php namespace Tests\Support;
class DatabaseTestCase extends \CodeIgniter\Test\CIDatabaseTestCase
{
/**
* Should the database be refreshed before each test?
*
* @var boolean
*/
protected $refresh = true;
/**
* The seed file(s) used for all tests within this test case.
* Should be fully-namespaced or relative to $basePath
*
* @var string|array
*/
protected $seed = 'Tests\Support\Database\Seeds\ExampleSeeder';
/**
* The path to the seeds directory.
* Allows overriding the default application directories.
*
* @var string
*/
protected $basePath = SUPPORTPATH . 'Database/';
/**
* The namespace(s) to help us find the migration classes.
* Empty is equivalent to running `spark migrate -all`.
* Note that running "all" runs migrations in date order,
* but specifying namespaces runs them in namespace order (then date)
*
* @var string|array|null
*/
protected $namespace = 'Tests\Support';
public function setUp(): void
{
parent::setUp();
// Extra code to run before each test
}
public function tearDown(): void
{
parent::tearDown();
// Extra code to run after each test
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* CodeIgniter
*
* An open source application development framework for PHP
*
* This content is released under the MIT License (MIT)
*
* Copyright (c) 2014-2019 British Columbia Institute of Technology
* Copyright (c) 2019-2020 CodeIgniter Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @package CodeIgniter
* @author CodeIgniter Dev Team
* @copyright 2019-2020 CodeIgniter Foundation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://codeigniter.com
* @since Version 4.0.0
* @filesource
*/
namespace Tests\Support\Libraries;
/**
* Class ConfigReader
*
* An extension of BaseConfig that prevents the constructor from
* loading external values. Used to read actual local values from
* a config file.
*/
class ConfigReader extends \Config\App
{
public function __construct()
{
}
}

View file

@ -0,0 +1,26 @@
<?php namespace Tests\Support\Models;
use CodeIgniter\Model;
class ExampleModel extends Model
{
protected $table = 'factories';
protected $primaryKey = 'id';
protected $returnType = 'object';
protected $useSoftDeletes = false;
protected $allowedFields = [
'name',
'uid',
'class',
'icon',
'summary',
];
protected $useTimestamps = true;
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
}

View file

@ -0,0 +1,32 @@
<?php namespace Tests\Support;
use CodeIgniter\Session\Handlers\ArrayHandler;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockSession;
class SessionTestCase extends CIUnitTestCase
{
/**
* @var SessionHandler
*/
protected $session;
public function setUp(): void
{
parent::setUp();
$this->mockSession();
}
/**
* Pre-loads the mock session driver into $this->session.
*
* @var string
*/
protected function mockSession()
{
$config = config('App');
$this->session = new MockSession(new ArrayHandler($config, '0.0.0.0'), $config);
\Config\Services::injectMock('session', $this->session);
}
}

View file

@ -0,0 +1,42 @@
<?php
use Tests\Support\Models\ExampleModel;
class ExampleDatabaseTest extends \Tests\Support\DatabaseTestCase
{
public function setUp(): void
{
parent::setUp();
// Extra code to run before each test
}
public function testModelFindAll()
{
$model = new ExampleModel();
// Get every row created by ExampleSeeder
$objects = $model->findAll();
// Make sure the count is as expected
$this->assertCount(3, $objects);
}
public function testSoftDeleteLeavesRow()
{
$model = new ExampleModel();
$this->setPrivateProperty($model, 'useSoftDeletes', true);
$this->setPrivateProperty($model, 'tempUseSoftDeletes', true);
$object = $model->first();
$model->delete($object->id);
// The model should no longer find it
$this->assertNull($model->find($object->id));
// ... but it should still be in the database
$result = $model->builder()->where('id', $object->id)->get()->getResult();
$this->assertCount(1, $result);
}
}

View file

@ -0,0 +1,18 @@
<?php
class ExampleSessionTest extends \Tests\Support\SessionTestCase
{
public function setUp(): void
{
parent::setUp();
}
public function testSessionSimple()
{
$this->session->set('logged_in', 123);
$value = $this->session->get('logged_in');
$this->assertEquals(123, $value);
}
}

33
tests/unit/HealthTest.php Normal file
View file

@ -0,0 +1,33 @@
<?php
class HealthTest extends \CodeIgniter\Test\CIUnitTestCase
{
public function setUp(): void
{
parent::setUp();
}
public function testIsDefinedAppPath()
{
$test = defined('APPPATH');
$this->assertTrue($test);
}
public function testBaseUrlHasBeenSet()
{
$env = $config = false;
// First check in .env
if (is_file(HOMEPATH . '.env'))
{
$env = (bool) preg_grep("/^app\.baseURL = './", file(HOMEPATH . '.env'));
}
// Then check the actual config file
$reader = new \Tests\Support\Libraries\ConfigReader();
$config = ! empty($reader->baseUrl);
$this->assertTrue($env || $config);
}
}