如何使用PHPUnit测试Factory / Strategy实现

时间:2013-01-08 22:45:52

标签: php mocking phpunit factory strategy-pattern

我有一个Factory类,它根据给定文件的扩展名返回编写器策略:

public static function getWriterForFile($file)
{
    // create file info object
    $fileInfo = new \SplFileInfo($file);
    // check that an extension is present
    if ('' === $extension = $fileInfo->getExtension()) {
        throw new \RuntimeException(
            'No extension found in target file: ' . $file
        );
    }
    // build a class name using the file extension
    $className = 'MyNamespace\Writer\Strategy\\'
        . ucfirst(strtolower($extension))
        . 'Writer';
    // attempt to get an instance of the class
    if (!in_array($className, get_declared_classes())) {
        throw new \RuntimeException(
            'No writer could be found for the extension: ' . $extension
        );
    }
    $instance = new $className();
    $instance->setTargetFile($file);
    return $instance;
}

我的所有策略都实现了相同的界面,例如:

class CsvWriter implements WriterStrategyInterface
{
    public function setTargetFile($file)
    {
        ...
    }

    public function writeLine(array $data)
    {
        ...
    }

    public function flush()
    {
        ...
    }
}

我希望能够使用虚拟扩展来测试此方法,以便我的测试不依赖于任何特定的策略。我尝试使用set classname为接口创建一个mock,但是这似乎没有声明mock类名:

public function testExpectedWriterStrategyReturned()
{
    $mockWriter = $this->getMock(
        'MyNamespace\Writer\Strategy\WriterStrategyInterface',
        array(),
        array(),
        'SssWriter'
    );

    $file = 'extension.sss';

    $writer = MyNamespace\Writer\WriterFactory::getWriterForFile($file);

    $this->assertInstanceOf('MyNamespace\Writer\Strategy\WriterStrategyInterface', $writer);;
}

我有没有办法让工厂加载模拟编写器策略,还是应该重构工厂方法以使其更易于测试?

1 个答案:

答案 0 :(得分:5)

该工厂的主要目的和责任是创建对象。

你应该使用

进行测试
$this->assertInstanceOf('class', $factory->getWriterForFile($file));

为什么不进一步抽象?

您可以通过某种方式实现,以便工厂将对象创建调度到另一个类。

拥有一个“对象创建者”:

$class = new ReflectionClass($class);
$instance = $class->newInstanceArgs($args);

或类似的东西除了“好吧,测试看起来更像是一个单元测试”之外,你有什么收获?你没有改进真正改进你的代码库和改变测试能力的缘故总是让我觉得有点滑稽。

我将工厂测试视为集成/接线测试,确保您的所有工厂实际工作并生成所需的对象。


我建议你当前代码示例的唯一变化是改变两件事:

a)使工厂方法非静态

如果你没有一个很好的理由你没有从静态工厂获得任何东西,除了你不能注入它并使其静态使它可以从全球范围访问,因此获得依赖进入工厂将需要甚至更多全局变量等等。如果你对工厂进行DI,通常会使它们成为合适的物体,这也有助于避免出现问题

b)不要在工厂中使用diskIo。使用:getWriterForFile(\ SplFileInfo $ file)

工厂不应该关心文件系统。如果它需要一个现有文件,它应该需要一个,并留下如何处理错误给消费者的细节。

这样做的好处是,您还可以传递SplTempFileObject,这将使测试更容易,并允许您的工厂独立于文件系统,即使是出于生产目的。