如何使用PHPUnit测试一个方法调用了同一个类的其他方法,但是没有返回值

Posted

技术标签:

【中文标题】如何使用PHPUnit测试一个方法调用了同一个类的其他方法,但是没有返回值【英文标题】:How to use PHPUnit to test a method that calls other methods of the same class, but returns no value 【发布时间】:2014-10-02 13:51:24 【问题描述】:

如何为调用同一类的其他方法但不返回值的方法编写单元测试? (假设是 phpUnit。)

例如,假设我有以下课程:

class MyClass 

    public function doEverything() 

        $this->doA();
        $this->doB();
        $this->doC();

    

    public function doA() 
        // do something, return nothing
    

    public function doB() 
        // do something, return nothing
    

    public function doC() 
        // do something, return nothing
    


你将如何测试doEverything()

编辑:

我问这个是因为从我读到的内容看来,几乎每个方法都应该有自己的专用 unit 测试。当然,您也有功能和集成测试,但可以说是针对特定的例程(不一定在每个方法级别上)。

但是,如果几乎每个方法都需要自己的 unit 测试,我认为对上述所有方法进行单元测试将是“最佳实践”。是/否?

【问题讨论】:

是否会考虑单元测试?有人可能会争辩说,单元测试不如其他类型的测试重要,例如集成测试,在这种情况下,您可能更擅长进行测试。 听起来不错!关于为功能/集成和验收测试编写单元测试的内容是否有经验法则?我的印象是,几乎每个方法都应该有自己的单元测试,无论它们是否也被更广泛的功能/集成测试和/或验收测试覆盖。 How can I unit test void functions?的可能重复 也看看here。你应该有 4 个单元测试。 漂亮,谢谢。虽然您发布的线程肯定很有帮助,但我认为拥有这个专门显示用于完成我所追求的 PHPUnit 语法的线程没有任何害处? (假设我或其他人可以弄清楚:-) 【参考方案1】:

虽然您的模拟测试确实实现了您的目标,但我认为您降低了对代码的信心。将原始的琐碎方法与测试它的复杂方法进行比较。被测方法失败的唯一方法是忘记添加一个方法调用或输入错误的名称。但是你现在很可能用所有额外的代码来做这件事,而且它没有任何测试!

规则:如果你的测试代码比被测代码更复杂,它需要自己的测试。

鉴于上述情况,您最好找到另一种方法来测试原始代码。对于所写的方法——三个没有参数的方法调用——眼球检查就足够了。但是我怀疑这个方法确实在某个地方有一些副作用,否则你可以删除它。

单元测试是将类作为一个单元进行测试,而不是单独测试每个方法。单独测试每个方法很好地表明您是在代码之后编写测试。使用Test Driven Development 并首先编写测试将帮助您设计一个更易于测试的更好的类。

【讨论】:

【参考方案2】:

好的!我想通了!正如所料,mocking 是我在这种情况下所需要的——而模拟同级方法称为 partial mocking。在 article by Juan Treminio 中有一些关于 PHPUnit 模拟的非常棒的信息。

所以要在上面的课程中测试doEverything(),我需要做这样的事情:

public function testDoEverything()


    // Any methods not specified in setMethods will execute perfectly normally,
    // and any methods that ARE specified return null (or whatever you specify)
    $mock = $this->getMockBuilder('\MyClass')
        ->setMethods(array('doA', 'doB', 'doC'))
        ->getMock();

    // doA() should be called once
    $mock->expects($this->once())
         ->method('doA');

    // doB() should be called once
    $mock->expects($this->once())
         ->method('doB');

    // doC() should be called once
    $mock->expects($this->once())
         ->method('doC');

    // Call doEverything and see if it calls the functions like our
    // above written expectations specify
    $mock->doEverything();

就是这样!很简单!

奖励:如果你使用 Laravel 和 Codeception...

我正在使用 Laravel 框架和 Codeception,这使得弄清楚它有点棘手。如果你使用 Laravel 和 Codeception,你需要做更多的工作才能让它工作,因为 Laravel 自动加载默认情况下不会连接到 PHPUnit 测试。您基本上需要更新您的 unit.suite.yml 以包含 Laravel4,如下所示:

# Codeception Test Suite Configuration

# suite for unit (internal) tests.
class_name: UnitTester
modules:
    enabled: [Asserts, UnitHelper, Laravel4]

更新文件后,别忘了致电php codecept.phar build 更新您的配置。

【讨论】:

如果您需要在其中一种方法中返回值,您会怎么做?例如:doA 返回 $this->doD() @Lucas,你可以添加->andReturns($mock->doB());

以上是关于如何使用PHPUnit测试一个方法调用了同一个类的其他方法,但是没有返回值的主要内容,如果未能解决你的问题,请参考以下文章

如何通过phpunit对一个方法进行单元测试,该方法具有多个内部调用保护/私有方法?

PHPUnit 模拟一个抽象类的所有方法

如何使用 PHPUnit 在类中测试单个 void 方法?

PHPUnit:如何通过测试方法模拟内部调用的函数

来自另一个类的 PHPUnit 模拟方法

如果该函数应该杀死 PHP,您如何使用 PHPUnit 来测试该函数?