当前位置: 首页 > 知识库问答 >
问题:

集成测试模拟外观与注入模拟

胡昊
2023-03-14

我们有一些传统的laravel项目,它们在类中使用正面。

use Cache;

LegacyClass
{
    public function cacheFunctionOne()
    {
         $result = Cache::someFunction('parameter');

         // logic to manipulate result

         return $result;
    }

    public function cacheFunctionTwo()
    {
         $result = Cache::someFunction('parameter');

         // different logic to manipulate result

         return $result;
    }
}

我们最近的项目使用了底层laravel类的依赖注入,facades所代表的类正如Taylor Otwell自己所暗示的那样。(我们对每个类使用构造函数注入,但为了保持示例简短,这里我使用方法注入并使用单个类。)

use Illuminate\Cache\Repository as Cache;

ModernClass
{
    public function cacheFunctionOne(Cache $cache)
    {
         $result = $cache->someFunction('parameter');

         // logic to manipulate result

         return $result;
    }

    public function cacheFunctionTwo(Cache $cache)
    {
         $result = $cache->someFunction('parameter');

         // different logic to manipulate result

         return $result;
    }
}

我知道外表是可以被嘲笑的

public function testExample()
{
    Cache::shouldReceive('get')
                ->once()
                ->with('key')
                ->andReturn('value');

    $this->visit('/users')->see('value');
}

这对单元测试很有效。我试图理解的问题是,这些门面是否被“全球”嘲笑。

例如,让我们假设我正在编写一个集成测试(在模拟服务的同时测试几个相互连接的类,而不是使用活动服务的端到端测试),该测试在某个点上执行两个独立的类,它们包含相同的外观,使用相同的参数调用相同的方法。

在这些被调用的类之间,是一些复杂的功能,它改变了使用相同参数的facades方法返回的数据。**

$modernClass->cacheFunctionOne($cache); // easily mocked

// logic that changes data returned by laravel Cache object function 'someFunction'

$modernClass->cacheFunctionTwo($cache); // easily mocked with a different mock

我们的现代类很容易测试,因为facade表示的底层类被注入到每个类中(在本例中,每个方法)。这意味着我可以创建两个单独的mock,并将它们注入每个类(方法)以模拟不同的结果。

$legacyClass->cacheFunctionOne();

// logic that changes data returned by laravel Cache object function 'someFunction'

$legacyClass->cacheFunctionTwo();

但在遗留系统中,模拟的facade似乎是“全局的”,因此当facade在每个类中运行时,返回的值完全相同。

我这样想对吗?

*我知道从代码架构和测试的角度来看,这个例子可能看起来完全多余,但是我剥离了所有真正的功能,试图给出一些我所问的“简单”例子。

共有1个答案

濮阳烨然
2023-03-14

依赖注入vs Facades

依赖项注入的主要好处之一是,一旦开始将依赖项注入到方法中,而不是在方法中实例化/硬编码依赖项,代码就变得更易于测试。这是因为您可以从内部单元测试传入依赖项,它们将通过代码传播。

见:http://slashnode.com/dependency-injection/

依赖注入与门面形成鲜明对比。门面是静态全局类,PHP语言不允许覆盖或替换静态类上的静态函数。Laravel立面使用Mockery来提供模拟功能,它们受到上述相同事实的限制。

集成测试的问题可能出现在您希望从非模拟缓存检索数据的地方,但一旦您使用Facade::shouldReceive(),那么Facade::get()将被模拟缓存覆盖。反之亦然。因此,当您交错调用模拟和未模拟的数据时,Facades是不合适的。

为了使用所需的不同数据集测试代码,最佳实践是重构遗留代码以使用DI。

集成测试

简易方法

另一种选择是在集成测试开始时调用多个Facade::应该接收()。确保您在集成测试中对每个调用都有正确的期望数量和正确的顺序。鉴于您现有的代码库,这可能是编写测试的更快方法。

更难的方法

而依赖注入是编程的最佳实践。很可能是您的代码库有太多遗留类,以至于重构需要花费难以置信的时间。在这种情况下,考虑使用带有fixture的测试数据库进行端到端集成测试可能是值得的。

附录:

  • 有关Facade如何调用Mockry,请参见-function createMockByName():https://github.com/laravel/framework/blob/5.3/src/Illuminate/Support/Facades/Facade.php
 类似资料:
  • 如何模拟集成测试所需的许多依赖关系? 我使用Mockito进行纯单元测试。在这种情况下,Pure意味着测试一个类,嘲笑它的所有依赖关系。漂亮。 现在是集成测试。假设在这种情况下,集成测试将测试以下内容: 消息被放入队列 我们也可以说,在第2步中发生的处理是严肃的事情。它依赖于大量的数据库交互、多种外部服务、文件系统,以及各种各样的东西。流还会引发很多副作用,所以我不能简单地确保响应是正确的——我需

  • 我正在尝试为我的Spring Boot应用程序创建集成测试。其想法是启动一个嵌入式postgres db,并使用TestRestTemplate对我的控制器运行http调用。 问题是我的项目有一个我们用于redis队列的依赖项。 我曾尝试模拟依赖项,其中大多数都能正常工作,但我猜这一次它会抱怨,因为它是而不是: 依赖配置类: 我的测试配置类 我已经在我的config类中尝试了,但是原始的在启动之前

  • 问题内容: 每当我想测试使用资源注入的类时,我最终都会包含一个仅在测试中使用的构造函数: 还有另一种模拟资源注入的方式,或者这是遵循的正确模式吗? 问题答案: 您可以使用简单的光泽效果,它可以模拟EJB注入系统。 另一种方法是在测试中使用反射来设置字段,我有时使用类似这样的内容:

  • 我发现可以使用以下方法模拟和: 它工作得很好,但当我尝试运行集成测试时,授权服务器仍然需要启动并运行。否则,Quarkus无法连接到它。 我试图禁用OIDC扩展(),但是代码当然不能编译(endpoint不能识别依赖项)。 那么,在运行集成测试时,哪一种方法是跳过OIDC连接的最佳方法呢? 最好的,

  • 我试图使用构造函数注入依赖模式。 我想知道在集成测试类上注入JPA存储库的正确方法是什么: RepoClass 注射后的服务 测试它:(我的问题来了) SpringRunner类选项1:构造函数注入 由于控制台输出显示: 测试类应该只有一个公共零参数构造函数 SpringRunner级选项2:自动注射 我觉得它正在打破人们想要的模式。 SpringRunner类选项3:空构造函数 正如所评论的:显

  • 已删除MyTestConfig.class,但问题仍然相同。即使我使用@SpringBootTest(classes={Application.Class,MyProblematicServiceImpl.Class}),它仍然在自动连线的地方返回模拟对象。MyProblematicServiceImpl是用@Service注释的空类。