PHPUnit 模拟函数?

2022-01-25 00:00:00 mocking unit-testing php phpunit

I have an interesting scenario in that I need a function to be defined in order to make tests for another function. The function I want to test looks something like this:

if (function_exists('foo') && ! function_exists('baz')) {
    /**
     * Baz function
     * 
     * @param integer $n
     * @return integer
     */
    function baz($n)
    {
        return foo() + $n;
    }
}

The reason I am checking for the existence of foo is because it may or may not be defined in a developer's project and the function baz relies on foo. Because of this, I only want baz to be defined if it can call foo.

The only problem is that so far it has been impossible to write tests for. I tried creating a bootstrap script in the PHPUnit configuration that would define a fake foo function and then require the Composer autoloader, but my main script still thinks foo is not defined. foo is not a Composer package and can not otherwise be required by my project. Obviously Mockery will not work for this either. My question is if anyone more experienced with PHPUnit has come across this issue and found a solution.

Thanks!

解决方案

Start with a slight refactor of the code to make it more testable.

function conditionalDefine($baseFunctionName, $defineFunctionName)
{
    if(function_exists($baseFunctionName) && ! function_exists($defineFunctionName))
    {
        eval("function $defineFunctionName($n) { return $baseFunctionName() + $n; }");
    }
}

Then just call it like this:

conditionalDefine('foo', 'bar');

Your PHPUnit test class will contain the following tests:

public function testFunctionIsDefined()
{
    $baseName = $this->mockBaseFunction(3);
    $expectedName = uniqid('testDefinedFunc');
    conditionalDefine($baseName, $expectedName);
    $this->assertTrue(function_exists($expectedName));
    $this->assertEquals(5, $expectedName(2)); 
}
public function testFunctionIsNotDefinedBecauseItExists()
{
    $baseName = $this->mockBaseFunction(3);
    $expectedName = $this->mockBaseFunction($value = 'predefined');
    conditionalDefine($base, $expectedName);
    // these are optional, you can't override a func in PHP
    // so all that is necessary is a call to conditionalDefine and if it doesn't
    // error, you're in the clear
    $this->assertTrue(function_exists($expectedName));
    $this->assertEquals($value, $expectedName()); 
}
public function testFunctionIsNotDefinedBecauseBaseFunctionDoesNotExists()
{
    $baseName = uniqid('testBaseFunc');
    $expectedName = uniqid('testDefinedFunc');
    conditionalDefine($base, $expectedName);
    $this->assertFalse(function_exists($expectedName));     
}

protected function mockBaseFunction($returnValue)
{
    $name = uniqid('testBaseFunc');
    eval("function $name() { return '$value'; }");
    return $name;
}

That is sufficient for what you're asking. You can however, further refactor this code extracting the function generation into a code generator. That what you can write unit tests against the generator to make sure it creates the kind of function you expect.

相关文章