$> blog detassigny.net https://www.detassigny.net -bash: blog: command not found en-US Fri, 25 Jun 2021 10:35:07 +0200 Fri, 25 Jun 2021 10:35:07 +0200 60 Safe Refactoring: split a class into smaller ones https://www.detassigny.net/posts/12/safe-refactoring-split-a-class-into-smaller-ones A step-by-step guide on how to refactor big chunks of code using unit tests Refactoring is hard! You most likely want to refactor old spaghetti code that you may or may not understand. There is a risk that you break everything in the process because of the complexity and potential edge cases that you didn't think about.

There are multiple ways to refactor old code and it also depends on what kind of code we're looking at improving (multiple classes and modules? a single class? old procedural code?).

In this guide, I will explain my general process when specifically looking at refactoring a single large class with the aim to split it into smaller ones. More importantly, I will explain how to do it in a safe way that should hopefully give you enough confidence to deploy your new changes without breaking anything.

 

Case study

Let's imagine we have the following class we wish to refactor:

namespace App\Controller;

use App\Post;
use App\PostRepository;
use Psr\Log\LoggerInterface;

class PostController
{
    private PostRepository $repository;
    private LoggerInterface $logger;

    public function __construct(PostRepository $postRepository, LoggerInterface $logger)
    {
        $this->repository = $postRepository;
        $this->logger = $logger;
    }

    public function getPostData(array $params): string
    {
        if (!isset($params['id'])) {
            throw new \InvalidArgumentException('Missing id parameter');
        }

        $id = (int) $params['id'];
        if ($id <= 0) {
            throw new \InvalidArgumentException('Invalid id parameter');
        }

        /** @var Post|null $post */
        $post = $this->repository->find($id);
        if (is_null($post)) {
            $this->logger->warning("Post with $id was not found");
            throw new \RuntimeException("Post with $id was not found");
        }

        return json_encode([
            'id' => $post->getId(),
            'text' => $post->getText()
        ]);
    }
}

It's a fairly large controller for a homemade API. It does a lot of things:

- it verifies the validity of the ID parameter and throws an exception in case it's not valid

- it uses Doctrine's repository to fetch the post entity.

- if the repository fails to get the data, it logs an error, and throws a runtime exception

- finally, if all went fine, it returns a json encoded response of the post data

 

Now, you may tell me "Well my work examples are more complex than that!" and you would be right... But obviously I keep the example simple enough so that it's not too complicated to explain the process. But the concept should be the same regardless.

 

Unit test coverage

Now that we know roughly what our class does, the first thing to do (if not already available) is to cover its behaviour with unit tests! We'll be using PHPUnit for the tests and Mockery to mock the dependencies.

namespace App\Tests\Controller;

use App\Controller\PostController;
use App\Post;
use App\PostRepository;
use Mockery;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class PostControllerTest extends TestCase
{
    use MockeryPHPUnitIntegration;

    private const POST_ID = 1;

    private PostRepository $repository;
    private LoggerInterface $logger;
    private PostController $controller;

    protected function setUp(): void
    {
        $this->repository = Mockery::mock(PostRepository::class);
        $this->logger = Mockery::mock(LoggerInterface::class);

        $this->controller = new PostController($this->repository, $this->logger);
    }

    /**
     * @dataProvider invalidParamsDataProvider
     */
    public function testGetPostDataThrowsErrorForInvalidParams(array $params, string $message): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage($message);

        $this->controller->getPostData($params);
    }

    public function invalidParamsDataProvider(): array
    {
        return [
            'Id is missing' => [[], 'Missing id parameter'],
            'Id is null' => [['id' => null], 'Missing id parameter'],
            'Id is set to zero' => [['id' => 0], 'Invalid id parameter'],
            'Id is set to a string' => [['id' => 'invalid'], 'Invalid id parameter']
        ];
    }

    public function testGetPostDataThrowsErrorIfPostNotFound(): void
    {
        $this->repository->allows('find')->with(self::POST_ID)->andReturn(null);

        $errorMessage = 'Post with id ' . self::POST_ID . ' was not found';

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage($errorMessage);

        $this->logger->expects('warning')->with($errorMessage);

        $this->controller->getPostData(['id' => 1]);
    }

    public function testGetPostDataReturnsJsonEncodedPostData(): void
    {
        $expected = '{"id":1,"text":"some text from the post"}';

        $post = Mockery::mock(Post::class);
        $post->allows('getId')->andReturn(self::POST_ID);
        $post->allows('getText')->andReturn('some text from the post');

        $this->repository->allows('find')->with(self::POST_ID)->andReturn($post);

        $actual = $this->controller->getPostData(['id' => 1]);

        $this->assertJsonStringEqualsJsonString($expected, $actual);
    }
}

You should try to cover all paths of your code. In this example, we make sure to cover:

- invalid parameters (using a data provider)

- error handling for a post not found

- the successful scenario: the post is retrieved and formatted in JSON

 

If you want to ensure that all paths are properly covered, several metrics and tools could potentially help you:

code coverage

mutation testing

 

But of course manually looking at it may be enough, depending on the size and complexity of the class.

 

Refactoring

We're now in a position where we've defined our expected behaviour (with unit tests) and can therefore validate if our refactor will be successful.

There are many ways we could go at simplifying this controller code, but here is my take on it:

- the parameter validation could be taken out and done in a value object.

- there is too much logic of doctrine repository and logging for a simple controller. Let's build a service that deals with that instead.

 

First, here is the PostId value object:

namespace App\Value;

class PostId
{
    private int $id;

    public function __construct(array $params)
    {
        if (!isset($params['id'])) {
            throw new \InvalidArgumentException('Missing id parameter');
        }

        if ((int) $params['id'] <= 0) {
            throw new \InvalidArgumentException('Invalid id parameter');
        }

        $this->id = (int) $params['id'];
    }

