优化单元测试,使用不同的参数测试相同的方法

时间:2016-05-05 00:40:21

标签: php unit-testing optimization phpunit

我有很多像这样的PHP类:

class Article
{
    public function fetchAll($params) {
        if ($params["client"] == "mobile") {
            return [
                "author" => "",
                "body" => ""
            ];
        }
        return [
            "author" => "",
            "body" => "",
            "publishedOn => """
        ];
    }
}

当您看到fetchAll()方法根据$params["client"]返回不同的字段时。 我需要检查这些字段的存在。我有3个选项来实现我的目标。

(注意:所有代码都已简化,不用担心语法错误。)

方法1 :针对不同client s的不同测试方法

class ArticleTest extends ...
{
    public function testFetchAll() {
        $params = [];
        $data = $article->fetchAll($params);
        $this->assertEqual(array_keys($data), ["author","body","publishedOn"]);
    }
    public function testFetchAll2() {
        $params = ["client" => "mobile"];
        $data = $article->fetchAll($params);
        $this->assertEqual(array_keys($data), ["author","body"]);
    }
}

缺点:一次又一次地使用相同的代码,这是一个非常糟糕的做法。无需解释。重构真的很难。此方法也会导致数十种测试方法。

方法2 :为不同的client s

使用for-loop
class ArticleTest extends ...
{
    public function testFetchAll() {
        for ($i=0; $i<2; $i++) {
            $params = [];
            if ($i == 1) $params["client"] = "mobile";
            $data = $article->fetchAll($params);
            if ($i == 0)
                $this->assertEqual(array_keys($data), ["author","body","publishedOn"]);
            else
                $this->assertEqual(array_keys($data), ["author","body"]);
        }
    }
}

缺点:我不确定在PHPUnit测试中使用循环是否是个好主意。代码可读性也降低了。

方法3:针对不同client的不同测试用例/测试文件

// articleTest.php
class ArticleTest extends ...
{
    public function testFetchAll() {
        $params = [];
        $data = $article->fetchAll($params);
        $this->assertEqual(array_keys($data), ["author","body","publishedOn"]);
    }
}
// articleTest2.php
class ArticleTest2 extends ...
{
    public function testFetchAll() {
        $params = ["client" => "mobile"];
        $data = $article->fetchAll($params);
        $this->assertEqual(array_keys($data), ["author","body"]);
    }
}

缺点:此方法会导致数十个测试文件,并反复使用相同的代码。重构真的很难。

效果比较

方法1:时间:127毫秒,内存:8.00Mb

方法2:时间:125毫秒,内存:8.00Mb

方法3:时间:96毫秒,内存:8.00Mb

我试图了解哪种方法更适合这样的类,并等待在这种情况下优化单元测试的讨论。

1 个答案:

答案 0 :(得分:3)

编辑:即使您的问题集中在优化问题上,实际上对您的案例也有广泛接受的最佳做法,以便我在答案中显示的内容。

只需使用 dataProviders 即可。有了这些,您可以执行相同的测试,将参数传递给测试方法。参数可以是参数的变化或/和预期的结果。

所以你可以这样做:

public function fetchAllTestData() {

    return [

        // case 1
        [
             [],  // params
             ['author', 'body', 'publishedon'] // expectedKeys
        ],

        // case 2
        [
             ['client' => 'mobile'],  // params
             ['author', 'body'] // expectedKeys
        ],
    ];
}

/**
 * @dataProvider fetchAllTestData
 */
public function testFetchAll(array $params, array $expectedKeys) {        
    $data = $article->fetchAll($params);
    $this->assertEqual($expectedKeys, array_keys($data));
}

这样,测试将针对每个dataProvider行独立执行。这意味着如果第一种情况失败,第二种情况将被测试,因此您将知道它是否也失败。如果你在测试用例中使用循环,那么每当出现故障时,其余的情况都不会被测试。

看看dataProviders documentation

注意编写测试时请注意断言方法参数的排序。通常,预期的数据首先出现。如果以相反的顺序传递它们,错误消息将会产生误导,并且数组和对象的比较将告诉缺少行,而实际上行不应该存在。