【问题标题】:Phpunit, how to mock an abstract class?Phpunit,如何模拟一个抽象类?
【发布时间】:2015-09-08 20:40:54
【问题描述】:

我说的是这个功能:

function testMeSomehow ($id, Flag $flags)
{
    $flags::NAME;
}

它的参数对象:

abstract class Flag
{
    abstract function method1();
    abstract function method2();
.
.
.
    abstract function method999();
}

如何模拟这个Flag 类?它有很多抽象方法,我应该用空体创建它们吗?如果这个类发生变化怎么办?我还必须向它添加一个NAME 常量

【问题讨论】:

  • 如果你先试试呢?为什么你认为模拟抽象类与模拟普通类或接口不同?
  • 没错,但看到 $flags::NAME 了吗?如何为 mock 设置常量?

标签: php unit-testing mocking phpunit


【解决方案1】:

您可以像对任何其他课程一样使用测试替身来模拟它,请参阅https://phpunit.de/manual/current/en/test-doubles.html

这可能是一个示例测试:

class TargetClass
{
    public function testMeSomehow($id, Flag $flag)
    {
        return $flag->method1();
    }
}

class TargetClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $mock = $this->getMock('Flag');
        $mock->expects($this->once())
            ->method('method1')
            ->willReturn('methodResult');

        $targetClass = new TargetClass();
        $this->assertEquals('methodResult', $classToTest->testMeSomehow(1, $mock));
    }
}

您可以将要被 mock 替换的方法指定为 $this->getMock() 的第二个参数。因为我们根本没有指定任何东西,所以它将替换所有方法,因此不会打扰抽象方法。

编辑:添加了一个访问常量的示例:

class ClassToTest
{
    public function testMeSomehow($id, Flag $flag)
    {
        return $flag::NAME;
    }
}

class FlagTest extends PHPUnit_Framework_TestCase
{
    public function testStuff()
    {
        $mock = $this->getMock('Flag');
        $classToTest = new ClassToTest();
        $this->assertEquals('Flag name', $classToTest->testMeSomehow(1, $mock));
    }
}

如果您想在测试中为该常量指定一个特定值,我建议使用该常量创建一个子类并将其用于模拟。

编辑:添加了一个示例,其中包含指定动态常量值的解决方法。

class Flag
{
    const NAME = 'Flag name';

    public function getName()
    {
        return static::NAME;
    }
}

class TargetClass
{
    public function testMeSomehow($id, Flag $flag)
    {
        return $flag->getName();
    }
}

class TargetClassTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $mock = $this->getMock('Flag');
        $mock->expects($this->any())
            ->method('getName')
            ->willReturn('New flag name');

        $targetClass = new TargetClass();
        $this->assertEquals('New flag name', $classToTest->testMeSomehow(1, $mock));
    }
}

但是,这迫使您在任何地方都使用getName() 方法,所以我个人更喜欢前面的建议:模拟具有更改值的子类。

【讨论】:

  • 我添加了一个访问常量的示例,这段代码对我有用。
  • 无法动态更改常量值。如果你真的想为不同的测试指定很多不同的值,我添加了一个解决方法,但我会使用以前的解决方案。
猜你喜欢
  • 2013-04-16
  • 1970-01-01
  • 2018-10-06
  • 1970-01-01
  • 2011-12-23
  • 2020-02-18
  • 2014-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多