    public function getId(): int
    {
        return $this->id;
    }
}

It includes all our check for the parameters that the controller used to do.

 

Now let's add a post service that deals with fetching the post using doctrine, and logging a warning if it's not found:

namespace App;

use App\Value\PostId;
use Psr\Log\LoggerInterface;

class PostService
{
    private PostRepository $repository;
    private LoggerInterface $logger;

    public function __construct(PostRepository $repository, LoggerInterface $logger)
    {
        $this->repository = $repository;
        $this->logger = $logger;
    }

    public function getPost(PostId $postId): Post
    {
        /** @var Post|null $post */
        $post = $this->repository->find($postId->getId());
        if (is_null($post)) {
            $this->logger->warning("Post with id {$postId->getId()} was not found");
            throw new \RuntimeException("Post with id {$postId->getId()} was not found");
        }

        return $post;
    }
}

Perfect!

Now we can simplify our controller to use these two classes:

namespace App\Controller;

use App\PostService;
use App\Value\PostId;

class PostController
{
    /** @var PostService */
    private PostService $postService;

    public function __construct(PostService $postService)
    {
        $this->postService = $postService;
    }

    public function getPostData(array $params): string
    {
        $postId = new PostId($params);
        $post = $this->postService->getPost($postId);

        return json_encode([
            'id' => $post->getId(),
            'text' => $post->getText()
        ]);
    }
}

Now, that's an easy to read controller! We could also take out the json formatting into a separate class, but that will do for the purpose of this exercise.

 

Updating the tests

If you try running the tests now, it will fail because the controller dependencies have changed!

However, none of our logic actually has been modified: the code is doing the same thing as before.

This is probably the most important step, and yet if the refactor is done right, it's the simplest:

Instead of directly start writing tests for the new classes, and completely rewrite the existing ones, we need to slightly change the controller tests to prove that it still works. To do so, we'll only change the dependencies but without mocking them! Let it actually use the PostService properly, but we'll keep mocking the logger and the repository as before.

 

Our only change should therefore be to modify the PostController instanciation in the setUp method:

$this->controller = new PostController(new PostService($this->repository, $this->logger));

That's it! The tests still pass! We've now refactored everything and we're fairly confident that the behaviour hasn't changed.

 

What next?

Well, you could leave it like that, but most likely you may want to start writing tests for the PostService and then simplify the PostControllerTest by mocking the service and removing any mentions of the logger and repository. That way, not only your production code, but also your tests are simplified.

 

]]>
https://www.detassigny.net/posts/12/safe-refactoring-split-a-class-into-smaller-ones Fri, 25 Jun 2021 10:35:07 +0200 Gabriel de Tassigny
Design Pattern #3: Decorator Pattern https://www.detassigny.net/posts/11/design-pattern-3-decorator-pattern A pattern with a similar purpose to inheritance, but with some advantages! I have used the decorator design pattern a few times recently, and while it's simple and useful, it's surprisingly not that often applied.

Therefore, I decided to write a small post detailing how it works, and why you could potentially use it.

 

What's the decorator pattern?

The concept of a decorator pattern is fairly simple: let's imagine that you already have an established concrete class (like a service) doing something and this class implements some interface. You want to add a piece of extra functionality to it but that work isn't a must-have, just something to add on top. You could either:

- add your changes to the existing class

- extend the class with a new child class

- use the decorator pattern: You create a new class that will implement the same interface, and receive the original class (i.e. the decorated class) as a parameter using dependency injection.

Each option has its pros and cons, but let's see in practice how you would use the decorator pattern.

 

Example

 

Decorated class

Let's say we have a service that sends an email (this will be our decorated class) and it extends an interface:

interface MailSenderInterface
{
    public function sendEmail(string $destination, string $subject, string $message): bool;
}


class MailSenderService implements MailSenderInterface
{
    public function sendEmail(string $destination, string $subject, string $message): bool
    {
        return mail($destination, $subject, $message);
    }
}

 

Decorator class

Now, we realise that in some cases we want to log an error in case the mail failed to be sent. Let's use our decorator pattern to do that!

class ErrorLoggerDecorator implements MailSenderInterface
{
    private MailSenderInterface $decorated;
    private LoggerInterface $logger;

    public function __construct(MailSenderInterface $decorated, LoggerInterface $logger)
    {
        $this->decorated = $decorated;
        $this->logger = $logger;
    }

    public function sendEmail(string $destination, string $subject, string $message): bool
    {
        if ($this->decorated->sendEmail($destination, $subject, $message)) {
            return true;
        }
        $this->logger->error('Failed to send e-mail to ' . $destination);

        return false;
    }
}

That's it! we inject our decorated class into it, use it to do the job of sending the email, and in case it failed we log an error using our application logger.

Now, you could say that we could have done that directly in the decorated class, but perhaps your mail sender service is used multiple times in your application and you want to log one specific instance only.

Also, this is more in line with SOLID Principles: each class does one thing only, and you're extending instead of modifying your existing code.

 

Make your application use it

If your application is well structured, other parts of the code would hopefully already reference the interface instead of the decorated class. For example:

class MailSendingController
{
    private MailSenderInterface $mailSender;

    public function __construct(MailSenderInterface $mailSender)
    {
        $this->mailSender = $mailSender;
    }

    public function sendEmail(array $params): void
    {
        $this->mailSender->sendEmail($params['dest'], $params['subject'], $params['message']);
    }
}

This means that you wouldn't have to change anything there. You simply need to inject the decorator into that controller:

$decorator = new ErrorLoggerDecorator(new MailSenderService(), $logger);
$controller = new MailSendingController($decorator);

If you're using a framework, this might be done via configuration. In fact, Symfony actually has built-in support for service decoration in its configuration making it even easier to set up.

 

