【问题标题】:PHPUnit test casesPHPUnit 测试用例
【发布时间】:2014-07-21 22:30:29
【问题描述】:

我对单元测试很陌生;我需要使用 PHPUnit 测试来编写测试用例!由于我没有看过任何测试用例,我不知道我该如何测试,我如何编写测试用例......如果你能帮助我测试我的一个小方法,我可以对测试有一个很好的认识和理解逻辑。所以我想知道您是否介意帮助我测试以下方法(最好使用 PHPUnit,但任何其他测试用例也可以帮助我理解逻辑):

static public function get_accepted_images($limit, $page, $uuid) {

    try {
        $conn = new pdo_connect();

        $query = "  SELECT ....";

        $result = $conn->prepare($query);
        $result->bindParam(':uuid', $uuid);
        $result->execute();
        $array_result = $result->fetchAll(PDO::FETCH_OBJ);
        $paginate_result = WS_Utility::paginate_array($array_result, $page, $limit);
        $result_Set = array($paginate_result, TRUE);
    } catch (Exception $e) {

        $result_Set = array($e->getMessage(), FALSE);
    }


    return $result_Set;
}

如果您需要更多说明,请告诉我,我们非常感谢您的帮助!

【问题讨论】:

  • 一般来说,静态方法和单元测试不能混用。
  • 您阅读了 PHPUnit 文档并按照其中的示例进行操作?
  • @GordonM 感谢您的回复!那么在这种情况下我应该怎么做呢?我是否需要将它们一一转换为非静态方法仅用于测试...?
  • 我不得不承认,在给你一个好的答案之前,我需要再次为自己重新阅读文档。 PHPUnit 的数据库测试接口自从我上次使用它后发生了很大的变化。但是,我可以做到这一点,然后会给你一个例子。对我来说,阅读并理解该文档很重要,因为这是理解此处答案的最低基础。此外,您可能需要重构代码以获得可测试性。
  • 好的。明天等我的回答,今天已经很晚了。您应该遵循文档中的教程,在此期间独立于您的代码。这将有助于熟悉它。

标签: php unit-testing testing phpunit testcase


【解决方案1】:

首先你应该把这个方法改成非静态的。

public function get_accepted_images($limit, $page, $uuid) {
    try {
        $conn = new pdo_connect();
        $query = "SELECT...";
        $result = $conn->prepare($query);
        $result->bindParam(':uuid', $uuid);
        $result->execute();
        $array_result = $result->fetchAll(PDO::FETCH_OBJ);
        $paginate_result = WS_Utility::paginate_array($array_result, $page, $limit);
        $result_Set = array($paginate_result, TRUE);
    } catch (Exception $e) {
        $result_Set = array($e->getMessage(), FALSE);
    }
    return $res;
}

接下来就是在这个方法上进行一些集成测试(不是单元测试)。
当您进行集成测试时,您并不关心方法是如何实现的,但您应该准确了解方法的合同(例如:在此输入上将接收此输出,在此上下文上将收到此异常等)

基于此方法合同的知识,您开始进行集成测试:

class ImageDatabaseHelperIntegrationTest extends PHPUnit_Framework_TestCase
{
//The most simple test. When no images exists in the DB what happens?
public function testWhenNoImagesExistsReturnCorrectResult() {
    $sut = new ImageDatabaseHelper(); //I dont know what is the name of the class
    $actual = $sut->get_accepted_images(1,1,1);
    $expected = array(null, FALSE); //?? I dont know what the result is. you know this
    $this->assertEquals($expected, $actual);
}


//You should keep doing like this. 
//The next tests will include inserting data in the database, and testing what is the output




编辑:

user3421904 写道:但为什么不是静态的呢?我们不能这样做吗:$actual = images::get_accepted_images(1, 1, 11); 这个对我有用 好吧!错了吗?

没有错。但是,如果您要对消耗 ImageDatabaseHelper 的其他组件进行单元测试,您将无法模拟它。原因如下:

假设您正在使用一个直接使用get_accepted_images 静态方法的ImageService 类。像这样:

class ImageService {

    public function getAcceptedImages() {
        $images = ImageDatabaseHelper::get_accepted_images(1,1,1);

        //...
        //more code 
        //...

        return $images;
    }
}

如何在不访问数据库的情况下测试getAcceptedImages 方法?你不能,对吧?

现在假设您执行非静态方法。

class ImageService {
    private $imageDatabaseHelper;

    public function __construct(ImageDatabaseHelper $imageDatabaseHelper = null) {
       if(!$imageDatabaseHelper) {
          $this->imageDatabaseHelper = new ImageDatabaseHelper();
       }
       else {
          $this->imageDatabaseHelper = $imageDatabaseHelper;
       }
    }


