嘲笑SUT本身

gra*_*ora 5 php phpunit unit-testing mocking

我的问题是关于单元测试.假设我们有以下课程;

class X
{
    public function p1(){
       //logic
       $a = $this->p2();
       //more logic
    }

    public function p2(){
       //even more logic
    }
}
Run Code Online (Sandbox Code Playgroud)

在为p1方法编写单元测试时,我应该模拟p2方法吗?

我在想的是,为p1方法编写的测试应该只执行并测试p1方法而不是p2.但是为了意识到我应该得到一个类X的模拟并在该模拟实例上调用p1方法,如下所示.

$xMock = $this->getMockBuilder('\X')
    ->setMethods(array('p2'))
    ->getMock();

$xMock->expects($this->any())
    ->method('p2')
    ->will($this->returnValue($value));

$resultTobeAsserted = $xMock->p1();
Run Code Online (Sandbox Code Playgroud)

不幸的是,这样做对我来说有点不对劲.我与同事讨论了这个话题,归结为你如何定义你的SUT(被测系统).如果测试人员将正在测试的特定方法视为SUT,那么从SUT调用的其他方法似乎是依赖关系,并且测试人员自然会想要模拟它们.另一方面,如果测试者将整个类视为SUT,那么这些方法调用将成为测试的一部分,因此没有任何理由来模拟它们.

这个结论是否正确?哪种思维会产生更强大的单元测试?

edo*_*ian 3

当为 p1 方法编写单元测试时,我应该模拟 p2 方法吗?

不。

您正在调用类上的方法,并且您期望事情发生。

通过模拟 p2,您可以对类的实现细节进行例外处理。

不幸的是,这样做对我来说有点不对劲。

我说的是恰到好处的砍伐。

哪种思维会产生更健壮的单元测试?

如果您测试一个类的可观察行为,您可以确保当您更改类的实现时,该类仍然执行其应该执行的操作。这就是稳健性。

如果您测试一个方法并模拟该方法的部分实现(内部方法调用),那么您将测试特定的实现,如果测试失败,您将不知道外部行为是否发生变化。


我在以下文章中对此进行了更详细的描述:

The UNIT in unit testing.

其中详细说明了为什么我认为测试行为而不是方法很重要的几点。

简而言之

PHP 中的单元测试是关于测试类的可观察行为!

行为:

  • 返回值
  • 调用其他方法
  • 修改全局状态(写入文件、数据库、$GLOBALS)

测试那些东西。忽略实施细节。