Chaining decorators

Let's add another decorator: In some parts of my application, I want to retry one more time sending the email if it failed.

We can add a new decorator for that:

class RetryDecorator implements MailSenderInterface
{
    private MailSenderInterface $decorated;

    public function __construct(MailSenderInterface $decorated)
    {
        $this->decorated = $decorated;
    }

    public function sendEmail(string $destination, string $subject, string $message): bool
    {
        if ($this->decorated->sendEmail($destination, $subject, $message)) {
            return true;
        }

        return $this->decorated->sendEmail($destination, $subject, $message);
    }
}

Simple, isn't it?

You can then add this one on top of the other if needed.

 

Why should I use this instead of inheritance?

While both cases studied above could have been solved with inheritance, inheritance has some limits and disadvantages that decorators can fix:

- if your service is declared as a final class, you won't be able to use inheritance. This is often the case with some 3rd party libraries, including some Symfony packages.

- You can chain your decorators however you like! To use our examples above: Maybe one part of your application wants to log error but try sending only the email once, maybe another one would like to send twice and log the error as well, etc...

 

]]>
https://www.detassigny.net/posts/11/design-pattern-3-decorator-pattern Sat, 03 Apr 2021 15:48:36 +0200 Gabriel de Tassigny
Mocking in PHP #4: Phake https://www.detassigny.net/posts/10/mocking-in-php-4-phake A simple to use and yet powerful mocking library In previous articles, we've seen how it's possible to write your own mocks, and also how easier it is to use PHPUnit built-in mocking mechanism. We've then finally looked at one of the most popular PHP mocking library: Mockery.

We'll now look at an alternative one called Phake. We will compare the two and what their pros and cons are.

Phake is certainly less famous than Mockery, however it has a slightly different approach to mocking in some cases. It's worth noting that it's heavily inspired by Mockito, a Java mocking library.

Case study

In order to compare these libraries appropriately, let's reuse our example from the previous articles.

The following PostService class takes as dependencies a doctrine entity manager and a repository.

It also has two public methods that we will want to test :

- one to create a post

- one to retrieve a post from the DB

<?php

namespace App;

use Doctrine\ORM\EntityManagerInterface;

class PostService
{
    /** @var EntityManagerInterface */
    private $entityManager;

    /** @var PostRepository  */
    private $postRepository;

    public function __construct(EntityManagerInterface $entityManager, PostRepository $postRepository)
    {
        $this->entityManager = $entityManager;
        $this->postRepository = $postRepository;
    }

    public function createPost(Post $post): void
    {
        $this->entityManager->persist($post);
        $this->entityManager->flush();
    }

    public function getPost(int $id): Post
    {
        /** @var Post|null $post */
        $post = $this->postRepository->find($id);
        if (is_null($post)) {
            throw new \RuntimeException('Post not found');
        }

        return $post;
    }
}

 

Installation

Just like with Mockery, Phake can be installed with composer:

composer require --dev phake/phake

 

Now that it's installed, let's have a look at unit testing this service class.

Unit tests

<?php

namespace App\Tests\Phake;

use App\Post;
use App\PostRepository;
use App\PostService;
use Doctrine\ORM\EntityManagerInterface;
use Phake;
use Phake_IMock;
use PHPUnit\Framework\TestCase;

class PostServiceTest extends TestCase
{
    private const POST_ID = 1;

    /** @var PostRepository|Phake_IMock */
    private $postRepository;

    /** @var EntityManagerInterface|Phake_IMock */
    private $entityManager;

    /** @var PostService */
    private $service;

    protected function setUp(): void
    {
        $this->postRepository = Phake::mock(PostRepository::class);
        $this->entityManager = Phake::mock(EntityManagerInterface::class);
        $this->service = new PostService($this->entityManager, $this->postRepository);
    }

    public function testGetPost(): void
    {
        $expected = new Post();
        Phake::when($this->postRepository)->find(self::POST_ID)->thenReturn($expected);

        $actual = $this->service->getPost(self::POST_ID);

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

    public function testGetPost_PostNotFound(): void
    {
        $this->expectException(\RuntimeException::class);

        $this->service->getPost(self::POST_ID);
    }

    public function testCreatePost(): void
    {
        $post = new Post();

        $this->service->createPost($post);

        Phake::verify($this->entityManager)->persist($post);
        Phake::verify($this->entityManager)->flush();
    }
}

 

We can notice a few similarities with Mockery:

- we use a static call to Phake::mock to instantiate our mocks.

- we can define behaviours fairly easily for each mock's methods.

 

However, there are some differences in the approach and syntax:

- Phake doesn't need to include any PHP trait to be able to make assertions in PHPUnit (compared to Mockery's MockeryPHPUnitIntegration trait)

- We use Phake::when to define an expected outcome of a method (return statement, throwing an exception...)

- In testGetPost_PostNotFound, we didn't define any expected outcome. That's because by default Phake methods will return null, which is what we wanted here.

- Phake::verify is used to assert an excepted call has been made to the mock. This is a fairly big difference compared to Mockery for two reasons:

   1. Mockery uses the same call to the expects method to both define an expected outcome and verify that a call was made.

   2. Phake::verify is called after the method call takes place, just like a regular PHPUnit assertion. This is in my opinion way more logical.

 

I personally prefer Phake's syntax than Mockery's. It's more straightforward, the distinction between Phake::when and Phake::verify gives a clearer intent, and verifying calls at the end of the test is more instinctive.

 

This is however a matter of personal preferences, as both Mockery and Phake present similar functionalities. 

Here is a link to Phake's documentation if you are interested.

]]>
https://www.detassigny.net/posts/10/mocking-in-php-4-phake Thu, 22 Oct 2020 21:56:44 +0200 Gabriel de Tassigny
Mocking in PHP #3: Mockery https://www.detassigny.net/posts/9/mocking-in-php-3-mockery How to use one of PHP's most popular mocking library In previous articles, we've seen how it's possible to write your own mocks, and also how easier it is to use PHPUnit built-in mocking mechanism.

