Как получить ScopeConfigInterface через диспетчер объектов модульных тестов в magento 2?

8

Я пытаюсь прочитать строку в моем модульном тесте из core_config_table в базе данных magento 2. Я знаю, что для выполнения этой работы, как я прочитал эту ссылку . Я должен использовать:

\Magento\Framework\App\Config\ScopeConfigInterface

через:

\Magento\Framework\TestFramework\Unit\Helper\ObjectManager

Вот мой код:

    protected function setUp()
{
    $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
    $this->scopeConfig = $objectManager->getObject('\Magento\Framework\App\Config\ScopeConfigInterface');
}

public function testgetImageCDNConfigValue()
{
    $this->scopeConfig->getValue($this->path, \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
    if ($this->scopeConfig == null) {
        $this->assertFalse(true);
    } else {
        $this->assertTrue(true);
    }
}

Я могу получить каждый объект, который хочу, используя testObject, \Magento\Framework\TestFramework\Unit\Helper\ObjectManagerно всякий раз, когда я хочу получить\Magento\Framework\App\Config\ScopeConfigInterface

Неустранимая ошибка: невозможно создать экземпляр интерфейса Magento \ Framework \ App \ Config \ ScopeConf igInterface в C: \ xampp \ htdocs \ magento \ vendor \ magento \ framework \ TestFramework \ Un it \ Helper \ ObjectManager.php в строке 162

али гх
источник
та же проблема здесь ....
Мишель Гокан

Ответы:

12

Я могу ошибаться, но я думаю, что для модульных тестов вам не нужно извлекать значения из базы данных. Вы можете предположить, что реализации \Magento\Framework\App\Config\ScopeConfigInterfaceпроверены и работают должным образом. Вам нужно только проверить свой метод, который использует getValueиз ScopeConfigInterface.
Например, если у вас есть такой метод:

public function getSomeConfigValue()
{
    return $this->scopeConfig->getValue('some/path/here', ScopeInterface::SCOPE_STORE)
}

вам нужно протестировать только этот метод, а не, если вам нужно значение из БД.
и вы можете проверить это следующим образом:

public function testGetSomeConfigValue()
{
    $dbValue = 'dummy_value_here';
    $scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class)
            ->disableOriginalConstructor()
            ->getMock();
    $scopeConfigMock->method('getValue')
            ->willReturn($dbValue);
    $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
    $myClass = $objectManager->getObject(
        \Your\Class\Name\Here::class,
        [
             'scopeConfig' => $scopeConfigMock,
             ..., //your other mocked dependencies here
        ]
    );

    $this->assertEquals($dbValue, $myClass->getSomeConfigValue());
}

В зависимости от количества зависимостей, которые должны быть внедрены в конструктор, вам может даже не понадобиться использовать модульный тест ObjectManager, но вы можете просто создать экземпляр тестируемого класса напрямую, используя new.

$myClass = new \Your\Class\Name\Here($scopeConfigMock);

Это проще и предпочтительнее для модульных тестов. Единственная причина использовать диспетчер объектов модульного теста - если большое количество зависимостей делает манипулирование каждой из них слишком громоздким.

Мариус
источник
спасибо за отличный ответ Предположим, я хочу написать тест, цель которого заключается в том, чтобы при конфигурации ядра «True» данные какого-либо продукта заменялись на X, а когда «False» - данные некоторых продуктов заменялись на Y. Если мне нужно написать макет для проверки этой функциональности в моем модуле, тогда какой смысл юнит-тестирование? Я хочу проверить мой реальный и реальный модуль, а не «макет» его функциональности.
Али GH
в этом случае вы делаете 2 теста. один для того, когда метод getValueвозвращает истину, ->willReturn(true)и один для когда getValueвозвращает ложь. ->willReturn(false), Таким образом, вы проверяете свой фактический модуль в обоих случаях, независимо от того, что у вас есть в вашей базе данных.
Мариус
1
@Marius является правильным , если вы пишете модульное тестирование , то вы должны не говорить с базой данных напрямую , но вместо этого вы должны быть насмешливыми на scopeConfigInterface и предположите , что состояние конфигурации базы данных устанавливаются, когда вы начинаете хотеть получить данные из базу данных, которую вы начинаете перемещать в интеграционные тесты, где вы можете затем вызывать реальную базу данных для получения данных и выполнения утверждений на них.
Джеймс Коуи
@Marus Я сделал то, что вы упомянули, но когда я утверждаю, я всегда получу истину, даже если $ dbValue не имеет фактического значения в базе данных
ali gh
@aligh. Это было главное. Прочитайте комментарий выше от Джеймса Коуи. Он гораздо больше авторитет в юнит-тестировании (и во всех видах тестирования), чем я или буду.
Мариус
1

Я считаю, что вам нужно использовать mock для этого, но в вашем случае это потребует некоторого рефакторинга вашего модуля, особенно необходим Configкласс, связанный с вашим модулем.

Вы можете основывать свою разработку на том, app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.phpчто реализует что-то вроде этого:

namespace Magento\Braintree\Test\Unit\Gateway\Config;

use Magento\Braintree\Gateway\Config\Config;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

/**
 * Class ConfigTest
 */
class ConfigTest extends \PHPUnit_Framework_TestCase
{
    const METHOD_CODE = 'braintree';

    /**
     * @var Config
     */
    private $model;

    /**
     * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
     */
    private $scopeConfigMock;

    protected function setUp()
    {
        $this->scopeConfigMock = $this->getMock(ScopeConfigInterface::class);

        $this->model = new Config($this->scopeConfigMock, self::METHOD_CODE);
    }

    /**
     * @param string $value
     * @param array $expected
     * @dataProvider getCountrySpecificCardTypeConfigDataProvider
     */
    public function testGetCountrySpecificCardTypeConfig($value, $expected)
    {
        $this->scopeConfigMock->expects(static::once())
            ->method('getValue')
            ->with($this->getPath(Config::KEY_COUNTRY_CREDIT_CARD), ScopeInterface::SCOPE_STORE, null)
            ->willReturn($value);

        static::assertEquals(
            $expected,
            $this->model->getCountrySpecificCardTypeConfig()
        );
    }

    /* skipped code */
}
Рафаэль в цифровом пианизме
источник
Какова роль функции willReturn в методе testGetCountrySpecificCardTypeConfig?
Али GH