4

I am very new to PHP Unit Testing. I am trying to create Unit Test for the following function:

    $context = $args[0];

    if (Subscriber::instance()->isSubscriber()) {
        $context['body_class'] .= ' ' . $this->bodyClass;
    }

    return $context;

Which is very simple which adds classname in array if User is subscriber. Subscriber is class which has has a static instance method which returns true or false.

so far I have written this but I don't think this is correct:

$subscriber = $this->getMockBuilder(Subscriber::class)
    ->disableOriginalConstructor()
    ->setMethods(['isSubscriber'])
    ->getMock();

$subscriber->expects($this->once())
    ->method('isSubscriber')
    ->will($this->returnValue(true));

$this->assertInternalType('bool',$subscriber->isSubscriber());

Any help would be appreciated.

user991041
  • 81
  • 1
  • 2
  • 5

2 Answers2

4

Yes, You can mock PHP static methods using PhpUnit together with Mockery!

Case: class to be tested Foo (Foo.php) uses the static method mockMe of the class ToBeMocked.

<?php

namespace Test;

use  Test\ToBeMocked;

class Foo {

    public static function bar(){
        return 1  + ToBeMocked::mockMe();        
    }
}

ToBeMocked class (ToBeMocked.php)

<?php

namespace Test;

class ToBeMocked {

    public static function mockMe(){
        return 2;                
    }
}

The PhpUnit test file (FooTest.php) with mock of static method (using Mockery):

<?php

namespace Test;

use Mockery;
use Mockery\Adapter\Phpunit\MockeryTestCase;

// IMPORTANT: extends MockeryTestCase, NOT EXTENDS TestCase
final class FooTest extends MockeryTestCase 
{
    protected $preserveGlobalState = FALSE; // Only use local info
    protected $runTestInSeparateProcess = TRUE; // Run isolated

    /**
     * Test without mock: returns 3
     */
    public function testDefault() 
    {

        $expected = 3;
        $this->assertEquals(
            $expected,
            Foo::bar()
        );
    }

     /**
     * Test with mock: returns 6
     */
    public function testWithMock()
    {
        // Creating the mock for a static method
        Mockery::mock(
        // don't forget to prefix it with "overload:"
        'overload:Geko\MVC\Models\Test\ToBeMocked',
        // Method name and the value that it will be return
        ['mockMe' => 5]
    );
        $expected = 6;
        $this->assertEquals(
            $expected,
            Foo::bar()
        );
    }
}

Running this test:

$ ./vendor/phpunit/phpunit/phpunit -c testes/phpunit.xml --filter FooTest
PHPUnit 9.5.4 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.0.3
Configuration: testes/phpunit.xml

..                                                                  2 / 2 (100%)

Time: 00:00.068, Memory: 10.00 MB

OK (2 tests, 3 assertions)
Dharman
  • 30,962
  • 25
  • 85
  • 135
Allan Andrade
  • 670
  • 10
  • 26
1

You can test (assert) static methods but you can't mock or stub them in PHPunit.

From the documentation:

Please note that final, private, and static methods cannot be stubbed or mocked. They are ignored by PHPUnit’s test double functionality and retain their original behavior except for static methods that will be replaced by a method throwing a \PHPUnit\Framework\MockObject\BadMethodCallException exception.

Sven van Zoelen
  • 6,989
  • 5
  • 37
  • 48