However, there are a few libraries dedicated to mocking in PHP land. Their popularities prove that while PHPUnit can work, it isn't always enough and its syntax can be rather confusing at times.

In this article, we'll demonstrate how to use mockery.

Case study

Let's reuse our example from the previous article of the PostService class.

This class takes as dependencies a doctrine entity manager and a repository.

It also has two public methods that we will want to test :

- one to create a post

- one to retrieve a post from the DB

<?php

namespace App;

use Doctrine\ORM\EntityManagerInterface;

class PostService
{
    /** @var EntityManagerInterface */
    private $entityManager;

    /** @var PostRepository  */
    private $postRepository;

    public function __construct(EntityManagerInterface $entityManager, PostRepository $postRepository)
    {
        $this->entityManager = $entityManager;
        $this->postRepository = $postRepository;
    }

    public function createPost(Post $post): void
    {
        $this->entityManager->persist($post);
        $this->entityManager->flush();
    }

    public function getPost(int $id): Post
    {
        /** @var Post|null $post */
        $post = $this->postRepository->find($id);
        if (is_null($post)) {
            throw new \RuntimeException('Post not found');
        }

        return $post;
    }
}

Installation

First, if you haven't already done so, you will need to install mockery using composer:

composer require --dev mockery/mockery

Unit tests

 

<?php

namespace App\Tests\Mockery;

use App\Post;
use App\PostRepository;
use App\PostService;
use Doctrine\ORM\EntityManagerInterface;
use Mockery;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;

class PostServiceTest extends TestCase
{
    use MockeryPHPUnitIntegration;

    private const POST_ID = 1;

    /** @var PostRepository|MockInterface */
    private $postRepository;

    /** @var EntityManagerInterface|MockInterface */
    private $entityManager;

    /** @var PostService */
    private $service;

    protected function setUp(): void
    {
        $this->postRepository = Mockery::mock(PostRepository::class);
        $this->entityManager = Mockery::mock(EntityManagerInterface::class);
        $this->service = new PostService($this->entityManager, $this->postRepository);
    }

    public function testGetPost(): void
    {
        $expected = new Post();

        $this->postRepository->expects('find')
            ->with(self::POST_ID)
            ->andReturn($expected);

        $actual = $this->service->getPost(self::POST_ID);

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

    public function testGetPost_PostNotFound(): void
    {
        $this->expectException(\RuntimeException::class);

        $this->postRepository->expects('find')
            ->with(self::POST_ID)
            ->andReturn(null);

        $this->service->getPost(self::POST_ID);
    }

    public function testCreatePost(): void
    {
        $post = new Post();

        $this->entityManager->expects('persist')->with($post);
        $this->entityManager->expects('flush');

        $this->service->createPost($post);
    }
}

Compared to PHPUnit mocks, we can note a few things:

- We use the static call to Mockery::mock to create a mock of the class passed as an argument.

- We use a trait named MockeryPHPUnitIntegration to tell PHPUnit to treat all Mockery's expects calls as assertions.

- In testGetPost_PostNotFound, we need to tell Mockery that the call to find should return null. If not, an error will be thrown by Mockery.

Other functionalities

Mockery supports many useful additional features:

- Spies

- Partial mocks

- allows method let you define a desired outcome for a method call without requiring it to be called (compared to expects)

 

...and many more! Have a look at their documentation if you are interested.

]]>
https://www.detassigny.net/posts/9/mocking-in-php-3-mockery Wed, 14 Oct 2020 21:19:55 +0200 Gabriel de Tassigny
Mocking in PHP #2: PHPUnit mocks https://www.detassigny.net/posts/8/mocking-in-php-2-phpunit-mocks How to use PHPUnit built-in mocking system As we've seen in the previous article, writing your own mocks is certainly a good way to understand how mocking works and why it's a powerful tool in the context of unit tests. However, writing custom mocks takes time and can generate a lot of code once your code base starts to grow.

Most developers use mocking tools or frameworks to avoid this issue.

PHPUnit comes with some mocking mechanism, so this can be a good place to start!

Case study

Let's reuse our example from the previous article of the PostService class.

This class takes as dependencies a doctrine entity manager and a repository.

It also has two public methods that we will want to test :

- one to create a post

- one to retrieve a post from the DB

<?php

namespace App;

use Doctrine\ORM\EntityManagerInterface;

class PostService
{
    /** @var EntityManagerInterface */
    private $entityManager;

    /** @var PostRepository  */
    private $postRepository;

    public function __construct(EntityManagerInterface $entityManager, PostRepository $postRepository)
    {
        $this->entityManager = $entityManager;
        $this->postRepository = $postRepository;
    }

    public function createPost(Post $post): void
    {
        $this->entityManager->persist($post);
        $this->entityManager->flush();
    }

    public function getPost(int $id): Post
    {
        /** @var Post|null $post */
        $post = $this->postRepository->find($id);
        if (is_null($post)) {
            throw new \RuntimeException('Post not found');
        }

        return $post;
    }
}

 

Unit tests

This time, we will use the createMock method from PHPUnit to create our mocks.

This method takes the name of the class as an argument, and returns a mock version of that class.

Most mocking frameworks (including PHPUnit) works in a similar way that what we did when we created our own mocks:

- the mock extends our original class

- public methods are not doing anything by default (they usually simply returns null)

- we can override the behaviour we want for each public method

<?php

namespace App\Tests\PHPUnit;

