CodeIgniter has been created to make testing both the framework and the app as simple as possible. Support for PHPUnit is created in, and the framework provides a number of convenient helper methods to make testing every aspect of your application as painless as possible.
How to install PHPUnit?
CodeIgniter uses PHPUnit as the backbone for all of the testing. There are two ways to enable PHPUnit to use within your system.
Install Composer
The recommended method is to enable it in your project using composer. While it’s possible to install it in the whole world we do not recommend it, since it can cause many issues with other projects on your system as time goes on. Make sure that you installed it. From the project root type the following from the command line:
> composer require --dev phpunit/phpunit
Once that is done, you can execute all of the tests for this project by typing:
> ./vendor/bin/phpunit
Install Phar
The other option is to install the .phar file from the PHP site. This is a foundation file that should be placed within your project.
How to test your application?
The framework has a phpunit.xml.dist
file . This enables unit testing of the framework . If you enable your own phpunit.xml
, it will override this.
Your phpunit.xml
should get out of the system
folder, as well as any vendor
or ThirdParty
directories, if you are working unit testing your application.
In order to take advantage of the more options provided, your tests must extend CIUnitTestCase
. All tests are expected to be situated in the tests/app directory by default.
To test the library, you would built a new file at tests/app/Libraries/FooTest.php:
<?PHP
namespace App\Libraries;
use CodeIgniter\Test\CIUnitTestCase;
class FooTest extends CIUnitTestCase
{
public function testFooNotBar()
{
// ...
}
}
To test one of the program, you might enter this code in tests/app/Models/OneOfMyModelsTest.php:
<?php
namespace App\Models;
use CodeIgniter\Test\CIUnitTestCase;
class OneOfMyModelsTest extends CIUnitTestCase
{
public function testFooNotBar()
{
// ...
}
}
You can make any directory structure that fits your testing style. When namespacing the classes, remember that the app directory is the main root of the App
namespace, so any classes you use must have the correct namespace relative to App
.
What is staging?
Most tests need some preparation in order to work correctly. PHPUnit’s TestCase
have four methods to help with staging and clean up:
public static function setUpBeforeClass(): void
public static function tearDownAfterClass(): void
public function setUp(): void
public function tearDown(): void
The static methods execute before and after the test case, whereas the local methods work between each test. If you run any of these special functions make sure you run their parent as well so extended cases do not interfere with staging:
public function setUp(): void
{
parent::setUp();
helper('text');
}
After to these methods, CIUnitTestCase
also comes with a property for parameter-free methods you want to execute during set up and tear down:
protected $setUpMethods = [
'mockEmail',
'mockSession',
];
protected $tearDownMethods = [];
You can see by automatically these manage the mocking of intrusive services, but your class may overlap that or provide their own:
class OneOfMyModelsTest extends CIUnitTestCase
{
protected $tearDownMethods = [
'purgeRows',
];
protected function purgeRows()
{
$this->model->purgeDeleted()
}
What are Traits?
The basic way to encourage your tests is by using traits to consolidate staging across various test cases. CIUnitTestCase
will identify any class traits and look for staging methods to execute names for the trait itself.
trait AuthTrait
{
protected setUpAuthTrait()
{
$user = $this->createFakeUser();
$this->logInUser($user);
}
...
class AuthenticationFeatureTest
{
use AuthTrait;
...
What about Additional Assertions?
CIUnitTestCase
provides unit testing that you might find very useful.
assertLogged:
Ensure that something you expected to be signed actually was:
$config = new LoggerConfig();
$logger = new Logger($config);
... do something that you expect a log entry from
$logger->log('error', "That's no moon");
$this->assertLogged('error', "That's no moon");
assertEventTriggered: Make sure that an event you expected to be triggered actually was:
Events::on('foo', function ($arg) use(&$result) {
$result = $arg;
});
Events::trigger('foo', 'bar');
$this->assertEventTriggered('foo');
assertHeaderEmitted:
Make sure that a header or cookie was actually emitted:
$response->setCookie('foo', 'bar');
ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body
$this->assertHeaderEmitted("Set-Cookie: foo=bar");
assertHeaderNotEmitted:
Make sure that a header or cookie was not emitted:
$response->setCookie('foo', 'bar');
ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body
$this->assertHeaderNotEmitted("Set-Cookie: banana");
assertCloseEnough:
For extended execution time testing, tests that the absolute difference between expected and original time is within the prescribed tolerance.:
$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnough(11 * 60, $timer->getElapsedTime('longjohn'));
assertCloseEnoughString:
For extended running time testing, tests that the absolute difference between expected and original time, formatted as strings, is within the prescribed tolerance.:
$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnoughString(11 * 60, $timer->getElapsedTime('longjohn'));
getPrivateMethodInvoker:
Install you to call private methods from outside the class. This returns a program that can be called. The first parameter is an instance of the class . The second parameter is the name of the method you want to make a call.
// Create an instance of the class to test
$obj = new Foo();
// Get the invoker for the 'privateMethod' method.
$method = $this->getPrivateMethodInvoker($obj, 'privateMethod');
// Test the results
$this->assertEquals('bar', $method('param1', 'param2'));
getPrivateProperty:
Retrieves the value of a private class property from an instance of a class.
// Create an instance of the class to test
$obj = new Foo();
// Test the value
$this->assertEquals('bar', $this->getPrivateProperty($obj, 'baz'));
setPrivateProperty:
// Create an instance of the class to test
$obj = new Foo();
// Set the value
$this->setPrivateProperty($obj, 'baz', 'oops!');
// Do normal testing...
What are mocking services?
You will often find that you have to mock one of the services defined in app/Config/Services.php to limit your tests to only the code in question while simulating multiple responses from the services. This is especially true when testing controllers and other testing.
injectMock()
This method allows you to define the exact instance that will be returned by the Services class.
public function testSomething()
{
$curlrequest = $this->getMockBuilder('CodeIgniter\HTTP\CURLRequest')
->setMethods(['request'])
->getMock();
Services::injectMock('curlrequest', $curlrequest);
// Do normal testing here....
}
reset()
Removes all mocked classes from the class, bringing it back to its actual state.
resetSingle:
Delete any mock and shared instances for a single service, by its name. you may identify yourself needing to supply a pre-configured class instance during testing that will be used with Factories
. Use the similar injectMock() and
reset()` methods like Services, but they take an additional preceding parameter for the component name:
protected function setUp()
{
parent::setUp();
$model = new MockUserModel();
Factories::injectMock('models', 'App\Models\UserModel', $model);
}
CITestStreamFilter provides an alternative to these helper methods. You may need to test things that are difficult to test. Sometimes, capturing a stream, like PHP’s own STDOUT, or STDERR, might be useful. An example demonstrating this inside one of your test cases:
public function setUp()
{
CITestStreamFilter::$buffer = '';
$this->stream_filter = stream_filter_append(STDOUT, 'CITestStreamFilter');
}
public function tearDown()
{
stream_filter_remove($this->stream_filter);
}
public function testSomeOutput()
{
CLI::write('first.');
$expected = "first.\n";
$this->assertSame($expected, CITestStreamFilter::$buffer);
}
If you have any doubts about the above topic or have to get services and consultations and get CodeIgniter testing services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner. E-mail id: [email protected].
Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/