【问题标题】:How to use PHPUnit to mock and test a function which instantiates an object?如何使用 PHPUnit 模拟和测试实例化对象的函数?
【发布时间】:2018-03-10 22:47:52
【问题描述】:

我正在尝试编写一个函数,其目的是测试数据库连接。它的作用是实例化一个Database 对象,尝试连接到数据库并返回结果。

伪代码如下所示

<?php

function testDatabaseLogin() {
    // Get the database connection information from a file
    // ...

    // Database is a class which stores the connection information passed,
    // When its connect() method is invoked, it tries to connect. nothing fancy
    $conn = new Database($host, $port, $username, $password);
    return $conn->connect(); // TRUE on success, FALSE otherwise
}

?>

这段代码 sn-p 是我的情况的简化版本,但应该足以说明我的问题。

我想知道如何使用 PHPUnit 模拟此示例中的 Database 类。我想要的是我可以

  1. 断言从此函数实例化Database时的参数正确
  2. 存根Database 控制connect() 返回TRUEFALSE,这样我就不需要实际尝试连接到真实数据库了

我已经四处搜索,但我发现 PHPUnit Mock 对象似乎不能以这种方式工作。任何帮助表示赞赏。归根结底,如果有助于使测试更容易,更改函数实现是可以接受的。

谢谢。

【问题讨论】:

  • 不确定这是否有帮助,但我问了一个涉及模拟 PDO 连接的问题 (stackoverflow.com/questions/46379109/…)。您可能只需要验证参数以获得合理的值,因为没有连接您无法检查它们是否真的有效。
  • @NigelRen 感谢您的帮助。我认为我的情况与您的情况不同,因为您的 DBAccess 类接受 PDO 对象。因此,您可以使用 PHPUnit 模拟 PDO 对象并传递它。但在我的情况下,Database 对象是在函数内部实例化的,这意味着即使我有一个模拟对象,我也无法将它传递到内部。所以我在想如果 PHPUnit 不能处理这种情况,我应该尝试重构函数以使其可测试。
  • 我认为普遍的共识是能够注入任何依赖项以启用测试等,stackoverflow.com/questions/130794/what-is-dependency-injection 提供了更多细节。

标签: php phpunit


【解决方案1】:

我有时会使用一个技巧

<?php   
class MyClass
{
   public function databaseLogin() {
        $conn = $this->createDatabase();
           return $conn->connect(); // TRUE on success, FALSE otherwise
        }

   protected function createDatabase() {
        new Database($host, $port, $username, $password);
   }
} 

然后,创建一个“待测类”来扩展您的类:

class MyClassUnderTest extends MyClass
{
   public function __construct($databaseMock)
   {
       $this->databaseMock = $databaseMock;
       parent::__construct();
   }
}

然后,在测试中:

class MySuperTest
{
    public function testSomethingWitDatabase()
    {
        $databaseMock = $this->getMockBuilder(DataBase::class)
            ->disableOriginalConstructor()
            ->getMock();

        $classUnderTest = new MyClassUnderTest($databaseMock);
        $this->assertTrue($classunderTest->databaseLogin());
    }
}

因此,您将数据库对象实例化为一个方法,该方法被刚刚为测试而创建的类所覆盖。

【讨论】:

  • 总之还是用依赖注入来获取更多可测试的代码吧……
猜你喜欢
  • 2019-08-04
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
相关资源
最近更新 更多