Wie erhalte ich ScopeConfigInterface über den Objektmanager von Komponententests in Magento 2?

8

Ich versuche, eine Zeile in meinem Komponententest aus core_config_table in der Magento 2-Datenbank zu lesen. Ich weiß das, um diesen Job zu erledigen, da ich diesen Link gelesen habe . Ich muss benutzen:

\Magento\Framework\App\Config\ScopeConfigInterface

durch:

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

Hier ist mein Code:

    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);
    }
}

Ich kann jedes gewünschte Objekt mit testObject abrufen, \Magento\Framework\TestFramework\Unit\Helper\ObjectManageraber wann immer ich möchte\Magento\Framework\App\Config\ScopeConfigInterface

Schwerwiegender Fehler: Die Schnittstelle Magento \ Framework \ App \ Config \ ScopeConf igInterface in C: \ xampp \ htdocs \ magento \ vendor \ magento \ framework \ TestFramework \ Un it \ Helper \ ObjectManager.php in Zeile 162 kann nicht instanziiert werden

ali gh
quelle
das gleiche Problem hier ....
Michel Gokan

Antworten:

12

Ich kann mich hier irren, aber ich denke, für Unit-Tests müssen Sie keine Werte aus der Datenbank abrufen. Sie können davon ausgehen, dass die Implementierungen von \Magento\Framework\App\Config\ScopeConfigInterfacegetestet wurden und ordnungsgemäß funktionieren. Sie müssen nur Ihre Methode testen, die getValuevon der verwendet ScopeConfigInterface.
Wenn Sie beispielsweise eine Methode wie diese haben:

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

Sie müssen diese Methode nur testen und nicht, wenn der Wert aus der Datenbank Ihren Anforderungen entspricht.
und Sie können das so testen:

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());
}

Abhängig von der Anzahl der Abhängigkeiten, die in den Konstruktor eingefügt werden müssen, müssen Sie möglicherweise nicht einmal den Unit-Test ObjectManager verwenden, sondern können die zu testende Klasse einfach direkt mit instanziieren new.

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

Dies ist einfacher und daher für Unit-Tests vorzuziehen. Der einzige Grund für die Verwendung des Unit-Test-Objekt-Managers besteht darin, dass eine große Anzahl von Abhängigkeiten das manuelle Verspotten jedes einzelnen zu umständlich macht.

Marius
quelle
Danke für deine tolle Antwort. Stellen Sie sich vor, ich möchte einen Test schreiben, dessen Zweck darin besteht, dass wenn eine Kernkonfiguration "True" ist, die Daten einiger Produkte durch X ersetzt werden und wenn die Daten "False" sind, die Daten einiger Produkte durch Y ersetzt werden sollen. Wenn ich a schreiben muss Mock, um diese Funktionalität in meinem Modul zu testen. Was ist dann der Sinn des Unit-Tests? Ich möchte mein tatsächliches und reales Modul testen, nicht "ein Mock" seiner Funktionalität.
Ali Gh
In diesem Fall machen Sie 2 Tests. eine für den getValueFall, dass die Methode true zurückgibt, ->willReturn(true)und eine für den Fall, dass getValuefalse zurückgegeben wird. ->willReturn(false). Auf diese Weise testen Sie in beiden Fällen Ihr eigentliches Modul, unabhängig davon, was Sie in Ihrer Datenbank haben.
Marius
1
@Marius ist korrekt, wenn Sie einen Komponententest schreiben, sollten Sie nicht direkt mit der Datenbank sprechen, sondern sich über die scopeConfigInterface lustig machen und davon ausgehen, dass der Datenbankkonfigurationsstatus festgelegt ist, wenn Sie Daten abrufen möchten Die Datenbank, mit der Sie beginnen, beginnt mit Integrationstests, in denen Sie die eigentliche Datenbank aufrufen können, um Daten abzurufen und Zusicherungen dafür durchzuführen.
James Cowie
@Marius Ich habe getan, was Sie erwähnt haben, aber wenn ich behaupte, werde ich immer wahr werden, auch wenn $ dbValue nicht den tatsächlichen Wert in der Datenbank hat
ali gh
@aligh. Das war der Punkt. Lesen Sie den obigen Kommentar von James Cowie. Er ist weit mehr eine Autorität in Unit-Tests (und allen Arten von Tests) als ich oder werde es jemals sein.
Marius
1

Ich denke, Sie müssen dafür Mock verwenden, aber in Ihrem Fall ist eine Umgestaltung Ihres Moduls erforderlich, insbesondere die Notwendigkeit einer ConfigKlasse, die sich auf Ihr Modul bezieht.

Sie können Ihre Entwicklung auf das stützen, app/code/Magento/Braintree/Test/Unit/Gateway/Config/ConfigTest.phpwas so etwas implementiert:

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 */
}
Raphael beim digitalen Pianismus
quelle
Welche Rolle spielt die Funktion willReturn in der Methode 'testGetCountrySpecificCardTypeConfig'?
Ali