Как мокнуть несколько php функций внутри одного класса phpunit тестов?

Мне надо мокнуть нативные функции wordpress: current_user_can и get_current_user_id. Мокаю я их в разных тестах с помощью PHP-Mock. В первом тесте работает (смотри примеры ниже) а втором уже нет. Но если во втором сменить функцию тоже на get_current_user_id то тогда мок работает и во втором тесте.

Тестируемый метод (namespace Hacc\Inc\Models;):

    /**
     * Set balance.
     *
     * @param float $balance
     *
     * @throws AccessDeniedException
     */
    public function setBalance(float $balance): void
    {
        if (current_user_can('manage_options')
            || get_current_user_id() === $this->ownerId
            || (
                $this->getOwner()->getFamily()->isMember(get_current_user_id())
                && true === $this->familyVisibility
            )
        ) {
            $this->balance = $balance;
        } else {
            throw new AccessDeniedException();
        }
    }

Тесты:

<?php

namespace Hacc\Tests\Inc\Models;

use Hacc\Inc\Exceptions\AccessDeniedException;
use Hacc\Inc\Exceptions\HaccException;
use Hacc\Inc\Exceptions\NewInstanceException;
use Hacc\Inc\Models\Wallet;
use phpmock\environment\MockEnvironment;
use phpmock\functions\FixedValueFunction;
use phpmock\Mock;
use phpmock\MockBuilder;
use phpmock\MockEnabledException;
use PHPUnit\Framework\TestCase;

/**
 * Class WalletTest
 *
 * @author Rodkin Yevhenii <[email protected]>
 * @package Hacc\Tests\Inc\Models
 *
 * @coversDefaultClass \Hacc\Inc\Models\Wallet
 */
class WalletTest extends TestCase
{
    /**
     * @var int
     */
    public $wallet_id;

    /**
     * @throws HaccException
     */
    public function setUp(): void
    {
        $this->wallet_id = Wallet::create('Test common wallet', 2);
    }

    /**
     * @throws AccessDeniedException
     * @throws HaccException
     * @throws MockEnabledException
     * @throws NewInstanceException
     */
    public function tearDown(): void
    {
        $mock = $this->mockFunction('get_current_user_id', 2);

        try {
            $mock->enable();
            $wallet = Wallet::getInstance($this->wallet_id);
            $wallet->deleteWallet();
        } finally {
            $mock->disable();
        }
    }
     /**
     * Test wallet balance setter.
     *
     * @throws MockEnabledException
     * @throws AccessDeniedException
     * @throws NewInstanceException
     *
     * @covers ::setBalance
     * @covers ::getBalance
     */
    public function testSetBalance(): void
    {
        $mock = $this->mockFunction('get_current_user_id', 2);

        $wallet = Wallet::getInstance($this->wallet_id);
        try {
            $mock->enable();
            $wallet->setBalance(20.75);

            $this->assertEquals(20.75, $wallet->getBalance());
        } finally {
            $mock->disable();
        }
    }

    /**
     * Test wallet balance setter by admin.
     *
     * @throws MockEnabledException
     * @throws AccessDeniedException
     * @throws NewInstanceException
     *
     * @covers ::setBalance
     * @covers ::getBalance
     */
    public function testSetBalanceByAdmin(): void
    {
        $mock = $this->mockFunction('current_user_can', true);

        $wallet = Wallet::getInstance($this->wallet_id);
        try {
            $mock->enable();
            $wallet->setBalance(7.25);
        } finally {
            $mock->disable();
        }

        $this->assertEquals(7.25, $wallet->getBalance());
    }

    /**
     * Create mock function.
     *
     * @param string    $function   Function name.
     * @param mixed     $returnVal  The value witch will be return.
     * @param string    $namespace  Namespace.
     *
     * @return Mock
     */
    protected function mockFunction(string $function, $returnVal, string $namespace = 'Hacc\Inc\Models'): Mock
    {
        $builder = new MockBuilder();
        $builder->setNamespace($namespace)
                ->setName($function)
                ->setFunctionProvider(
                    new FixedValueFunction($returnVal)
                );

        return $builder->build();
    }
}

Ответы (1 шт):

Автор решения: Евгений Родкин

Решил эту проблему без использования мокеров. Я в начала файла тестов объявил namescpace тестируемого класса и в нём переопределил нужные мне функции. Получилось так:

<?php

namespace Hacc\Inc\Models;

/**
 * Mock of current_user_can function.
 *
 * @param string $capability
 *
 * @return mixed
 */
function current_user_can(string $capability)
{
    global $current_user_can;

    return $current_user_can ?? \current_user_can($capability);
}

/**
 * Mock of get_current_user_id function.
 */
function get_current_user_id()
{
    global $get_current_user_id;

    return $get_current_user_id ?? \get_current_user_id();
}

namespace Hacc\Tests\Inc\Models;

/**
 * Reset global variables.
 */
function resetGlobals()
{
    global $current_user_can;
    global $get_current_user_id;

    unset($_GLOBAL['current_user_can']);
    unset($_GLOBAL['get_current_user_id']);
}

use Hacc\Inc\Exceptions\AccessDeniedException;
use Hacc\Inc\Exceptions\HaccException;
use Hacc\Inc\Exceptions\NewInstanceException;
use Hacc\Inc\Models\Member;
use Hacc\Inc\Models\Wallet;
use PHPUnit\Framework\TestCase;

/**
 * Class WalletTest
 *
 * @author Rodkin Yevhenii <[email protected]>
 * @package Hacc\Tests\Inc\Models
 *
 * @coversDefaultClass \Hacc\Inc\Models\Wallet
 */
class WalletTest extends TestCase
{
    /**
     * @var int
     */
    public $wallet_id;

/**
     * Test wallet balance setter.
     *
     * @param Wallet $wallet     Wallet instance.
     *
     * @throws AccessDeniedException
     * @throws HaccException
     *
     * @depends testGetInstance
     * @covers ::setBalance
     * @covers ::getBalance
     */
    public function testSetBalance(Wallet $wallet): void
    {
        global $get_current_user_id;
        $get_current_user_id = 2;

        try {
            $wallet->setBalance(20.75);
            $this->assertEquals(20.75, $wallet->getBalance());
        } finally {
            resetGlobals();
        }
    }

    /**
     * Test wallet balance setter by admin.
     *
     * @param Wallet $wallet     Wallet instance.
     *
     * @throws AccessDeniedException
     * @throws HaccException
     *
     * @depends testGetInstance
     * @covers ::setBalance
     * @covers ::getBalance
     */
    public function testSetBalanceByAdmin(Wallet $wallet): void
    {
        global $current_user_can;
        $current_user_can = true;

        try {
            $wallet->setBalance(7.25);
            $this->assertEquals(7.25, $wallet->getBalance());
        } finally {
            resetGlobals();
        }
    }
}
→ Ссылка