dousha1873
2013-01-08 22:45
浏览 34
已采纳

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

I have a Factory class that returns a writer strategy based on the extension of a given file:

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

All of my strategies implement the same interface, for example:

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

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

    public function flush()
    {
        ...
    }
}

I want to be able to test this method using a dummy extension so that my tests are not dependant on any particular strategy existing. I tried creating a mock for the interface with a set classname, but this does not seem to have declared the mock class name:

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

Is there any way for me to stage a mock writer strategy for the factory to load, or should I refactor the factory method to make it more testable?

图片转代码服务由CSDN问答提供 功能建议

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

 公共静态函数getWriterForFile($ file)
 {
 //创建文件信息对象
 $ fileInfo = new \ SplFileInfo($ file); 
 //检查扩展名是否为 present 
 if(''=== $ extension = $ fileInfo-> getExtension()){
 throw new \ RuntimeException(
'目标文件中找不到扩展名:'。$ file 
); 
  } 
 //使用文件扩展名构建类名
 $ className ='MyNamespace \ Writer \ Strategy \\'
。  ucfirst(strtolower($ extension))
。  'Writer'; 
 //尝试获取类的实例
 if(!in_array($ className,get_declared_classes())){
 throw new \ RuntimeException(
'找不到扩展的编写器 :'。$ extension 
); 
} 
 $ instance = new $ className(); 
 $ instance-> setTargetFile($ file); 
返回$ instance; 
} 
   
 
 

我的所有策略都实现相同的接口,例如:

  class CsvWriter实现WriterStrategyInterface 
 {
 public function setTargetFile  ($ file)
 {
 ... 
} 
 
公共函数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条回答 默认 最新

  • dongxiong1941 2013-01-09 02:15
    已采纳

    The main purpose and responsibility for that factory is the creation of objects.

    Imho you should test that using

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

    Why not abstract further?

    You could beimplementation in a way so that the factory dispatches the object creation to another class.

    Having a "object creator" that does:

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

    or something along those lines but what is your gain apart from "well the test looks more like a unit test"? You're not improving really improving your code base and changes just for test-abilities sake always smell a little funny to me.

    I'd treat factory tests as integration/wiring tests that make sure all your factory actually work and produce the desired objects.


    The only change I'd suggest for your current code sample would be to change two things:

    a) make the factory method non static

    If you don't have a very good reason you don't gain anything from a static factory except that you can't inject it and making it static makes it accessible from global scope so getting dependencies into the factory will require even more globals and so on. If you do DI with factories usually making them proper objects too helps avoiding issues down the road

    b) Don't do diskIo in the factory. Use: getWriterForFile(\SplFileInfo $file)

    The factory should not care about the file system. If it needs a existing file it should require one and leave the details of how to handle the error to the consumers.

    That gives you the benefit that you can also pass a SplTempFileObject which will make testing easier and allow your factories to be independent of the file system even for production purposes.

    点赞 打赏 评论

相关推荐 更多相似问题