Robert Basic's blog

Posts tagged 'phpunit'

PHP traits to create test doubles

by Robert Basic on April 04, 2017.

Keeping your application or library code well organized, easy to follow, and read is important. Your test code should not be exempt from those rules, you should follow good testing conventions.

One part of my tests that I feel like that are out of control are the test doubles. Dummies, fakes, mocks… Seems like they are everywhere and that I keep writing the same ones over and over again.

I do follow some good practices on how to reduce code duplication in my tests, but these mocks…

Ugh.

Test doubles are everywhere

Lets look at a couple of example test cases:

tests/App/UnitTest/Transaction/TransactionTest.php

<?php declare(strict_types=1);
namespace App\UnitTest\Transaction;

use PHPUnit\Framework\TestCase;
use App\Account\Account;
use App\Account\AccountType;
use App\Transaction\Transaction;

class TransactionTest extends TestCase
{
    protected $asset;
    protected $expense;

    public function setup()
    {
        $this->asset = new Account(new AccountType('asset'), 'Cash');
        $this->expense = new Account(new AccountType('expense'), 'Groceries');
    }

    public function testTransactionCanBeExecutedBetweenAssetAndExpenseAccounts()
    {
        $transaction = new Transaction($this->asset, $this->expense, '5', 'EUR');

        $result = $transaction->execute();

        self::assertTrue($result);
    }

    public function testTransactionCannotBeExecutedBetweenExpenseAndAssetAccounts()
    {
        $transaction = new Transaction($this->expense, $this->asset, '5', 'EUR');

        $result = $transaction->execute();

        self::assertFalse($result);
    }
}

It’s not so bad, right? We create a couple of account types, so we can create a couple of account objects which are then used to test can a transaction be executed or not.

And then we need to test the persistence of the transaction in another test case. Again we create a couple of account types, accounts, create a transaction…

And then we need to test the TransactionExecuted event. Account types, accounts, transaction…

Over and over again.

Traits to the rescue

What if we move the creation of those test doubles to traits?

A trait for creating account types:

tests/Traits/AccountTypeTrait.php

<?php declare(strict_types=1);
namespace Traits;

use App\Account\AccountType;

trait AccountTypeTrait
{
    public function fakeAssetAccountType() : AccountType
    {
        return new AccountType('asset');
    }

    public function fakeExpenseAccountType() : AccountType
    {
        return new AccountType('expense');
    }
}

and a trait for creating accounts:

tests/Traits/AccountTrait.php

<?php declare(strict_types=1);
namespace Traits;

use App\Account\Account;
use Traits\AccountTypeTrait;

trait AccountTrait
{
    use AccountTypeTrait;

    public function fakeAssetAccount() : Account
    {
        return new Account($this->fakeAssetAccountType());
    }

    public function fakeExpenseAccount() : Account
    {
        return new Account($this->fakeExpenseAccountType());
    }
}

The example test case from the beginning now becomes a little bit more clear, hopefully:

tests/App/UnitTest/Transaction/TransactionTest.php

<?php declare(strict_types=1);
namespace App\UnitTest\Transaction;

use PHPUnit\Framework\TestCase;
use App\Transaction\Transaction;
use Traits\AccountTrait;

class TransactionTest extends TestCase
{
    use AccountTrait;

    public function testTransactionCanBeExecutedBetweenAssetAndExpenseAccounts()
    {
        $transaction = new Transaction($this->fakeAssetAccount(), $this->fakeExpenseAccount(), '5', 'EUR');

        $result = $transaction->execute();

        self::assertTrue($result);
    }

    public function testTransactionCannotBeExecutedBetweenExpenseAndAssetAccounts()
    {
        $transaction = new Transaction($this->fakeExpenseAccount(), $this->fakeAssetAccount(), '5', 'EUR');

        $result = $transaction->execute();

        self::assertFalse($result);
    }
}

A trait for every test double, clearly named as to what they create. fakeAssetAccount, mockTransactionRepository. Each test double can now be reused more easily across different test cases, if for some reason they need to be changed, we change them only in one place.

Just need to be disciplined on the naming of the traits and the methods they provide.

Currently I see no pitfalls with this approach, but time will tell is this a good idea or not.

Happy hackin’!

Tags: mocks, php, phpunit, tdd, test doubles, testing, traits, unit testing.
Categories: Development, Programming.

Missing colors for PHPUnit

by Robert Basic on July 20, 2016.

I ran accross a minor issue today that I never experienced before. The colors for the PHPUnit’s output were missing. I had the colors=true directive set in the phpunit.xml configuration file, but the output was just black and white.

Turns out I was missing the posix extension, which is provided by the php-process package on Fedora. After installing it:

$ sudo dnf install php-process

all was good again in the world of unit testing.

Oh well.

Happy hackin’!

Tags: php, phpunit.
Categories: Development, Programming, Software.