Posts tagged 'mocks'

Mockery partial mocks

published on January 28, 2018.

In dealing with legacy code I often come across some class that extends a big base abstract class, and the methods of that class call methods on that big base abstract class that do an awful lot of things. I myself have written such classes and methods in the past. Live and learn.

One of the biggest problems with this kind of code is that it is pretty hard to test. The methods from the base class can return other objects, have side effects, do HTTP calls…

A typical example of this would be a base model class that has a getDb() method:

AbstractModel.php

<?php

abstract class AbstractModel
{
    protected $db = null;

    protected function getDb()
    {
        if ($this->db == null) {
            $db = Config::get('dbname');
            $user = Config::get('dbuser');
            $pass = Config::get('dbpass');
            $this->db = new PDO('mysql:host=localhost;dbname='.$db, $user, $pass);
        }
        return $this->db;
    }
}

which can be called in child classes to get access to the database connection:

ArticleModel.php

<?php

class ArticleModel extends AbstractModel
{
    public function listArticles()
    {
        $db = $this->getDb();
        $stmt = $db->query('SELECT * FROM articles');

        return $stmt->fetchAll();
    }
}

If we want to write unit tests for this listArticles() method, the best option would probably be to refactor the models so that the database connection can be injected either through the constructor, or with a setter method.

In case refactoring is not an option for whatever reason, what we can do is to create a partial mock of the ArticleModel using Mockery and then mock (well, stub to be more precise) out only the getDb() method that will always return a mocked version of the PDO class:

tests/ArticleModelTest.php

<?php

use Mockery\Adapter\Phpunit;

class ArticleModelTest extends MockeryTestCase
{
    public function testListArticlesReturnsAnEmptyArrayWhenTheTableIsEmpty()
    {
        $stmtMock = \Mockery::mock('\PDOStatement');
        $stmtMock->shouldReceive('fetchAll')
            ->andReturn([]);
        $pdoMock = \Mockery::mock('\PDO');
        $pdoMock->shouldReceive('query')
            ->andReturn($stmtMock);

        // Create a partial mock of ArticleModel
        $articleModel = \Mockery::mock('ArticleModel')->makePartial();

        // Stub the getDb method on the ArticleModel
        $articleModel->shouldReceive('getDb')
            ->andReturn($pdoMock);

        // List all the articles
        $result = $articleModel->listArticles();
        $expected = [];

        $this->assertSame($expected, $result);
    }
}

When we tell Mockery to make a partial mock of a class, any method on that partially mocked class that has expectations set up will be mocked, but calls to other methods Mockery will pass through the real class. In other words, even though the ArticleModel is a partial mock, anytime we call the listArticles() method Mockery will pass that call to the original method, and only the calls to the getDb() method are being mocked.

Using partial mocks should probably be an option of a last resort and we should always aim to refactor code to be easier for testing, but there are cases when they can really help us in testing legacy code.

Happy hackin’!

Mockery return values based on arguments

published on December 12, 2017.

Sometimes when working with Mockery mock objects, we want to tell a mocked method to return different values for different arguments. It is a rare occasion when I need this feature, but every time I need it, I’m happy it’s there.

The feature that allows us to return different values based on arguments is the andReturnUsing Mockery method, which takes a closure as an argument:

example.php

$dependencyMock = \Mockery::mock('SomeDependency');
$dependencyMock->shouldReceive('callDependency')
    ->andReturnUsing(function ($argument) {
        if ($argument <= 10) {
            return 'low';
        }

        return 'high';
    });

$dependencyMock->callDependency(10); // 'low'
$dependencyMock->callDependency(11); // 'high'

Any number of times we call our callDependency method on our mock object with a number 10 or less, it will return 'low', otherwise it will return 'high'.

Not much of an example, so let’s take a look at one a bit closer to a real world scenario.

Say we’re using Doctrine’s entity manager to get repositories for our entities in a service class:

src/ArticleService.php

<?php

class ArticleService
{
    public function __construct(EntityManager $em)
    {
        $this->articleRepo = $em->getRepository(Entity\Article::class);
        $this->authorRepo = $em->getRepository(Entity\Author::class);
    }
}

Not the best of the codes, but we’ll manage. The entity manager receives two calls to the getRepository method, once for the Article entity, once for the Author entity.

In a test case we could then set up the mocks like so:

tests/ArticleServiceTest.php

<?php

class ArticleServiceTest extends MockeryTestCase
{
    public function setup()
    {
        $this->authorRepositoryMock = \Mockery::mock(AuthorRepository::class);
        $this->articleRepositoryMock = \Mockery::mock(ArticleRepository::class);
        $this->entityManagerMock = \Mockery::mock(EntityManager::class);
    }

    public function testArticleService()
    {
        $repositoryMap = [
            'Entity\Author' => $this->authorRepositoryMock,
            'Entity\Article' => $this->articleRepositoryMock,
        ];
        $this->entityManagerMock->shouldReceive('getRepository')
            ->andReturnUsing(function($argument) use ($repositoryMap) {
                return $repositoryMap[$argument];
            });

        $articleService = new ArticleService($this->entityManagerMock);
    }
}

In the setup method we create the three mock objects that we need and then in the test method we create a $repositoryMap to help us map entities to repositories. The repository map could have been created in the andReturnUsing closure as well.

Now when we instantiate the ArticleService with the mocked entity manager, that mocked entity manager will receive two calls to the getRepository method in the ArticleServices constructor, and it will use the closure defined in andReturnUsing to return the correct repository mock objects.

More than one way to do it

Of course there is another way to achieve the same thing and that’s by using andReturn for the return value expectations, but it’s a bit more to write:

tests/ArticleServiceTest.php

<?php
    public function testArticleService()
    {
        $this->entityManagerMock->shouldReceive('getRepository')
            ->with('Entity\Author')
            ->andReturn($this->authorRepositoryMock);
        $this->entityManagerMock->shouldReceive('getRepository')
            ->with('Entity\Article')
            ->andReturn($this->articleRepositoryMock);

        $articleService = new ArticleService($this->entityManagerMock);
    }

It does the same thing as the previous thing. We might even argue that this second example is even clearer than the first example, sure, for a relatively small argument “map”. But if we need to handle a case with more than just two possible arguments, andReturnUsing can help us in those cases.

Happy hackin’!

P.S.: The proper way to do this actually would be to refactor that ArticleService to not get the two repositories from the entity manager, but to inject them directly instead.

PHP traits to create test doubles

published 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’!

Robert Basic

Robert Basic

Software engineer, consultant, open source contributor.

Let's work together!

If you require outsourcing or consulting help on your projects, I'm available!

Robert Basic © 2008 — 2018
Get the feed