当前位置: 首页 > 面试题库 >

如何在PHP中很难测试注册表模式或单例?

司空瑾瑜
2023-03-14
问题内容

为什么用请求驱动的PHP之类的语言很难测试单例或注册表模式?

除了可以实际执行程序之外,您还可以编写和运行测试,这样您就可以自由地影响程序的全局状态,并且可以对每个测试功能进行一些拆卸和初始化,以使每个测试功能都处于相同的状态。

我想念什么吗?


问题答案:

的确,
“您可以在实际程序执行之外编写和运行测试,这样您就可以自由地影响程序的全局状态,并对每个测试函数进行一些拆卸和初始化,以使每个测试函数都处于相同状态测试。”
,这样做很麻烦。您想单独测试TestSubject,而不要花时间重新创建工作环境。

class MyTestSubject
{
    protected $registry;

    public function __construct()
    {
        $this->registry = Registry::getInstance();
    }
    public function foo($id)
    {
        return $this->doSomethingWithResults(
            $registry->get('MyActiveRecord')->findById($id)
        );
    }
}

为了使这项工作奏效,您必须掌握具体情况Registry。它是硬编码的,是一个Singleton。后者意味着防止先前测试的任何副作用。必须为将在MyTestSubject上运行的每个测试重置它。您可以添加一个Registry::reset()方法并在中调用它setup(),但是添加仅用于测试的方法似乎很难。假设您仍然需要此方法,那么最终得到

public function setup()
{
    Registry::reset();
    $this->testSubject = new MyTestSubject;
}

现在,您仍然没有应该返回的“ MyActiveRecord”对象foo。因为您喜欢注册表,所以您的MyActiveRecord实际上看起来像这样

class MyActiveRecord
{
    protected $db;

    public function __construct()
    {
        $registry = Registry::getInstance();
        $this->db = $registry->get('db');
    }
    public function findById($id) { … }
}

MyActiveRecord的构造函数中还有另一个对Registry的调用。您必须确保测试包含某些内容,否则测试将失败。当然,我们的数据库类也是Singleton,需要在测试之间重置。h!

public function setup()
{
    Registry::reset();
    Db::reset();
    Registry::set('db', Db::getInstance('host', 'user', 'pass', 'db'));
    Registry::set('MyActiveRecord', new MyActiveRecord);
    $this->testSubject = new MyTestSubject;
}

因此,有了这些最终设置,您就可以进行测试

public function testFooDoesSomethingToQueryResults()
{
    $this->assertSame('expectedResult', $this->testSubject->findById(1));
}

并意识到您还有另一个依赖性:您的物理测试数据库尚未设置。当您建立测试数据库并填充数据时,您的老板来告诉您现在要进行SOA,所有这些数据库调用都必须替换为Web服务调用。

有一个新的类MyWebService,您必须使MyActiveRecord代替它。太好了,正是您所需要的。现在,您必须更改所有使用该数据库的测试。该死,你想。所有这些废话只是为了确保它能doSomethingWithResults按预期工作?MyTestSubject并不真正在乎数据来自何处。

介绍模拟

好消息是,您确实可以通过存根或模拟它们来替换所有依赖项。测试双将假装成真。

$mock = $this->getMock('MyWebservice');
$mock->expects($this->once())
     ->method('findById')
     ->with($this->equalTo(1))
     ->will($this->returnValue('Expected Unprocessed Data'));

这将创建一个双为Web服务,它 希望 被称为 一次 试验期间 第一个参数 的方法 findById是1.
返回预定义的数据。

将其放入TestCase中的方法后,您setup将成为

public function setup()
{
    Registry::reset();
    Registry::set('MyWebservice', $this->getWebserviceMock());
    $this->testSubject = new MyTestSubject;
}

大。您现在不必再为设置实际环境而烦恼了。好吧,除了注册表。怎么样呢?但是该怎么做。它经过硬编码,因此无法在测试运行时替换。废话!

但是,请稍等,我们是否只是说MyTestClass不在乎数据来自何处?是的,它只是在乎它可以调用该findById方法。您希望现在想一想:为什么注册表在那里存在?是的,你是。让我们将整个事情更改为

