2

I have a class utilizing the Magento 2 AttributeRepository. Can I write a unit test for my class which gets the attribute based on the attribute code?

namespace Company\Catalog\Test\Unit\Model;


use Company\Catalog\Helper\LandingPage;
use Company\Catalog\Model\Brand;
use Magento\Eav\Model\AttributeRepository;
use Magento\Store\Model\StoreManagerInterface;

class BrandTest extends \PHPUnit_Framework_TestCase
{
    /** @var  Brand */
    private $brand;

    /** @var  AttributeRepository */
    private $attributeRepository;

    /** @var  StoreManagerInterface */
    private $storeManager;

    /** @var  LandingPage */
    private $landingPageHelper;

    protected function setUp()
    {
        $this->landingPageHelper = $this->getMockBuilder(LandingPage::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->attributeRepository  = $this->getMockBuilder(AttributeRepository::class)
            ->disableOriginalConstructor()
            ->getMock();
        $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->landingPageHelper
            ->method('getBrandAttributeCode')
            ->willReturn('brands');

        $this->brand = new Brand($this->storeManager, $this->attributeRepository, $this->landingPageHelper);
    }

    public function testGetBrandAttributeCode()
    {
        $expected = 'brands';
        $this->assertEquals($expected, $this->brand->getBrandAttributeCode());
    }

    public function testGetBrandAttribute()
    {
        $expected = 'brands';
        $attribute = $this->brand->getBrandAttribute($expected);
        echo $attribute->getAttributeCode();
        $this->expectOutputString($expected);
    }
}

Should I even prepare a mock data for repository or can I get the actual value from the database using the AttributeRepository?

1

Should I even prepare a mock data for repository or can I get the actual value from the database using the AttributeRepository?

This is the decision between unit tests and integration tests.

  • Integration tests test the real interaction with Magento and will hit the database. These tests are valuable to prove that your code (still) works together with the framework and other components and to find regressions.

  • Unit tests test your code in isolation and should not use the real repository. These tests are valuable in test driven development to guide your design, they can quickly test your business logic with all edge cases.

For unit tests of a class that uses core repository you will end up with a complex mock setup. It's possible and I've done it for attribute repositories, but a better approach is to get rid of the complex dependency and leave it in a thin wrapper (which only needs integration tests)

I've written about the topic here: What to mock in a Magento 2 unit test

“Only mock types you own” [...] should not be taken as a strict rule, otherwise you end up with the other extreme, having wrappers over wrappers for everything. But it can be extremely helpful to simplify the API and tailor it to your needs.

Simplify complicated API

Here is a possible wrapper for the product repository, if you need to find products by manufacturer:

interface ProductRepository
{
    /**
     * @api
     * @return ProductInterface[]
     */
    public function productsByManufacturer($manufacturer);
}

There, no search criteria and no search result. It is easier to mock and also easier to use. The calling code is only interested in manufacturer and products, we leave the framework details in the implementation of our wrapper.

You might ask, how to test this wrapper. The answer is, it should be tested via integration tests of the whole feature, without mocks. A unit test does not make sense for it.

If your Brand class does not more than retrieving a brand attribute, you already have your wrapper. Don't waste time with unit tests for this and write integration tests instead. In other classes that use Brand, you can then easily mock Brand and do not need to care about the attribute repository.

  • I totally forgot about the integration tests. So you mean to say we should be using unit tests to only test simple and straight forward methods (without involving real repositories) and if there is any actual data involved it's always best to have the integration test for it. – VK Ranjith Feb 18 '17 at 6:07
  • Unit tests involve data too, but it's not coming from the database. But if that's what you meant, yes – Fabian Schmengler Feb 18 '17 at 7:18
  • Still finding hard time to decide which code can be tested using unit test and where to use integration :) This is the first time getting my hands dirty with unit testing and Magento's DI concept is making a bit more difficult. – VK Ranjith Feb 19 '17 at 5:46
  • In this case, let me recommend you to not write unit tests yet at all,start with integration tests only and if you can, train TDD with unit tests in small projects that do not involve a framework like Magento, until you get used to it. Learning it all at once will be a frustrating experience. – Fabian Schmengler Feb 19 '17 at 9:26
  • A possible way to get started with unit tests: schmengler-se.de/en/2017/01/tdd-kata-01-the-bowling-game – Fabian Schmengler Feb 19 '17 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.