0

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

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()
    {
        ...
    }
}

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

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);;
}

有什么办法让我为工厂加载一个模拟编写器策略,或者我应该重构工厂方法以使其更具可测试性?

4

1 回答 1

5

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

恕我直言,您应该使用

$this->assertInstanceOf('class', $factory->getWriterForFile($file));

为什么不进一步抽象呢?

您可以以某种方式实现,以便工厂将对象创建分派给另一个类。

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

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

或类似的东西,但除了“测试看起来更像是一个单元测试”之外,你的收获是什么?你并没有真正改进你的代码库,只是为了测试能力而改变对我来说总是有点好笑。

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


对于您当前的代码示例,我建议的唯一更改是更改两件事:

a) 使工厂方法非静态

如果你没有一个很好的理由,你不会从静态工厂获得任何东西,除非你不能注入它并使其成为静态使其可以从全局范围访问,因此将依赖项放入工厂将需要更多的全局变量和很快。如果你对工厂进行 DI,通常使它们成为合适的对象也有助于避免未来的问题

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

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

这给您带来的好处是您还可以通过 aSplTempFileObject这将使测试更容易,并使您的工厂即使出于生产目的也可以独立于文件系统。

于 2013-01-09T02:15:21.040 回答