class MyTestSubject
{
    protected $finder;

    public function __construct(Finder $finder)
    {
        $this->finder = $finder;
    }
    public function foo($id)
    {
        return $this->doSomethingWithResults(
            $this->finder->findById($id)
        );
    }
}

Byebye注册表。我们现在正在注入依赖项MyWebSe…err…Finder ?!是的 我们只关心方法findById,所以我们现在使用接口

interface Finder
{
    public function findById($id);
}

别忘了相应地更改模拟

$mock = $this->getMock('Finder');
$mock->expects($this->once())
     ->method('findById')
     ->with($this->equalTo(1))
     ->will($this->returnValue('Expected Unprocessed Data'));

和setup()变成

public function setup()
{
    $this->testSubject = new MyTestSubject($this->getFinderMock());
}

瞧!尼斯和容易和。我们现在可以集中精力测试MyTestClass。

在执行此操作时,您的老板再次打来电话,说他希望您切换回数据库,因为SOA实际上只是价格过高的顾问所使用的流行语,使您感到企业精神。但这一次您不必担心,因为您不必再​​次更改测试。它们不再依赖于环境。

当然,您仍然必须确保MyWebservice和MyActiveRecord都为您的实际代码实现了Finder接口,但是由于我们假定它们已经具有这些方法,因此只需implements Finder在类上打耳光即可。

就是这样。希望能有所帮助。

其他资源:

您可以在测试Singleton和处理全局状态时找到有关其他缺点的其他信息。

这应该是最令人感兴趣的,因为它是PHPUnit的作者,并解释了PHPUnit中实际示例的困难。



 类似资料:
  • 我正在与Avro/Kafka和Confluent的Avro模式注册中心合作。 我使用avsc文件和avdl制作了一些基本模式和具有基本类型的主题。 我正在查看Confluent制作的API文档,以尝试将模式发展到版本2。特别是本部分: https://docs.confluent.io/current/schema-registry/using.html#register-a-new-versio

  • 我有一个SpringBootTest,需要通过ApplicationEnvironmentPreparedEvent通知我创建一个不存在的数据库文件,因为我的应用程序数据库试图连接到它,但它不存在。 我是通过SpringApplicationBuilder实现的,但是在JUnit中,我没有访问这个生成器。这是我当前的主代码: 当准备就绪时,如何通知我在数据源配置之前读取JDBC URL并为测试创建

  • 我正试图使用SpringKafka为我的生产者应用程序及其嵌入式Kafka服务器编写测试。 然而,我的应用程序也使用合流模式注册表,我想知道SpringKafka是否为模式注册表提供了一些嵌入式服务器? 或者有没有更好的方法来使用模式注册表进行Spring Kafka测试?

  • 我现在一直在查看Spring Cloud模式注册表和汇合模式注册表。我可以看到一些区别,例如Spring Cloud模式注册表将模式保存在普通数据库中,默认情况下保存在h2中,而汇合模式注册表保存在kafka主题中。 spring云模式注册表的这种方法是否会对性能产生任何影响。据我所知,即使数据保留在主题上,以防汇合,查询它时仍然会有延迟。但会有重大影响吗? 我还可以看到,spring云模式注册表

  • 我制作了一个简单的注册表单,并将其连接到数据库。如果我是第一次注册,它允许我登录并进入主页。然而,当我注销并尝试再次登录时,它总是显示“错误的密码或电子邮件”,即使我把一切都正确。尝试重置密码,密码成功重置,但当我尝试登录时,它只是再次显示相同的错误。 下面是im使用的注册php代码 } 这是登录php脚本im使用

  • 我正在了解Confluent的模式注册表,以满足所有模式管理需求。 我不太理解他们的版本控制方法...有一个的概念,我将其视为一个名称空间。据我所知,subject在模式注册表中必须是唯一。 然后是模式id,或者只是,它也是唯一的。 最后,还有一个。 以下是文档中的片段: :此主题的架构版本,每个主题从1开始 :全局唯一的架构版本id,在所有主题中的所有架构中都是唯一的 因此,一旦我想修改特定主题