    public function getAcceptedImages() {
        $images = $this->imageDatabaseHelper->get_accepted_images(1,1,1);

        //...
        //more code 
        //...

        return $images;
    }
}

这很容易测试!!!

class ImageServiceTest extends PHPUnit_Framework_TestCase {
    protected $sut;    

    protected $imageDatabaseHelperDouble;

    public function setUp() {
      $this->imageDatabaseHelperDouble = $this->getMockBuilder('ImageDatabaseHelper')->disableOriginalConstructor()->getMock();
      $this->sut = new ImageService($this->imageDatabaseHelperDouble);
    }


    public function testGetAcceptedImagesWhenCalledShouldCallImageDatabaseHelper() {
       $this->imageDatabaseHelperDouble->expected($this->once())->method('get_accepted_images')->with(1,1,1);
       $this->sut->getAcceptedImages();
    }


    public function testGetAcceptedImagesWhenCalledShouldReturnImages() {
       $this->imageDatabaseHelperDouble->expected($this->any())->method('get_accepted_images')->will($this->returnValue(array("hi im an image", "me tooo")));
       $actual = $this->sut->getAcceptedImages();

       $expected = array("hi im an image", "me tooo");
       $this->assertEquals($expected, $actual);
    }

}

你看到了吗?模拟的 ImageDatabaseHelper 覆盖了 ImageService 使用的原始 ImageDatabaseHelper,他认为他正在使用它。从此你可以为所欲为! (这也包括让 ImageDatabaseHelper 抛出异常!)

玩得开心!

【讨论】:

  • 非常感谢您的回复 :-) 但是为什么不是静态的呢?我们不能这样做: $actual = images::get_accepted_images(1, 1, 11);这个也适用于我!错了吗?
  • 如果你想对上面的层进行单元测试,你必须存根方法get_accepted_images 来返回你想要的任何东西。使用静态方法,您可以隐藏对象的依赖关系,因此无法注入模拟对象并使用存根方法。
  • 您好,感谢您的精彩解释!抱歉,如果我回复晚了!你的解释太好了,但我想你假设我想测试其他函数,我在其中使用了这个静态函数,但我想让它简单一点,假设我只有静态函数,我不调用任何函数其他静态方法(请忽略 WS_Utility::paginate_array() );
  • 由于某种原因我无法更改静态方法,但值得一提的是,在这些静态方法中我不调用任何其他静态方法,我只需要测试这些静态方法并基于此链接( stackoverflow.com/questions/5961023/…) 和这里的解释,如果我在那个函数中没有任何静态方法,测试就可以了!所以我又提出了两个问题,非常感谢您的帮助!
  • 我同意。如果您不打算测试将使用此静态方法的另一个函数,那么如果您想要更简单,则将其保留为静态是正确的。 :)
【解决方案2】:

你不能对这个方法进行单元测试,因为它有太多的副作用。特别是结果会因所选数据库而异。

要测试此方法,您需要进行功能测试。这与单元测试没有太大区别(也可以通过phpunit 完成,但您需要在运行测试之前准备好数据库。这样,您将获得可预测的结果,您可以测试函数的逻辑。

我认为测试上述方法没有多大意义,但我建议宁愿测试WS_Utility::paginate_array(),因为这是唯一一个执行任何逻辑的函数调用,当然除了从数据库中获取。

【讨论】:

  • 感谢您的回复!是的,我想你是对的!我需要做一些“功能测试”但是你能告诉我什么是最好的方法吗?有什么小例子可以澄清更多吗?
  • 我是否理解正确,不需要使用来自 PHPUnit 的命令,如 getDataSet()、datasets、datatables、assert.... 我是否需要单独调用函数并使用 var_dump 或 print_r查看具有预期结果的返回值?您能否为我的代码编写一个功能测试以使其更清晰?
  • 请看phpunit.de/getting-started.html。有具体问题请再来,我帮你理清细节。
  • +1。但这不是集成测试吗?功能测试测试产品所有者可以理解的程序的功能。集成测试检查一个系统的内部,或者将多个系统组合在一起做一些有用的事情。
  • 取决于您对多个系统的定义。例如。在 PHP 世界中,数据库被认为是应用程序逻辑的一部分,因为两者确实紧密耦合。因此,您无法在没有数据库的情况下进行功能测试。您可以尝试将其模拟掉,但我怀疑您是否真的会为此获得有用的测试,而且它会占用大量内存。如果您测试多个松散耦合的服务,集成测试(至少对我而言)是。例如。结合图像服务器测试 CMS,测试两个系统之间的用户复制等。但这实际上是您定义它的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 2014-03-31
  • 2018-12-21
  • 2017-11-10
  • 2013-08-12
  • 2013-08-17
相关资源
最近更新 更多