如何提供模拟实体管理器来测试服务类?

问题描述 投票:0回答:2

我的Symfony(3.4)应用程序中有一个服务类(OutletScraper)。该类使用实体管理器和Bazinga Geocoder捆绑包提供的地理编码服务。我已经成功配置了两个,以便我可以在我的服务类中调用它们。每当我需要服务时,我都会从容器中调用它,因此实体管理器和地理编码器包已经注入其中。

测试时,我知道我可以模拟实体管理器,然后将其提供给我的测试类。当我从容器访问服务类时,如何覆盖传递给构造函数的内容?即,以便我可以提供模拟实体管理器,而不是注入真实的实体管理器。我试图手动实例化服务类的对象:

$outletScraper = new OutletScraper(new Provider(), $this->createMock(EntityManagerInterface::class));

但是,这样做时会出现以下错误:

Error: Cannot instantiate interface Geocoder\Provider\Provider

如何正确实例化此类?我是否需要从服务容器(它设置为私有)调用它?感谢任何帮助。

php symfony service phpunit containers
2个回答
1
投票

您对实体管理器的模拟工作正常,问题是地理编码器提供商。就像EntityManagerInterface一样,Geocoder\Provider\Provider也是一个界面。图书馆维护者只是选择省略后缀。

这意味着您不能只创建它,而是必须传递实现接口的具体类,如Geocoder\Provider\GoogleMaps\GoogleMaps,如果您确实想要进行地理编码调用或模拟提供程序。

如果要检查配置的地理编码提供程序是否有效,可以使用Symfony的WebTestCase编写功能测试,大致如下所示:

<?php

namespace AppBundle\Tests\Scraper;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class OutletScraperTest extends WebTestCase
{
    public function testFindsLocation()
    {
        // This will instantiate your Symfony application in the test environment
        $client = static::createClient();

        $container = $client->getContainer();
        // Replace the class name with the service id, if you use artificial service ids like "app.outlet_scraper"
        $scraper = $container->get(OutletScraper::class);

        // Call whatever method you want to test on your outlet scraper
        $result = $scraper->someMethod();

        // Assert result matches expectations
        $this->assertEquals(..., $result);
    }
}

但要小心,这将使用您为应用程序配置的OutletScraper,并具有两个实际的依赖关系。因此,您的地理编码提供程序将对您使用的任何提供程序进行实际调用,这可能会占用请求,这可能是有限的。您还将使用真正的Doctrine EntityManager,这意味着您写入数据库的任何内容都将被实际写入。特别是对于数据库,您应该创建一个单独的测试数据库并在app/config/config_test.yaml中进行配置。


0
投票

我的建议是,如果你试图单独测试这项服务,你也应该嘲笑Geocoder\Provider\Provider。否则,您的测试断言可能会产生不同的结果,具体取决于您运行它们的时间。

如果你要使用这个模拟器,你甚至可以创建自己的测试类来实现该接口,这样你就可以在测试环境中自己准备不同的输出。

© www.soinside.com 2019 - 2024. All rights reserved.