1
public function thisMethod {
    $example = $this->methodReturnsObject()->this1->that2->there3->id;
    return $example;
}

你将如何在 PHPUnit 中测试 thisMethod?

显然我可以写一个methodReturnsObject() 将返回一些东西的期望......但是什么?该对象具有与之关联的属性,但是您将如何模拟该值?

4

3 回答 3

9

答案是“你没有”。单元测试应该单独测试每个类,你想要做的不是单元测试。正如我在评论中所说,你违反了得墨忒耳法则,简单地说

  • 每个单元应该只对其他单元有有限的了解:只有与当前单元“密切”相关的单元。
  • 每个单位只应与其朋友交谈;不要和陌生人说话。
  • 只与您的直系朋友交谈。

您在那里有需要重构的紧密耦合的类。我在这里先写了类来说明这一点,但我通常先写测试。

让我们从链的末端开始:-

class there3
{
    private $id

    public function setId($id)
    {
        $this->id = $id;
    }

    public function getId()
    {
        return $this->id;
    }

}

现在让我们为其设置一个单元测试:-

class there3Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $there3 = new there3();
        $there3->setId(3);
        $this->assertTrue($there3->getId() === 3);
    }
}

该类现在已经过测试,所以我们不需要再次测试它。现在让我们看下一个:-

class this2
{
    public $there3;

    //To facilitate unit testing we inject the dependency so we can mock it
    public function __construct(there3 $there3)
    {
        $this->there3 = $there3;
    }

    public function getId()
    {
        return $this->there3->getId();
    }

}

现在进行单元测试:-

class this2Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $mockThere3 = $this->getMock('there3');
        $mockThere3->method('getId')
                   ->will($this->returnValue(3);

        $this2 = new this2($mockThere3);//We pass in the mock object instead of the real one
        $this->assertTrue($this2->getId() === 3);
    }
}

我们将做最后一个例子来进一步说明我的观点:-

class this1
{
    private $this2;

    public function __construct(this2 $this2)//injecting again
    {
         $this->$this2 = $this2;
    }

    public function getId()
    {
        return $this->$this2->getId();
    }
}

而且,再次,单元测试: -

class this1Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $mockThis2 = $this->getMock('this2');
        $mockThis2->method('getId')
                  ->will($this->returnValue(3);

        $this1 = new this1($mockThis2);//We pass in the mock object instead of the real one
        $this->assertTrue($this1->getId() === 3);
    }
}

希望您无需我浏览您示例中的所有对象即可了解这个想法。

我所做的是将这些类彼此分离。他们只知道他们所依赖的对象,他们不关心该对象如何获取请求的信息。

现在对 id 的调用看起来像:-

public function getId()
{
    return $this->this1->getId();
}

在返回的 id 是 there2::id 之前,它会沿着链向上。你永远不必写像 $this->$this1->$this2->there3->id 这样的东西,你可以正确地对你的类进行单元测试。

有关单元测试的更多信息,请参阅PHPUnit 手册

于 2012-09-21T19:48:10.700 回答
2

你将如何在 PHPUnit 中测试 thisMethod,我要么:

  1. 尽量避免达到单元测试模拟 3+ 嵌套部分的地步
  2. 创建对象的随机版本,并验证随机是否相同

如:

<?php

// an opaque known
$random = rand();

// the results
$value = new stdClass();
$value->this1 = new stdClass();
$value->this1->this2 = new stdClass();
$value->this1->this2->there3 = new stdClass();
$value->this1->this2->there3->id = $random;

// somehow get the known value into the fixture
// not enough detail shown about $this->methodReturnsObject()
// to make a reasonable suggestion about it.


$this->assertEquals(
   $random, $this->fixture->thisMethod(),
   "Got a value from somewhere else"
);
于 2012-09-21T18:39:39.513 回答
1

使用 rrehbein 的方法从 mock 方法构建返回值,并创建包含任何类的部分 mockthisMethodmethodReturnsObject-- 被测类。模拟methodReturnsObject返回$value创建的对象。

假设该类被命名Foo

function testThisMethod() {
    $foo = $this->getMock('Foo', array('methodReturnsObject'));
    $value = // create as rrehbein demonstrated
    $foo->expects($this->once())
        ->method('methodReturnsObject')
        ->will($this->returnValue($value));
    self::assertSame($value, $foo->thisMethod());
}
于 2012-09-21T19:35:57.043 回答