Php 模拟已测试的服务或测试实例

Php 模拟已测试的服务或测试实例,php,unit-testing,mocking,phpunit,Php,Unit Testing,Mocking,Phpunit,最后,我与一位同事讨论了如何设置用于测试服务类的单元测试 当设置测试用例时,我们中的一个建议模拟我们正在测试的实际类,而另一个更喜欢创建类的实例,并且只模拟依赖项 假设我们正在测试SomeService 一种解决方案是模拟实际服务并测试模拟: $firstDependency = //create mock for first dependency $secondDependency = //create mock for second dependency $this->someSer

最后,我与一位同事讨论了如何设置用于测试服务类的单元测试

当设置测试用例时,我们中的一个建议模拟我们正在测试的实际类,而另一个更喜欢创建类的实例,并且只模拟依赖项

假设我们正在测试
SomeService

一种解决方案是模拟实际服务并测试模拟:

$firstDependency  = //create mock for first dependency
$secondDependency = //create mock for second dependency
$this->someService = $this->getMockBuilder(SomeService::class)
     ->setMethods(null)
     ->setConstructorArgs(array($firstDependency, $secondDependency))
     ->getMock();

// continue testing $this->someService which is a mock
另一种解决方案是测试服务实例,只模拟依赖项:

$firstDependency  = //create mock for first dependency
$secondDependency = //create mock for second dependency
$this->someService= new SomeService($firstDependency, $secondDependency);

// continue testing $this->someService which is direct instance of SomeService
以下哪种解决方案被视为最佳实践


回答时最好参考官方php单元文档或其他可靠来源。

单元测试的目的是测试行为。模拟要测试的对象实际上意味着您正在测试“伪造”行为。测试预定义的行为有什么意义?

单元测试的目的是测试行为。模拟要测试的对象实际上意味着您正在测试“伪造”行为。测试预定义的行为有什么意义?

。不完全是php单元文档,但所有要点仍然有效。模拟SUT最终测试的是模拟,而不是将在生产中使用的实际类

。不完全是php单元文档,但所有要点仍然有效。模拟SUT最终测试的是模拟,而不是将在生产中使用的实际类

在测试抽象类时,创建模拟被视为良好做法:

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * Service under test in this case an abstract class
     */
    $this->sut;

    public function setUp()
    {
        $this->sut = $this->getMockForAbstractClass('My\Abstract\Class');
    }

    public function testMyAbstractClass()
    {
        $this->sut // do your test
    }
}

在测试抽象类时,创建模拟被视为良好做法:

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * Service under test in this case an abstract class
     */
    $this->sut;

    public function setUp()
    {
        $this->sut = $this->getMockForAbstractClass('My\Abstract\Class');
    }

    public function testMyAbstractClass()
    {
        $this->sut // do your test
    }
}

如果真的不需要mock来测试,就不要创建它。如果真的不需要mock来测试,就不要创建它。我认为这并不像听起来或看起来那样是真正的模仿。假设您有一个完全实现的类,它仍然被定义为抽象类。在本例中,
getMockForAbstractClass
只是获取实例的一种变通方法,而不是伪造实例。这只会为您节省一些时间,以便从抽象类派生一个虚拟类并在测试用例中使用它。模拟抽象类仍然会将模拟魔法添加到抽象方法中,您需要在其中添加一些东西。如果这仍然有用,可能取决于抽象方法的数量和范围,但我会一直尝试测试真正的实现——毕竟,它们必须始终正确地处理所有问题。作为旁注,避免抽象类,使用组合来分离公共代码,并将其作为依赖项注入。抽象类也倾向于需要测试受保护的或私有的方法,这被认为是一种代码味道。我认为这并不像听起来或看起来那样是真正的模仿。假设您有一个完全实现的类,它仍然被定义为抽象类。在本例中,
getMockForAbstractClass
只是获取实例的一种变通方法,而不是伪造实例。这只会为您节省一些时间,以便从抽象类派生一个虚拟类并在测试用例中使用它。模拟抽象类仍然会将模拟魔法添加到抽象方法中,您需要在其中添加一些东西。如果这仍然有用,可能取决于抽象方法的数量和范围,但我会一直尝试测试真正的实现——毕竟,它们必须始终正确地处理所有问题。作为旁注,避免抽象类,使用组合来分离公共代码,并将其作为依赖项注入。抽象类也倾向于要求测试受保护的或私有的方法,这被认为是一种代码气味。