【问题标题】:phpunit Mock returns null while original method returns stringphpunit Mock 返回 null 而原始方法返回字符串
【发布时间】:2017-03-15 13:13:27
【问题描述】:

我有这个文件结构:

- module
-- phpunit.xml
-- blaat.php
-- tests
--- blaatTest.php

blaat.php 的内容

class Blaat
{
    public function doSomething()
    {
        return 'my return value';
    }
}

测试内容/blaatTest.php

use PHPUnit\Framework\TestCase;

require_once './blaat.php';

class blaatTest extends TestCase
{ 
    public function testCanBeCreatedFromValidEmailAddress(): void
    {
        $stub = $this->createMock(Blaat::class);

        $this->assertEquals('foo', $stub->doSomething());
    }
}

phpunit.xml 的内容

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
        bootstrap="./tests/bootstrap.php">
    <testsuites>
        <testsuite name="unit">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
    <filter>
        <blacklist>
            <directory suffix=".php">vendor</directory>
        </blacklist>
    </filter>
</phpunit>

当我在终端中运行 phpunit 时(当我在 modules 文件夹中时)我得到了这个:

PHPUnit 6.0.8 by Sebastian Bergmann and contributors.

F                                                     1 / 1 (100%)

Time: 427 ms, Memory: 22.00MB

There was 1 failure:

1) blaatTest::testCanBeCreatedFromValidEmailAddress
Failed asserting that null matches expected 'foo'.

/path/to/module/tests/blaatTest.php:19

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

这怎么可能?我的方法总是返回一个字符串,但是 phpunit 说它得到一个空值作为返回。 我在这里做错了什么?

【问题讨论】:

    标签: php unit-testing testing phpunit


    【解决方案1】:

    来自文档(Chapter 9. Test Doubles)

    默认情况下,原类的所有方法都替换为 只返回 null 的虚拟实现(不调用 原始方法)。使用will($this-&gt;returnValue()) 方法,对于 例如,您可以配置这些虚拟实现以返回 调用时的值

    所以这是默认行为,您需要自己检测。

    【讨论】:

    • aaah,所以我在测试我的方法的响应时不需要使用模拟?这解释了很多。
    • 是的,你应该只模拟与class under test交互的对象
    • 嗯,对不起。我不明白。你到底是什么意思?
    • 对不起我的英语,我的意思是你应该只使用模拟对象和期望来模拟你正在测试的类的依赖关系。
    • 你能举个例子吗?我对 phpunit 真的很陌生,我对此几乎一无所知。所以这可能是我缺乏知识,而不是你的英语,为什么我不明白你想说什么。
    【解决方案2】:

    这个问题可能很老,并且有一个答案被标记为理论上是正确的。以下是执行@Matteo 建议的测试用例示例。

    use Silex\Application;
    use PHPUnit\Framework\TestCase;
    use Ementor\Foundation\ApplicationFactory;
    use Symfony\Component\HttpKernel\HttpKernelInterface;
    
    class ApplicationFactoryTest extends TestCase
    {
       /**
        * @test
        */
       public function it_can_return_a_silex_object()
       {
          $factory = $this->getMockBuilder(ApplicationFactory::class)
              ->setMethods(array('create'))
              ->getMock();
    
           $factory->expects($this->atLeastOnce())
              ->method('create')
              ->willReturnCallback(function(){
                  return new Application();
              });
    
           $this->assertInstanceOf(HttpKernelInterface::class, $factory->create());
        }
     }
    

    在我的例子中,我重复了可以在原始类中找到的逻辑。希望这可以帮助其他像我一样偶然发现这个问题的人。

    【讨论】:

      【解决方案3】:

      看起来您正在 Drupal 中构建测试。我发现了需要模拟长构造函数的情况,因为它包含我们不关心的参数。您的实现是正确的,尽管您缺少 ->setMethods(NULL)

      查看我作为维护者的 webp 模块示例:

      <?php
      namespace Drupal\Tests\phpunit_example\Unit;
      use Drupal\Core\Image\ImageFactory;
      use Drupal\Tests\UnitTestCase;
      use Drupal\webp;
      
      class WebpTest extends UnitTestCase {
        protected $webp;
        /**
         * Before a test method is run, setUp() is invoked.
         * Create new unit object.
         */
        public function setUp() {
          // Mock the class to avoid the constructor.
          $this->webp = $this->getMockBuilder('\Drupal\webp\Webp')
            ->disableOriginalConstructor()
            ->setMethods(NULL)
            ->getMock();
        }
      
        /**
         * @covers Drupal\webp\Webp::getWebpFilename
         */
        public function testgetWebpFilename() {
          $this->assertEquals("testimage.webp", $this->webp->getWebpFilename("testimage.jpg"));
          $this->assertEquals("testimage2.webp", $this->webp->getWebpFilename("testimage2.jpg"));
        }
      
      }
      

      【讨论】:

        【解决方案4】:

        require_once './blaat.php'; 不应该包含两个点 .. 像这样的 require_once '../blaat.php'; 在 tests/blaatTest.php 中吗?

        如果我理解您的目录结构 blaat.php 在另一个(上)目录中,但您引用的是当前目录。

        【讨论】:

        • 不,又一个错误:atal error: require_once(): Failed opening required '../blaat.php' (include_path='.:/usr/local/php5/lib/php') in / 这样工作正常
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-28
        • 2012-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多