Search code examples
phpcomposer-phpautoloaderphpunit

PHPUnit signals a warning when mocking a namespace-ed class


My composer project consists of the src and tests folders. The code inside src is auto-loaded using composers psr4 autoloader like so

    "autoload": {
    "psr-4": {
        "christmas\\":"src/"
    }
}

The class to be tested looks like so

namespace christmas;
class Hello
{   //constructor & dependency injection included in real class

public function sayHello()
{
    return "HELLO";
}
}

And Finally my test class looks like so

<?php
    use PHPUnit\Framework\TestCase;
    require_once('../vendor/autoload.php');
    use christmas\Hello;


    class TestHello extends TestCase
    {
        public function testSayHelloMethod()
        {
            $hello = $this->getMockBuilder('Hello')
                ->getMock();
            $hello->expects($this->once())
                ->method('sayHello')
                ->will($this->returnValue('HELLO'));

            $this->assertEquals(
                "HELLO",
                $hello->sayHello()
            );


        }
    }

And here is how my i run phpunit

phpunit tests/TestHello

phpunit echoes back the following output

Time: 45 ms, Memory: 4.00 MB

    There was 1 warning:

    1) tests\Hello::testSayHelloMethod()
    Trying to configure method "sayHello" which cannot be configured because it does not exist, has not been specified, is final, or is static

    /usr/share/php/PHPUnit/TextUI/TestRunner.php:641
    /usr/share/php/PHPUnit/TextUI/Command.php:206
    /usr/share/php/PHPUnit/TextUI/Command.php:162

    WARNINGS!
    Tests: 1, Assertions: 0, Warnings: 1.

Below is a view of how my code is organized,

    ├── composer.json
    ├── src
    │   └── Hello.php
    ├── tests
    │   └── TestHello.php
    └── vendor

What am i missing ? I need to have a passing test with no single warning.


Solution

  • You need to pass the full, namespaced name of the class to the mock builder, i.e. 'christmas\Hello'. Since you need to pass it as a string, just using "Hello" isn't enough to properly identify the class.

    Since you're already have a use for the class, you can use ::class to get the full name. For example:

    $hello = $this->getMockBuilder(Hello::class)
                    ->getMock();