Airo Global Software

Think Beyond Future !

How Do You Test Your Code?

- Posted in Application Testing by

enter image description here

Testing code is one of the most major parts to understand your app and the behavior of code. All the developer knows how hurtful bugs can be, especially in the creating stage as it takes hours of effort. Testing each stage can be as good as believing in yourself. You write and run your code, without knowing how it actually behaves if some exception occurs, this may cause a problem in production. So, executing test cases plays a major role in SDLC. You give a better life and believe your code with your eyes closed, once your examination coverage is at a good level.

Today, we are going to write an easy test method and execute test code using tools like Xdebug and PHPUnit. PHPUnit is a programmer-based testing framework. This is an excellent testing framework for writing Unit tests for PHP Web Apps. With the help of PHPUnit, we can direct test-driven improvement.

How to install PHPUnit?

wget https://phar.phpunit.de/phpunit-7.5.phar
chmod +x phpunit-7.5.phar
sudo mv phpunit-7.5.phar /usr/bin/phpunit

There are several other possible ways to install PHPUnit. Now, To get our Test Coverage full record we need to install Xdebug:

sudo apt install php-pear
pecl install xdebug
sudo apt-get install php-xdebug

Without making any further delay Let's dive into our easy test stages where we test whether an array is empty:

  • Create a file UnitTest where we will include all testable files
  • In this folder, create a subfolder test
  • Create a new file phpunit.xml
  • In this subfolder and add the below code

    /UnitTest/tests/ /UnitTest/tests/

What are the Basic Conventions to Write Unit Test Case?

Below are some basic conventions and steps for writing tests with PHPUnit:

  • Test File names should have a suffix Test.
  • Same as it, If the class name is MyFirstClass then the test class name will be MyFirstClassTest.
  • Add test as the prefix for method names.
  • All testing types are public.
  • MyFirstClassTest class should be inherited

These are the major instructions for the PHP unit testing framework. The basic configurations and settings are all set up. It is now time to write the prime test case.

How to Write The First Unit Test Case in PHP?

Build a file EmptyTest.php in UnitTest/tests. Add the below code to it.

<?PHP
use PHPUnit\Framework\TestCase;
class EmptyTest extends TestCase
{
 public function testFailure()
{
$this->assertEmpty(['something']);
 }
}
?>

Now, to execute our test code inside terminal type:

phpunit i.e. phpunit tests/EmptyTest.php.

By executing only phpunit, it automatically executes all test file inside UnitTest/tests folder because we have mentioned this directory in phpunit.xml above. We will get our test failure because the value in the array is not empty. In the terminal you are getting like this:

F                                                                   1 / 1 (100%)
Time: 513 ms, Memory: 10.00MB
There was 1 failure:
EmptyTest::testFailure
Failed asserting that an array is empty
/home/anu/myproj/testcase/tests/EmptyTest.php:8

FAILURES!

Tests: 1, Assertions: 1, Failures: 1

Let's pass our above test case and add one more step to test equals.

<?PHP
use PHPUnit\Framework\TestCase;
class EmptyTest extends
TestCase
{

public function

testFailure()
{
$this->assertEmpty([]);
 }
}
?>
<?PHP
use PHPUnit\Framework\TestCase;
class EqualsTest extends TestCase
{
public function testFailure()
{
$this->assertEquals(0,1); //failed asserting 0 and 1 are equal
}
}
?>

It gives an unexpected error when $expected is not the same as $actual. If $expected is the same to $actual then it is true. Don’t forget that the first argument is expected and the other is actual. The above test only walks away if the expected (0) is equal to (1). To pass the above test case just replace 0 with 1 or 1 with 0. Now, execute both test cases. Your both tests must pass:

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.3.1-1+ubuntu18.04.1+deb.sury.org+1 with Xdebug 2.7.0
Configuration: /home/samrat/myproj/UnitTest/phpunit.xml
..                                                                  2 / 2 (100%)
Time: 200 ms, Memory: 10.00MB
OK (2 tests, 2 assertions)

Let’s see another example in which a developer has built a code to parse the items received in JSON and validate it for the possible use case may be an exception or for other validation instruction.

Let's build an app/item_parser.php file inside the UnitTest directory

