【问题标题】:Optimizing unit tests which testing same methods with different parameters优化单元测试,用不同的参数测试相同的方法
【发布时间】:2016-05-11 15:48:39
【问题描述】:

我有很多这样的 PHP 类:

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

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

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

方法一:不同clients的不同测试方法

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"]);
    }
}

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

方法二:对不同的clients使用for循环

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:不同的测试用例/测试文件针对不同的clients

// 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"]);
    }
}

缺点:这种方法会导致几十个测试文件,并且一次又一次地使用相同的代码。重构真的很难。

性能比较

方法一:时间:127 ms,内存:8.00Mb

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

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

我正在尝试了解哪种方法对很多这样的类更好,并等待有关在这种情况下优化单元测试的讨论。

【问题讨论】:

    标签: php unit-testing optimization phpunit


    【解决方案1】:

    编辑:即使您的问题侧重于优化,实际上也有针对您的案例被广泛接受的最佳做法,所以这就是我在回答中所展示的内容。

    只需使用 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

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

    【讨论】:

    • 同意,数据提供者是最好的方法,并且为此而设计,我经常使用自定义断言,并且发现它们更容易阅读......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多