use App\Post;
use App\PostRepository;
use App\PostService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class PostServiceTest extends TestCase
{
    private const POST_ID = 1;

    /** @var PostRepository|MockObject */
    private $postRepository;

    /** @var EntityManagerInterface|MockObject */
    private $entityManager;

    /** @var PostService */
    private $service;

    public function setUp(): void
    {
        $this->postRepository = $this->createMock(PostRepository::class);
        $this->entityManager = $this->createMock(EntityManagerInterface::class);
        $this->service = new PostService($this->entityManager, $this->postRepository);
    }

    public function testGetPost(): void
    {
        $expected = new Post();

        $this->postRepository->method('find')
            ->with(self::POST_ID)
            ->willReturn($expected);

        $post = $this->service->getPost(self::POST_ID);

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

    public function testGetPost_PostNotFound(): void
    {
        $this->expectException(\RuntimeException::class);

        $this->postRepository->method('find')
            ->with(self::POST_ID)
            ->willReturn(null);

        $this->service->getPost(self::POST_ID);
    }

    public function testCreatePost(): void
    {
        $post = new Post();

        $this->entityManager->expects($this->once())
            ->method('persist')
            ->with($post);

        $this->entityManager->expects($this->once())
            ->method('flush');

        $this->service->createPost($post);
    }
}

When testing getPost, we mock the behaviour of postRepository using ->method(...)->with(...)->willReturn(...). 

It means we can define what each method should return for a given set of parameters.

Furthermore, when testing createPost, we prefix the method mocking by a call to ->expects($this->once()). This will perform an assertion that the call has been made once.

There are of course more options on what you can assert and mock (e.g. you can make a method throw an exception, you can verify successive calls to a method...). PHPUnit documentation on mock is full of information on all the possibilities.

 

]]>
https://www.detassigny.net/posts/8/mocking-in-php-2-phpunit-mocks Tue, 07 May 2019 22:37:59 +0200 Gabriel de Tassigny
Mocking in PHP #1: Writing your own mocks https://www.detassigny.net/posts/7/mocking-in-php-1-writing-your-own-mocks A good way to vizualize how mocking works in the first place is to implement your own mocks Mocking is a fundamental part of unit testing. Without mocks, you're stuck into writing tests that need to handle all dependencies of your class. Which sometimes means having to interact with the database, a file system, an API... It's simply way to complicated and is outside the scope of unit testing anyway.

In this series of articles called "Mocking in PHP", I will explore different ways of mocking your dependencies when writing PHPUnit test.

While definitely not the most common way of doing this, writing your own mocks can be a great way to understand what a mock is and what it does.

On the following articles, I will then go through different methods, such as :

- PHPUnit mocks: Their syntax isn't very straightforward but they come out of the box with PHPUnit without a need for any other package.

- Mockery: This is probably the most popular PHP mocking library right now.

- Phake: Another mocking library. My personal favourite.

 

Case Study

First, let's define what we'll be testing here.

Let's say we have a service that is used by our application to interact with blog posts:

namespace App;

use Doctrine\ORM\EntityManagerInterface;

class PostService
{
    /** @var EntityManagerInterface */
    private $entityManager;

    /** @var PostRepository  */
    private $postRepository;

    public function __construct(EntityManagerInterface $entityManager, PostRepository $postRepository)
    {
        $this->entityManager = $entityManager;
        $this->postRepository = $postRepository;
    }

    public function createPost(Post $post): void
    {
        $this->entityManager->persist($post);
        $this->entityManager->flush();
    }

    public function getPost(int $id): Post
    {
        /** @var Post|null $post */
        $post = $this->postRepository->find($id);
        if (is_null($post)) {
            throw new \RuntimeException('Post not found');
        }

        return $post;
    }
}

It contains two methods :

- one to create a Post entity

- one to retrieve a Post by a given ID

Our service also has two Injected dependencies:

- Doctrine's entity manager. We need it for persisting our entities into the database.

- Our PostRepository, which is also a doctrine repository. We use that to search for entities.

 

Unit tests

In order to test our PostService class, we will need three different classes :

- one mock class for the repository

- one mock class for the entity manager

- our test suite

 

Mocking the repository

When writing our mock for the repository, we simply need to deal with the methods that our service will interact with, and see what possible outcome we want from our mock.

In this case, the service only uses one method from the repository : find.

Then, we have two different expected behavior we want to handle :

- an entity is returned

- null is returned

namespace App\Tests\OwnMocks;

use App\Post;
use App\PostRepository;

class MockPostRepository extends PostRepository
{
    /** @var int */
    private $expectedId;

    /** @var Post */
    private $post;

    public function __construct(Post $post, int $expectedId)
    {
        $this->post = $post;
        $this->expectedId = $expectedId;
    }

    public function find($id, $lockMode = null, $lockVersion = null)
    {
        if ($id === $this->expectedId) {
            return $this->post;
        }

        return null;
    }
}

There are probably many ways to achieve that, but this is a good example.

First, our mock needs to extend the class that we're mocking. Without this, we would get an error that our mock isn't a PostRepository, as we type hinted it in the PostService constructor.

Then, you'll notice we're injecting a post and an expectedId into our mock constructor. Then, once we call find, if the id parameter is the expected one, we return our post. Otherwise we return null.

We can then test both behavior without any real repository implementations (no database calls or complicated logic).

 

Mocking the entity manager

Now, let's have a look at the entity manager! In our PostService, you may have noticed that we actually inject the EntityManagerInterface. This means that our mock will need to implement the interface and all its methods. In this case, there is a lot of them! No problem though, we can simply leave all the ones we're not interested as empty.

namespace App\Tests\OwnMocks;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\ResultSetMapping;

class MockEntityManager implements EntityManagerInterface
{
    private $persistedObject = null;
    private $flushed = false;

    public function persist($object)
    {
        $this->persistedObject = $object;
    }

    public function flush()
    {
        $this->flushed = true;
    }

