PHPUnit:如何保存传递给stub方法的参数

时间:2011-03-21 10:05:22

标签: php mocking phpunit

方法setValue()在类SomeClass中接受参数,并且无法为make断言获取此参数。

我可以为方法setValue()创建一个存根,它允许保存传递给此方法的参数吗?


class SomeClass {
  public function setValue($name, $value)
  {
    // do some stuff
  }
  public function doSomething(array $values)
  {
    foreach ($values as $name=>$value) {
      $this->setValue($name, trim($value));
    }
  }
}

class TestSomeClass extends PHPUnit_Framework_TestCase {
public function testDoSomething() { $mock = $this->getMock('SomeClass', array('setValue')); $mock->doSomething(array('v1'=>' string ')); // here need I need assert like this $this->assertEquals('string', $argumentPassedToSetValue); } }

4 个答案:

答案 0 :(得分:3)

关于如何测试设置是否有效,请参阅@Gordons回答。

我想说你不需要 来测试它。

您的单元测试应确保您的类的公共 API按预期工作。您不关心(为了测试)如何在内部存储您的值,因此您不需要需要对其进行断言。这样做也会让你的测试只测试你班级做什么而不是班级怎么做

重点是,在更改课程时不必更改测试而不影响其作用

为了论证,让我们说SomeClass最终会吐出HTML。

class SomeClass {
  public function setValue($name, $value)
  {
    // do some stuff
  }

  public function doSomething(array $values)
  {
    foreach ($values as $name=>$value) {
      $this->setValue($name, trim($value));
    }
  }

  public function createHTML() 
  {
    $return = "";
    foreach($this->values as $key => $value) { 
         $return .= "<div is='$key'>$value</div>"; 
    }
    return $return;
  }

}

如果这是你的班级所做的一切,那么该班级的绝对足够的测试可能如下所示:

class SomeClassTest extends PHPUnit_Framework_TestCase {

    public function testHtmlGenerationWithTwoValuesSet() {
        $o = new SomeClass();
        $o->setValue("foo", "bar");
        $o->setValue("x", "y");
        $result = $o->createHTML();
        $this->assertSame(
             2,
             substr_count("<div>", $result),
             "There should be as many divs as there are values set"
        );
        $this->assertTrue(
             strpos("<div>bar</div>") !== false
             "String should contain a set value enclosed in divs"
        );
    }

}

再说一遍:它是关于测试你的类的行为不测试每个方法。如果以这种方式实现它,您的测试套件将更有价值

虽然使用html的示例可能不是正确的,但它显示了如何非常好地测试行为(我希望)

答案 1 :(得分:2)

没有。 Mocksreplacements for Dependencies,而不是实际的TestSubject。

如果您的setValue方法设置了非公开属性,则可以使用assertAttributeEquals

class SomeClass
{
    protected $foo;
    public function setFoo($val)
    {
        $this->foo = trim($val);
    }
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    public function testSetFooTrimsArgument()
    {
        $testSubject = new SomeClass;
        $testSubject->setFoo('  bar  ');
        $this->assertAttributeEquals(
            'bar',  /* expected value */
            'foo',  /* attribute name */
            $testSubject
        );
    }
}

有关如何测试私有的更多信息,请参阅

答案 2 :(得分:1)

您可以使用returnCallback执行任意代码,例如测试用例的测试助手方法。例如,如果setValueTester是测试用例中的一个方法,则此期望会将对模拟的setValue的调用重定向到它。 func_get_args可以使用参数。

$mock->expects($this->any())->method("setValue")->will($this->returnCallback(array($this,"setValueTester"));

答案 3 :(得分:1)

在这个简单的例子中,您可以指定希望传递给setValue()的参数。

class TestSomeClass extends PHPUnit_Framework_TestCase
{
  public function testDoSomething()
  {
    $mock = $this->getMock('SomeClass', array('setValue'));
    $mock->expects($this->once())->method('setValue')->with('v1', 'string');
    $mock->doSomething(array('v1'=>'  string  '));
  }
}

我同意edorian的观点,你最好通过公共API测试课堂上的内容。直接测试内部实现意味着每次更改或重构时都会更新更多测试。