Codementor Events

How to write Magento 2 Unit Tests under a custom module

Published Aug 24, 2018
How to write Magento 2 Unit Tests under a custom module

In this post, I'm going to build a custom module that will explain the basics on how to create Unit Tests in Magento2 platform.

For this purpose, I created a really basic module, that doesn't add any tables on the database, just a Model with simple methods which we are going to test in order to learn a little bit more about PHPUnit and Magento 2.

What is Unit Testing?

Unit testing is a method for testing software that allows testing small testable chunks of code in order to determine if that component works correctly. In simpler words, unit testing checks whether the software application is working fine or a unit of code is causing trouble.

Why unit testing it's important?

One of the main reasons why you should use unit testing in your projects it's that by doing so you can find bugs at an early stage of development and fix them without making a major impact on other chunks of code of your project.

Since the bugs are found early in unit testing, it helps in reducing the cost and time of bug fixes. Bugs detected earlier are easier to fix, bugs detected in a much later development stage it's much more time consuming and harder to find the cause of the bug.

What tools include Magento 2 for Unit Testing?

Magento 2 comes pre-installed with PHPUnit by default which is a testing framework for unit testing in PHP. This post won't explain the basics of PHPUnit, we are covering just how to apply unit testing in Magento2 taking advantage of PHPUnit.

Creating a Custom Extension for Unit Testing

In this tutorial we are creating a super basic module that consists in creating a super basic model, this model has no database attached to it in order to keep it simple, just for testing purposes. In this case, we are using Pekebyte as a Vendor and UniTests as a Module Name but you are free to replace it as you want

Step 1.- Create module.xml in app/code/[Vendor]/[ModuleName]/etc

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
  <module name="Pekebyte_UnitTests" setup_version="1.0.0" />
</config>

Step 2.- Create registration.php in app/code/[Vendor]/[ModuleName]

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Pekebyte_UnitTests',
    __DIR__
);

Step 3.- Creating the Test Model

The Model that we are creating is just a basic Model with the ability of performing elementary mathematical operations, such as addition, subtraction, multiplication, and division. In this case, we put the file we created under the path: app/code/[Vendor]/[ModuleName]/Model/TestModel.php

<?php
namespace Pekebyte\UnitTests\Model;
 
class TestModel
{
    /**
     * this function returns the result of the addition of two numbers
     *
     * @param float $a
     * @param float $b
     * @return float
     */
    public function add($a, $b)
    {
        return $a + $b;
    }

    /**
     * this function returns the result of the subtraction of two numbers
     *
     * @param float $a
     * @param float $b
     * @return float
     */
    public function subtract($a, $b)
    {
        return $a - $b;
    }

    /**
     * this function returns the result of the multiplication of two numbers
     *
     * @param float $a
     * @param float $b
     * @return float
     */
    public function multiply($a, $b)
    {
        return $a * $b;
    }

    /**
     * this function returns the result of the division of two numbers
     *
     * @param float $a
     * @param float $b
     * @return float
     */
    public function divide($a, $b)
    {
        return $a / $b;
    }
}

Step 4.- Creating the Unit Test File for the Test Model

Given that we have created a basic Model, we proceed to write a Unit test for that model. In order to create it, it's important to clarify that we need to define a setUp method which will run before the tests run and also the tearDown method that runs after the tests have been run.

The model created has 4 methods, and we'll test all 4 operations, it's important that the tests methods begin with the test word. After this brief explanation, we proceed to create a Unit Test File under the path: app/code/[Vendor]/[ModuleName]/Test/Unit/Model/TestModel.php

<?php
namespace Pekebyte\UnitTests\Test\Unit\Model;
 
class TestModel extends \PHPUnit\Framework\TestCase
{
    protected $_objectManager;
    protected $_model;
    /**
     * This function is called before the test runs.
     * Ideal for setting the values to variables or objects.
     */
    protected function setUp()
    {
        $this->_objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
        $this->_model = $this->_objectManager->getObject("Pekebyte\UnitTests\Model\TestModel");
    }

    /**
     * This function is called after the test runs.
     * Ideal for setting the values to variables or objects.
     */
    public function tearDown()
    {
    }

    /**
     * this function tests the result of the addition of two numbers
     *
     */
    public function testAdd()
    {
        $result = $this->_model->add(5.0, 5.0);
        $expectedResult = 10.0;
        $this->assertEquals($expectedResult, $result);
    }

    /**
     * this function tests the result of the subtraction of two numbers
     *
     */
    public function testSubtract()
    {
        $result = $this->_model->subtract(5.0, 2.0);
        $expectedResult = 3.0;
        $this->assertEquals($expectedResult, $result);
    }

    /**
     * this function tests the result of the multiplication of two numbers
     *
     */
    public function testMultiply()
    {
        $result = $this->_model->multiply(5.0, 10.0);
        $expectedResult = 50.0;
        $this->assertEquals($expectedResult, $result);
    }

    /**
     * this function tests the result of the division of two numbers
     */
    public function testDivide()
    {
        $result = $this->_model->divide(6.0, 2.0);
        $expectedResult = 3.0;
        $this->assertEquals($expectedResult, $result);
    }
}

On the class created you can see that in the setUp function it's necessary to create the model in order to test it. The execution sequence of this test file should be:

setUp()
testAdd()
setUp()
testSubstract()
setUp()
testMultiply()
setUp()
testDivide()

Executing the test

If you want to execute all the unit tests from all the modules of Magento2 including yours, you can execute this command under the root of the magento2 installation:

php bin/magento dev:tests:run unit

Or If you want to execute the test only on a single file, you should execute this command under the root of the magento2 installation:

php ./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Pekebyte/UnitTests/Test/Unit/Model/TestModel.php

As a result, you can see something like this:

Result of executing Unit Test in Magento 2

Conclusion

In this tutorial, we learned how to do a really basic Magento 2 unit test. Unit Testing is complex, and it requires knowledge in PHPUnit, it is a lot to cover in just one tutorial. I suggest taking a look under the vendor/magento/ folder where you can look at all the core modules of Magento and see a lot of more complex examples of unit testing.

Testing it could save us time in the future, and when doing a custom development under Magento 2 it should be present in your custom modules. so If you are a newbie in unit testing, I encourage you also to read the PHPUnit documentation, before trying coding any test on Magento 2.

Bonus - some types of Assertion

assert($a)  Throws error on failed expectation object
assertEqual($a, $b) Throws error if $a == $b is false
assertTrue($a)  Throws error if $a is false
assertFalse($a) Throws error if $a is true
assertNull($a)  Throws error if $a has a value
assertNotNull($a)   Throws error if $a not set
assertIsA($a, $b)   Throws error if $a is not the class or type $b
assertNotA($a, $b)  Throws error if $a is of the class or type $b
assertNotEqual($a, $b)  Throws error if $a == $b is true

You can check the code of this tutorial here

Discover and read more posts from Pedro Molina
get started