    public function isPersisted($object): bool
    {
        return $this->persistedObject === $object;
    }

    public function hasFlushed(): bool
    {
        return $this->flushed;
    }

    /** Defining all remaining EntityManager methods below */
    public function getCache() {}
    public function getConnection() {}
    public function getExpressionBuilder() {}
    public function beginTransaction() {}
    public function transactional($func) {}
    public function commit() {}
    public function rollback() {}
    public function createQuery($dql = '') {}
    public function createNamedQuery($name) {}
    public function createNativeQuery($sql, ResultSetMapping $rsm) {}
    public function createNamedNativeQuery($name) {}
    public function createQueryBuilder() {}
    public function getReference($entityName, $id) {}
    public function getPartialReference($entityName, $identifier) {}
    public function close() {}
    public function copy($entity, $deep = false) {}
    public function lock($entity, $lockMode, $lockVersion = null) {}
    public function getEventManager() {}
    public function getConfiguration() {}
    public function isOpen() {}
    public function getUnitOfWork() {}
    public function getHydrator($hydrationMode) {}
    public function newHydrator($hydrationMode) {}
    public function getProxyFactory() {}
    public function getFilters() {}
    public function isFiltersStateClean() {}
    public function hasFilters() {}
    public function find($className, $id) {}
    public function remove($object) {}
    public function merge($object) {}
    public function clear($objectName = null) {}
    public function detach($object) {}
    public function refresh($object) {}
    public function getRepository($className) {}
    public function getMetadataFactory() {}
    public function initializeObject($obj) {}
    public function contains($object) {}
    public function __call($name, $arguments) {}
    public function getClassMetadata($className) {}
}

We implement the two methods used by our service : flush and persist.

Then, as these methods don't return anything, we instead need the ability to check that they were correctly called.

To do so, I added two methods that returns boolean values : isPersisted and hasFlushed.

We then simply set variables in our flush and persist methods, and verify their values in our added boolean methods.

 

Test suite

Now that we have our mocks, let's write our actual tests!

namespace App\Tests\OwnMocks;

use App\Post;
use App\PostService;
use PHPUnit\Framework\TestCase;

class PostServiceTest extends TestCase
{
    private const EXPECTED_ID = 1;

    /** @var Post */
    private $expectedPost;

    /** @var MockPostRepository */
    private $postRepository;

    /** @var MockEntityManager */
    private $entityManager;

    /** @var PostService */
    private $service;

    public function setUp(): void
    {
        $this->expectedPost = new Post();
        $this->postRepository = new MockPostRepository($this->expectedPost, self::EXPECTED_ID);
        $this->entityManager = new MockEntityManager();
        $this->service = new PostService($this->entityManager, $this->postRepository);
    }

    public function testGetPost(): void
    {
        $post = $this->service->getPost(self::EXPECTED_ID);

        $this->assertSame($this->expectedPost, $post);
    }

    public function testGetPost_PostNotFound(): void
    {
        $this->expectException(\RuntimeException::class);

        $this->service->getPost(2);
    }

    public function testCreatePost(): void
    {
        $post = new Post();
        $this->service->createPost($post);

        $this->assertTrue($this->entityManager->isPersisted($post));
        $this->assertTrue($this->entityManager->hasFlushed());
    }
}

We create and inject our mocks via the setUp method of PHPUnit.

Then, we can test how our service methods behave using our mocks.

 

]]>
https://www.detassigny.net/posts/7/mocking-in-php-1-writing-your-own-mocks Sat, 02 Mar 2019 12:54:09 +0100 Gabriel de Tassigny
You need to write tests https://www.detassigny.net/posts/6/you-need-to-write-tests Writing unit tests at work should be one of your top priority! In my first ever programming job, I remember hearing about unit tests a lot, but I simply didn't get the importance of them : "Why do I need to waste time writing extra code to test my own code? I know how my code works!!"

It felt completely useless and also extremely boring. But boy, was I wrong! A few years later I progressively realised that unit tests are necessary in having a stable and ever-evolving professional code base.

 

"Any code that isn't covered by unit tests is legacy code"

That's a bold statement, isn't it? I remember hearing it at a tech conference a while ago. For those who may not know, legacy code is basically code that we judge very hard to maintain and change, or even understand. Every tech company has it, to some extend. They're often due to change of mind, emergencies that we didn't clean later, or general bad maintainance of the code base.

But surely you would say, if I write some very clean code right now, it's not legacy code! Even without unit test, right?

Well, actually it is... or more exactly, it will be!

Because, let's say that you write a brand new shiny class and its purpose is very clear... What makes you think that Bob the new software engineer in 5 years will understand it? Especially if you're not around anymore? And what if in those 5 years a lot of bug fixes, edge cases, and extra methods were added in your class? Well, you have it! Congratulations, you created legacy code! Sure, it wasn't ugly at the start, but it became so in the long run!

 

Understanding the purpose of tests

One problem is that junior developers tend to not understand the actual purpose for unit tests. That was my mistake too back in the days.

It's not really about testing right away that your new code works (although it can help you achieve that!). It's about writing a contract on how your code is supposed to behave and what's expected of it. By defining all the expected outcomes of your class / method / function, you simply show what the intent of your code is.

One of the main issue with software engineering is of time : code bases evolve and go through several teams of developers all with different ideas and new tech trends they want to try out. The code gets old, messy, and hard to understand.

Therefore, defining a contract that tells future developers what each part of the code was intended to do is fundamental.

It means that :

- new features added in the code can be done with more confidence as tests might break if bugs are introduced

- refactoring can be done more easily for the same reason

