2

I've been working on getting our systems more compatible with PHPUnit so we can do more unit testing of our classes and have managed to get some of them working with mock objects, but I've run across a problem which I can't seem to get around.

One of the classes we have (which I'm creating a mock version of) is for queries. You pass the query string into it's "query()" method, it logs the query, runs it and returns the result. It also wraps the mysql_fetch_assoc with a method called "get_row()", which returns an array value much like the original.

The problem is that, in some methods, there's more than one query being passed to the "query()" method and as a result it needs to run through multiple while loops to load the data into different variables. I've created a simplified version below:

class object{
    public function __construct($query){
        $this->query = $query;
    }

    public function loadData(){

        $data1 = queryDataSource("SELECT * FROM data1");
        $data2 = queryDataSource("SELECT * FROM data2");

        return Array(
            "data1" => $data1,
            "data2" => $data2,
        );

    }

    private function queryDataSource($query){
        $this->query->query($query)

        while($row = $this->query->get_row()){
            $result[] = $row;
        }

        return $result
    }
}

class testObject extends PHPUnit_Framework_TestCase{
    method testLoadData(){
        $test_data = Array('name' => 'Bob', 'number' => '98210');

        $query = $this->getMock('Query');
        $query->expects($this->any())->method('query');
        $query->expects($this->at(1))->method('get_row')->will($this->returnValue($test_data);
        $query->expects($this->at(2))->method('get_row')->will($this->returnValue(False);
        $query->expects($this->at(3))->method('get_row')->will($this->returnValue($test_data);
        $query->expects($this->at(4))->method('get_row')->will($this->returnValue(False);
    }
}

In order to escape the first while loop in $object->queryDataSource() I'm returning a boolean FALSE value, as would happen when doing mysql_fetch_assoc. The problem is that, when it tries to run the second query and fetch the data through get_row(), the mock object seems to keep returning FALSE ratehr than moving on to the at(3) point. This happens even with 4 objects, only the first will get the test data as a return value then get FALSE the second time, the others will get FALSE every time.

Does anyone know if there's a way to get around this? I tried removing the FALSE flags and just having the odd values in at(), but that had the same problem, and I tried just having it return the data for at(1-2), but that just passed all the data into the first while loop and nothing for the other.

Thanks for any help you can give, hope the description of the problem's clear enough

4

2 回答 2

7

我无法运行代码,因为它似乎只是伪代码,但据我了解,您正试图像这样模拟:

Call to query, get_row, get_row, query, get_row, get_row.

您似乎遇到的问题是匹配器中的数字不是 per而是 per->at()methodobject

所以你可能想要写的是:

    $query->expects($this->any())->method('query');
    $query->expects($this->at(1))->method('get_row')->will($this->returnValue($test_data);
    $query->expects($this->at(2))->method('get_row')->will($this->returnValue(False);
    $query->expects($this->at(4))->method('get_row')->will($this->returnValue($test_data);
    $query->expects($this->at(5))->method('get_row')->will($this->returnValue(False);

或者让它更容易阅读甚至:

    $query->expects($this->at(0))->method('query');
    $query->expects($this->at(1))->method('get_row')->will($this->returnValue($test_data);
    $query->expects($this->at(2))->method('get_row')->will($this->returnValue(False);
    $query->expects($this->at(3))->method('query');
    $query->expects($this->at(4))->method('get_row')->will($this->returnValue($test_data);
    $query->expects($this->at(5))->method('get_row')->will($this->returnValue(False);

通过您的模拟,您遇到了第二次调用“查询”正在计算一个“调用”并因此跳过第二个的问题return($test_data);

于 2012-10-03T14:30:48.723 回答
1

不幸的是,at()您的测试非常强烈地绑定到实现。

想象一下,如果您在测试方法中重新安排了 2 个方法调用,功能完全相同,但现在使用的所有测试at()都将失败,通常带有神秘的消息,例如method doesn't exist at index N

在某些情况下您想明确地说“这就是这样调用的,然后这个就是这样调用的”,这很好,但是如果您只想要断言,那么 PHPUnit Mock Extensions 之一似乎更友好,尤其是Mockery这里的指南(触摸我相信过时了)

还有其他的。

于 2012-10-23T19:52:28.380 回答