将依赖关系与数据提供程序相结合


Combining dependencies with data providers

我有一个测试方法,它依赖于另一个方法,该方法本身使用PHPUnit中的数据提供程序:

/** 
 * @dataProvider getFields
 */
public function testCanDoSomeStuff($parm1, $parm2) {
  $result = my_func($parm1, $parm2);
  $this->assertNotNull($result);
  return $result;
}
/**
 * @depends testCanDoSomeStuff
 */
public function testCanDoSomeMoreStuff($result) {
  $this->assertNotNull($result);
}

我还有一个getFields()数据提供程序函数,这里不需要显示。

依赖于数据提供程序的第一个测试通过-$result不是null。

我希望测试结果将作为$result参数传递给相关测试。但是,testCanDoSomeMoreStuff函数接收到NULL参数,测试失败。

更新

这个简单的测试用例说明了问题:

class MyTest extends PHPUnit_Framework_TestCase {
    /**
     * @dataProvider myFunc
     */
    public function testCanDoSomeStuff($value) {
        $this->assertNotNull($value);
        return $value;
    }
    /**
     * @depends testCanDoSomeStuff
     */
    public function testCanDoSomeMoreStuff($value) {
        $this->assertNotNull($value);
    }
    /**
     * Data provider function
     */
    public function myFunc() {
      $values = array('22');
      return array($values);
    }
}

作为目前的解决方法,我已将结果存储在测试之间的静态属性中。

问题是几个因素造成的:

  • 每个测试结果都存储在一个数组中,使用测试的名称作为关键字
  • 接收数据的测试的名称为<name> with data set #<x>
  • @depends注释不接受多个单词

有一个巧妙的解决方法:重写TestCase::getDataSetAsString以返回注释将接受的名称。这有点问题,因为所需的TestCase字段是私有的,但使用PHP 5.3.2+可以绕过这一点。

重要信息:不幸的是,您不能为的每个数据行运行依赖测试,只能运行一个特定的行。如果您的数据提供程序只返回一行数据,则这不是问题。

这是带有示例测试的代码。请注意,您不必为数据行命名。如果关闭'foo'键,请将@depends更改为testOne-0

class DependencyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider data
     */
    public function testOne($x, $y) {
        return $x + $y;
    }
    public function data() {
        return array(
            'foo' => array(1, 2),
        );
    }
    /**
     * @depends testOne-foo
     */
    public function testTwo($z) {
        self::assertEquals(3, $z);
    }
    protected function getDataSetAsString($includeData = false) {
        if (!$includeData && $this->getPrivateField('data')) {
            return '-' . $this->getPrivateField('dataName');
        }
        return parent::getDataSetAsString($includeData);
    }
    private function getPrivateField($name) {
        $reflector = new ReflectionProperty('PHPUnit_Framework_TestCase', $name);
        $reflector->setAccessible(true);
        return $reflector->getValue($this);
    }
}

显然,这不是一个长期的解决方案。对于接收数据的方法的每个测试结果,最好可以让依赖测试运行一次。您可以向PHPUnit提交功能请求或拉取请求。

如果testCanDoSomeStuff()中的$result真的不是null,那么这应该有效。

要将其拆开,首先尝试在没有数据提供商的情况下简化它,类似于以下内容:

class StackTest extends PHPUnit_Framework_TestCase {
    public function testCanDoSomeStuff() {
        $result = true;
        $this->assertTrue($result); 
        return $result;
    }
    /**
     * @depends testCanDoSomeStuff
     */
    public function testCanDoSomeMoreStuff($result) {
        $this->assertNotNull($result);
    }
}

测试这应该会导致类似。。。

~>phpunit test.php
PHPUnit 3.6.11 by Sebastian Bergmann.
..
Time: 1 second, Memory: 3.25Mb
OK (2 tests, 2 assertions)

现在添加数据提供程序,用函数替换我的简单变量,然后再次测试它。

如果此结果不同,则在测试用例testCanDoSomeStuff()中返回变量$result之前,先将其var_dump。如果不是null,则对行为进行bug处理。

我也希望所描述的问题能起作用,经过一些研究,我发现这不是一个bug,而是一种预期的、没有记录的行为。依赖测试不知道提供程序返回的数据集,这就是测试参数为null的原因。

来源:https://github.com/sebastianbergmann/phpunit/issues/183#issuecomment-816066

@dataProvider注释在测试执行之前进行计算。基本上,预测试阶段为数据提供者提供的每一组参数创建一个测试方法。@dependents依赖于数据驱动测试的原型,因此在某种程度上@dependent依赖于不存在的(未执行的测试)。

另一种想法是,如果提供者提供了一组以上的参数。PHPUnit会生成那么多testDataProvider方法,但不会有那么多testDataReceiver方法,因为在测试前阶段,该测试方法上没有@dataProvider方法。

但是,您可以在相同的测试方法上使用@dependents和@dataProvider。尽管在这种情况下可能没有第一个参数,但要注意正确的参数顺序。

基本上,当数据集有多行时,应该使用数据提供程序。但是,您可以始终同时使用@depend@dataProvider来实现大致相同的行为。