- unit tests can be read to give some context on what to expect (in case the production code isn't clear enough)

 

]]>
https://www.detassigny.net/posts/6/you-need-to-write-tests Thu, 10 Jan 2019 23:59:33 +0100 Gabriel de Tassigny
I gave up on Android https://www.detassigny.net/posts/5/i-gave-up-on-android I had been using Google's mobile OS for a while, but now it's time to say goodbye... This is the story of why I used Android phones, and why I recently switched to iOS. Therefore, I will talk about both Google and Apple since they're the only two real options on this market.

Let's be clear, I always enjoyed Apple products. However, they tend to be a bit pricey.

While I'm okay in buying an expensive computer as I tend to use them for work related stuff, I find it harder to justify spending around €1,000 for a phone. Therefore, I used to mostly look at mid-range android phones (from €300 to €500). In theory, they fit my need of a lower price phone that does everything I need well enough. I always found iOS to be the better operating system, however android was good enough for me as I could save €500!

But I give up on that now for multiple reasons... Please note that some of these reasons might not be an issue on very expensive android phones, but as I said, I honestly prefer iOS for a similar price.

Anyway, let's go into details on why I gave up on Android!

 

Google

 

Google Data Monster

Oh, Google... There would be so much to say about this company, but I'm going to be brief. Let's just say that over the years I've tend to lose respect for Google more and more.

This is of course mostly related to the privacy issues. Overall, I feel that I really can't trust this company with my data anymore.

Just have a look over this wikipedia page.

Apple tends to respect more its users so I'm glad to join them on that aspect.

I had a look many times at setting up somehow a Googleless Android, but roms are complicated to install, and not always well maintained depending on your device. Also, some android apps and services may still requires you sign-in using a Google account.

 

A dying phone

 

Moto X play

This is my (now former) android phone: A Motorola Moto X Play.

When I bought it two and a half years ago, it was at a decent price, had some of the best online reviews, and was powerful enough for my needs.

I was really happy with it... for about a year and a half. Then everything progressively started to fall apart!

First and foremost, my micro USB port was getting worse and worse. I had to do this trick I found online of using a sharp stick to replace it properly almost every week so that I can somehow manage to charge my phone. In the last few months, it sometimes took me several minutes for me to manage to find the sweet spot where my phone would accept to charge. And then I had to make sure my phone wouldn't move at all, else I would start over again. And some micro usb cables simply wouldn't work at all anymore! So I sometimes couldn't charge my phone in my friends' house. Extremely annoying!

Speaking of battery, my phone would also randomly shut down saying the battery level was at zero even though I had plenty left. It would then be able to turn back on, so there definitely was some battery left. This would mostly happen when using resource heavy applications (the camera most often triggered it, but Google Maps would do it too sometimes). I like traveling a lot so having my phone shutdown when I'm trying to take pictures in the middle of Kyoto or Seoul was very painful.

Another issue I had was that my flashlight completely stopped working 4 months ago, after an OS update. Thanks for the update Google!

One other time, around 6 months ago, I tried to migrate data from the internal memory towards the micro SD card. I used the system tool that is part of Android for that, so it should be all ok, right? Haha, well no. Actually it failed somehow, but more importantly it managed to make the android back button stop working completely. If you have an android phone, I challenge you to try using yours for an hour without that button. After hours of attempts and online research, I managed to fix it by installing a completely different Launcher... What the hell, Android!

 

Alternatives?

So after all this, I gave up. Sorry Google! It's not me, it's definitely you! You and your lack of respect for my online privacy. You and your OS that seems to mostly work but that gave me so many headaches!

Ultimately, I started to look at alternatives, and let's be honest there is a big lack of alternatives!

There is only one real one in Apple's iOS. I bought an iPhone XR and I like it so far. However, I wish there was some true open source alternative. Basically, what Linux is for computers. Hopefully this will happen some day, but I doubt it...

]]>
https://www.detassigny.net/posts/5/i-gave-up-on-android Mon, 24 Dec 2018 15:46:33 +0100 Gabriel de Tassigny
Design Pattern #2: Dependency Injection https://www.detassigny.net/posts/4/design-pattern-2-dependency-injection A very useful pattern to keep your application clean and enable unit testing In the previous design pattern article, I went over the Value Object pattern.

This time I will talk about how and why you should use dependency injection. Personally, this may be the design pattern that I use the most, and it's generally very common so if you don't know it, I strongly encourage you to learn about it!

I'm personally a bit annoyed about articles that uses cats and dogs, or wheels and cars as examples for this kind of stuff. It doesn't reflect what people usually work on. Therefore, I will instead use real life examples, mostly taken from this blog's source code.

What's dependency injection and how do I use it?

Let's say you have a class, and that class has some dependencies on other classes.

