PHPUnit:如何测试调用在不同文件中声明的另一个函数的方法

时间:2017-11-22 13:53:03

标签: php phpunit

我试图使用PHPUnit测试一个方法,它调用另一个函数(独立函数,没有类),它驻留在不同的文件中,它做了一些非常好的计算并返回一个对象。

这是我的实际主要代码:

class CreateRecords
{
    public function createEntities($details)
    {
        if (trim($details['username']) == "") {
            $this->result = "Username is empty.";
        } else {
            $this->result = create_record($Details['username']);
        }       
        return $this->result;
    }
}

这个create_record函数,(独立函数,没有类),它是核心函数,驻留在单独的文件中,它做了很好的计算(调用了很多其他方法/ functions )并返回对象,无论它是否成功。

我可以模拟createEntities方法,但我想模拟create_record函数,它执行所有计算并返回结果。

我见过很少有类似情况的帖子,

phpunit testing method that calls other class methods which need mock

PHPUnit mock method used in another class

但我无法理解,如何模拟在某个不同文件中声明的独立函数。

3 个答案:

答案 0 :(得分:1)

您可以创建将从外部函数返回结果的新方法。 然后你可以模拟这个新方法

class CreateRecords
{
    public function createEntities($details)
    {
        if (trim($details['username']) == "") {
            $this->result = "Username is empty.";
        } else {
            $this->result = $this->createRecord($Details['username']);
        }       
        return $this->result;
    }

    public function createRecord($username){
    return create_record($username);
    }
}

答案 1 :(得分:0)

PHP5.3 +中的命名空间为此提供了一个很好的解决方案,它允许您覆盖当前命名空间中的内置函数。

从长远来看,将全局create_record()重构为一个可以注入CreateRecords类,然后进行模拟的类将是一个很好的方法。在这个例子中,全局函数只是一个简单的包装器来调用类,直到更新整个应用程序。

重新创建create_record($username)虽然并不难,但对测试很有用。同样的技术也可用于覆盖全局time()函数。

在测试文件中,添加一个新的(但是本地的)命名空间:

<?php
namespace Test\DatabaseAccess;

use DatabaseAccess\CreateRecord;
use PHPUnit\Framework\TestCase;

namespace DatabaseAccess {
    function create_record($username)
    {
        // pretend to do something
    }
}

class CreateRecordTest extends TestCase
{
     // test to check CreateRecord class
     // which should call the `create_record`, above
}

这与SymfonyBridge系统用于创建ClockMock的技术相同 - 它动态地将time()sleep()等添加到您进行单元测试的命名空间中(在此示例中) ,命名空间DatabaseAccess\CreateRecord,而不是Test \ prefixed命名空间)。 ClockMock(和DnsMock)通过eval()调用来完成它,但由于您明确知道了命名空间,因此为了清楚起见,您可以将其写入测试文件本身。

答案 2 :(得分:0)

在阅读上述(相当不错的)答案后,您的评论说您无法触及测试类 - CreateRecords, 我可以建议另一个不理想的解决方案,但应该完成工作:

创建一个继承\ {extends CreateRecords - CreateRecordsExtended

的新类

扩展类应仅覆盖问题createEntities($details)中的测试函数。所以创建一个新的并从原始函数中复制代码。

另外,创建一个新函数create_record()。 现在,在新createEntitied内,调用您的create_record版本 - $this->create_record(),而不是调用全局函数。

现在你可以嘲笑它!甚至因为这个类只用于测试,你甚至可以从中恢复任何你想要的东西,甚至不必现在就嘲笑它。

此类可以驻留在常规代码文件夹中,也可以作为测试类的邻居 - 因为它仅用于测试。

优点:

  • 现有代码根本没有修改。
  • 你仍然使用相同的 功能,除了模拟功能,这是你想要的。

缺点:

  • 你将测试一个与你想要的不同的课程,但是 它仍然足够接近。
  • 函数createEntities内的代码需要 被复制

也许这不是最理想的,但应该完成工作。希望我帮忙。