"If a worker wants to do his job well, he must first sharpen his tools." - Confucius, "The Analects of Confucius. Lu Linggong"
Front page > Programming > Focusing your tests on the domain. A PHPUnit example

Focusing your tests on the domain. A PHPUnit example

Published on 2024-11-03
Browse:492

Introduction

Many times developers try to test the 100% (or almost the 100%) of their code. Apparently, this is the aim every team should reach for their projects but, from my point of view, only one piece of the entire code should be fully tested: Your domain.

The domain is, basically, the part of your code which defines what the project actually does. For instance, when you persist an entity to the database, your domain is not in charge of persisting it on the database, but making sure that the persisted data makes sense according to your business model. Possibly, when you save your data on the database, you will use an external library like PHP Doctrine. This library is already fully tested, there is no need to test what it does. If you pass to doctrine the correct data, it will be saved to the database with no issues.

The example shown in the following sections does not try to show how the Domain Driven Design works, there are many articles which explain it very well. I will try to show how having your domain well defined and decoupled can help to test easily and focused on what your application does.

The example is built over a Symfony environment and using the PHPUnit library, but the idea is valid for any language or framework.

The code to test

Let’s imagine that our application connects to an external api which returns data about the rain probability for a specified date. The returned data looks like this:


{
   "date" : "2022-12-01",
   "rain_probability" : 0.75
}


Now, we have to take those data and classify it following this mapping:

  • rain_probability LOW
  • rain_probability ≥ 0.40 && rain_probability MEDIUM
  • rain_probability ≥ 0.75: HIGH

and save the result on a database table described by the following entity:


#[ORM\Entity(repositoryClass: RainMeasure::class)]
class RainMeasure {

    #[ORM\Column]
    private string $date;

    #[ORM\Column]
    private float $probability;

    #[ORM\Column(length: 10)]
    private string $label;

    // Getters and setters
    // .......
}


Let’s create a handler which gets the external api data, sets the label according to the rain probability and saves it to the database.


class RainMeassureHandler {
    private EntityManagerInterface $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function saveMeasure(array $measureData): void
    {
        if($measureData['rain_probability'] = 0.40 && $measureData['rain_probability'] setDate($measureData['date']);
        $rainMeasure->setProbability($measureData['rain_probability']);
        $rainMeasure->setLabel($label);

        $this->em->persist($rainMeasure);
        $this->em->flush();
    }
}


If we try to create a test for the above handler, we will find that we will need to inject the EntityManagerInterface since the behaviour we want to test (setting a label according to the probability value) is coupled in the same handler which saves the result to the database. We could try to load the EntityManagerInterface using mocks and stubs but, is it necessary ?. Obviously not. As said before, we should try to focus on testing the behaviour that belongs to our domain, which is getting the correct label according to the rain probability.

Decoupling behaviour we want to test

In order to simplify our test, we are going to move the behaviour we want to test to another class:


class RainMeasureLabelHandler 
{
    public function getLabelFromProbability(float $prob): string
    {
        if($prob = 0.40 && $prob 

And now, our RainMeassureHandler will look like this:


class RainMeasureHandler 
{
    private EntityManagerInterface $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function saveMeasure(array $measureData): void
    {
        $rainMeasureLabelHandler = new RainMeasureLabelHandler();
        $label = $rainMeasureLabelHandler->getLabelFromProbability($measureData['rain_probability']);

        $rainMeasure = new RainMeasure();
        $rainMeasure->setDate($measureData['date']);
        $rainMeasure->setProbability($measureData['rain_probability']);
        $rainMeasure->setLabel($label);

        $this->em->persist($rainMeasure);
        $this->em->flush();
    }
}


Now we can focus on test our RainMeasureLabelHandler which would be part of our domain and would have no dependencies to external layers. Testing it would be as easy as shown:

Focusing your tests on the domain. A PHPUnit example

Conclusion

I would like to say that other kind of tests would be useful too. Maybe we have an api and we want to test input and outputs with a test environment which includes database and other resources we could need. But, remember to have your domain decoupled and fully tested.

Release Statement This article is reproduced at: https://dev.to/icolomina/focusing-your-tests-on-the-domain-a-phpunit-example-c82?1 If there is any infringement, please contact [email protected] to delete it
Latest tutorial More>

Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.

Copyright© 2022 湘ICP备2022001581号-3