<?PHP
namespace App\Parser;
require_once 'exceptions.php';
use App\Exceptions\JsonParseException;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParser {
public static function parse($response){
$jDecode=json_decode($response);
if (JSON_ERROR_NONE !== json_last_error()){
throw new JsonParseException('Error parsing
JSON:'.$response);
}
$jd=$jDecode->_embedded->menu_items;
$parsed = [];
foreach($jd as $key => $item){
$price=$item->price_per_unit;
if (is_string($price)) {
throw new 
InvalidNumberFormatException('Invalid numeric value encountered:'.$price);
}
$parsed[$item->id] = [
"label" => $item->name,
"value" => number_format(($price/100), 2, '.', ' ')
];
}
return $parsed;
}
}
?>

create a class called ItemsParser with a static option called parse that takes one argument called a response, received from JSON data. We have checked for different types of exceptions and returned parsed data as an array.

Let's add some exceptions classes for the above use cases inside the same folder:

<?PHP
namespace App\Exceptions;
class JsonParseException extends
\Exception {}
class InvalidNumberFormatException 
extends \Exception {}
?>

Next in the UnitTest/tests folder, create a new file called ItemsParserTest.php and add the below test code:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
use App\Parser\ItemsParser;
class ItemsParserTest extends TestCase
{   
public function test_create_ItemsParser() {
 $ip = new ItemsParser();
$this->assertEquals('App\Parser\ItemsParser',
\get_class($ip));
}
}
?>

Run the above test code, Our test case must pass. By, this we can make sure that ItemParser class is successfully instantiated. As we don't invoke real endpoint. Let's create a fake json data called one.json inside UnitTest/json and test.

{
"_embedded": {
"menu_items": [
{
"id": "1",
"in_stock": null,
"name": "Pizza",
"open": false,
"pos_id": "101",
"price_per_unit": 899
}
]
}
}

Now Let's write another test code to ensure our data is actually parsed the way we aimed it:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
require_once __DIR__ . '/../app/exceptions.php';
use App\Exceptions\JsonParseException;
use App\Parser\ItemsParser;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParserTest extends TestCase
{
var $dir = __DIR__ . '/../json/';
 public function test_create_ItemsParser() {
$ip = new ItemsParser();
 $this->assertEquals('App\Parser\ItemsParser', \get_class($ip));
}
 public function     test_parseSingleItem_shouldReturnArrayOneLabelAndValue(){
$items=file_get_contents($this->dir.'one.json');
$expected = ['1' => ['label' => 'Pizza', 'value' => '8.99']];
$this->assertSame($expected, ItemsParser::parse($items));
 }
}
?>

As, we make fake data, and send that data to ItemParser::parse() option. If we now execute our test case we get the data the way we expected.

PHPUnit 7.5.8 by Sebastian Bergmann and contributors.
Runtime:       PHP 7.2.15-0ubuntu0.18.04.1 with Xdebug 2.6.0
Configuration: /home/samrat/myproj/UnitTest/phpunit.xml
..                                                                  2 / 2 (100%)
Time: 377 ms, Memory: 10.00 MB
OK (2 tests, 2 assertions)

Basically, what we did is we send json details and decoupled them on our data only with label and value as keys and returned only data that is needed as an array. Next, Let's test for the array keys we are getting:

<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../app/item_parser.php';
require_once __DIR__ . '/../app/exceptions.php';
use App\Exceptions\JsonParseException;
use App\Parser\ItemsParser;
use App\Exceptions\InvalidNumberFormatException;
class ItemsParserTest extends TestCase
{
 var $dir = __DIR__ . '/../json/';
 public function 
test_JsonParser_arrayKey_shouldReturnKey_label_and_value(){
$items=file_get_contents($this->dir.'one.json');
 $itemId='1';
$parsed=ItemsParser::parse($items);
$this->assertArrayHasKey('label',$parsed[$itemId]);
 $this->assertArrayHasKey('value',$parsed[$itemId]);
 }
}
?>

From the above test code, we can make sure that whether we are getting the key we expected that is label and value. What about the wishes we included in our methods, whether they are executed the way we wanted? Well, I am not sure so let's test it. Say, we have some JSON data with price in the string:

{
"_embedded": {
"menu_items": [
{
"id": "1",
"in_stock": 
"name": "Pizza",
"open": false,
"pos_id": "101",
"price_per_unit": "iamstring"
},
{
"id": "2",
"in_stock": 
"name": "Burger",
"open": false,
"pos_id": "101",
"price_per_unit":"gfsgd"
}
]
}
}

How does our test code block respond when we send price as string, well we must be sure our test code handles such kinds of exceptions.

public function
test_stringPriceValueProvided_shouldThrow_InvalidNumberFormatException(){
$items=file_get_contents("stringPrice.json")
 $this->expectException(InvalidNumberFormatException::class);
$parsed=ItemsParser::parse($items);
}

expectException() method is to ensure whether an expected exception is thrown by our code block. Run phpunit and check the above code it must by the way!

Rechange, the ItemParser.php file we have used the number_format function to convert cent price into the dollar. So, Let's enter a test to check whether our price has been perfectly converted.

public function testCent_Conversion_toDollar(){
$items=$this->file_get_contents('one.json');
 $itemId='1';
 $parsed= ItemsParser::parse($items);
$this->assertEquals(8.99,$parsed[$itemId]['value']);
 }

And These tests can go on and on, wouldn't it be nice to get a report or analyzer for our test case. To get a total test coverage report would be best. So, we have written some test code and let's generate our test report. It's easy just type this in terminal: phpunit --coverage-html . In my case phpunit --coverage-html report/. You will get whole report in html format. By the way, we can generate in xml, php, text and many other formats. Just type: phpunit --help for more.

The above test coverage explains, our first example test coverage is complete but ItemParserTest could be great and we only covered some part of our code so let's stick with partially completed. I leave it up to you to make it full. Actually, partially test code coverage can be acceptable coverage in the software world.

We also get the report of where we can do better. Here are some other code coverage statistics:

How about the Conclusion?

This article explains a basic setup that helps you in getting started with PHPUnit for PHP unit testing.

Hope you find this blog helpful. Unit Testing is a vast topic. Here I have given you a brief introduction so that you can start entering your own tests.

If you have any questions about the above topic or have to get services and consultations and get the unit test services. Feel free to contact us. AIRO GLOBAL SOFTWARE will be your strong digital partner.

E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/