In our case, I have a service class to retrieve a blog post (such as the one you're reading right now) for a given ID, and its dependency is a doctrine repository that is in charge of connecting to the database and retrieve that post.

Well, one naive way to have your main class using your dependencies might be to declare its dependency inside it.

Like this :

class PostViewingService
{
    /** @var PostRepository */
    private $repository;

    public function __construct()
    {
        // Create doctrine entity manager, and then get the repository from it
        $config = Setup::createAnnotationMetadataConfiguration([__DIR__ . '/Entity']);
        $em = EntityManager::create(['driver => 'pdo_mysql', 'user' => 'user'], $config);
        $this->repository = $em->getRepository(Post::class);
    }

    public function getPost(int $id): Post
    {
        /** @var Post $post */
        $post = $this->repository->find($id);

        if (!$post) {
            throw new PostNotFoundException("Post with ID {$id} was not found");
        }
        return $post;
    }
}

class PostRepository extends EntityRepository
{
}

That would work! You would be able to use your service, and it definitely has an instance of the repository created in its constructor. However, the constructor is now polluted with all the logic of how to create the repository.

This is how it would look instead using dependency injection :

class PostViewingService
{
    /** @var PostRepository */
    private $repository;

    public function __construct(PostRepository $postRepository)
    {
        $this->repository = $postRepository;
    }

    public function getPost(int $id): Post
    {
        /** @var Post $post */
        $post = $this->repository->find($id);

        if (!$post) {
            throw new PostNotFoundException("Post with ID {$id} was not found");
        }
        return $post;
    }
}

class PostRepository extends EntityRepository
{
}

Isn't that cleaner? Now, of course you'll still need to create the repository somewhere beforehand to pass it into the service.

However, it makes your code way easier to read. And there are more advantages than just code readability too...

Why should I use this?

Centralize your instantiations

One reason you may want to use dependency injection is to have your instantiations written in one place only. Using our previous example, what would happen if I added another service that needs the PostRepository?

If the repository was instantiated in the services constructor as we did first, the new service would need to do the same! It means we would duplicate that ugly code to retrieve the repository from the doctrine entity manager.

However, using dependency injection, we can simply pass the same instance of the repository to both services, and avoid having duplicated code.

Having no duplication of that code means that if we ever need to change how that repository is created, we can change it in one place only.

Now, if you don't know how to handle these instantiations, I suggest having a look at containers, such as this one.

Unit testing

Another huge advantage to dependency injection is that it allows us to unit test our code easily.

Indeed, if you wanted to test the behaviour of the getPost method on our service, you would need to have a database setup for the test, and therefore you would test both the service and its dependency (the repository). While this isn't necessarily a bad idea, it can be quite difficult and slow. Furthermore, it would be closer to an integration test than a unit test.

Instead, dependency injection allows us to use Mocks.

Here is how tests could look like using PHPUnit and Phake as a mocking library :

class PostViewingServiceTest extends TestCase
{
    /** @var PostViewingService */
    private $service;

    /** @var PostRepository */
    private $repository;

    public function setUp()
    {
        $this->repository = Phake::mock(PostRepository::class);
        $this->service = new PostViewingService($this->repository);
    }

    public function testGetPost_NotFound(): void
    {
        $this->expectException(PostNotFoundException::class);

        Phake::when($this->repository)->find(self::POST_ID)->thenReturn(null);

        $this->service->getPost(1);
    }

    public function testGetPost(): void
    {
         $post = new Post();
         Phake::when($this->repository)->find(self::POST_ID)->thenReturn($post);

        $result = $this->service->getPost(1);

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

Here we create a mock of the repository and inject it into our service. This means we don't need any database at all!

We can then define the mocks behaviour in our two test scenarios :

- if the repository returns null, the service method should throw an exception

- if the repository returns a Post entity, the service method should return it

If you're not too familiar with unit tests and mocks, I strongly suggest having a look at them as they're very big parts of software engineering.

]]>
https://www.detassigny.net/posts/4/design-pattern-2-dependency-injection Thu, 06 Dec 2018 07:32:07 +0100 Gabriel de Tassigny
Managing my passwords with KeePass and OwnCloud https://www.detassigny.net/posts/3/managing-my-passwords-with-keepass-and-owncloud I had been trying to find the perfect setup for managing my passwords for a while, and I think I'm getting close. If you don't really trust online password managers like me, you might also want to manage your passwords offline.

Enters KeePass!

 

KeePass / KeePassX / KeePassXC

KeePass logoKeePassX logo       KeePassXC

To sum it up, KeePass was originally an open-source offline password manager on windows. It basically created its own format for storing your database file of credentials, and you set up a master password to access the database using the software.

It also allows you to generate random passwords.

KeePassX was then created as an attempt to have a linux version of it. (Although I think that nowadays both KeePass and KeePassX are actually cross-platform applications).

After a while, a big part of the keepass community decided to fork KeePassX and created KeePassXC (for slow development reasons on the former, apparently).

KeePassXC also has a way nicer UI / UX than it's predecessor in my opinion. Furthermore, it runs on Mac OS / Linux / Windows.

The concept is the same : you have a local encrypted database file containing all your passwords and can access them using a master password.

I've been using KeePassXC for a while now on my macbook, and I have to admit that I can't complain about anything. It's a great application!

 

What about smartphones?

Well, I personally own an Android phone (although I'm probably going to switch to iOS soon, but more on that in another post later...)

I am using an application called KeePass2Android. There are others, but that's the one I preferred.

It also uses a local file to store your password.

 

Keep it in sync

Now, you're probably wondering : how do I keep the 2 databases (computer and phone) in sync?

Well, there are multiple solutions for that : Dropbox, Google Drive, BT Sync (now renamed resilio sync)...

I personally used a private owncloud server that I have an access to, but other options can be more straightforward!

Usually, KeePass applications can have two ways of accessing that data :

- they can support directly your specific cloud storage

- they can read from a local file which means you'll have to have another application in charge of syncing to your cloud

I went with the latter simply because my keepass applications didn't support OwnCloud! In any case, once I installed OwnCloud on mac and android and configured them I never had to open them again so it's not so bad.

 

Browser completion

I started looking at browser auto-completion recently and for now I'm using KeePass Tusk (available on Chrome and Firefox).

The big drawback I have with it is that the only way for it to access my database in owncloud is through a publicly available link there, which isn't great. I wish it could either read from the local file on my computer, or connect to owncloud directly.

In practice, it means I wouldn't need to expose the database through an URL. That database is password protected and good luck finding the link, but still, it's not ideal in my opinion!

 

Conclusion

Obviously this is my personal setup and it's not for everyone. Each of the tools I mentioned have some alternatives too! KeePass isn't the only player in the game (I heard pass is very good too).

However, I hope it proves that you can really store and access your passwords the way you want! You shouldn't use a tool you don't feel comfortable keeping your credentials.

 

 

 

]]>
https://www.detassigny.net/posts/3/managing-my-passwords-with-keepass-and-owncloud Mon, 03 Dec 2018 10:06:19 +0100 Gabriel de Tassigny