说明 我有一个简单的类,它创建一个符号链接到上传文件的目录,这些文件只对注册成员可用。它使用当前用户会话ID来为用户生成随机目录。用户注销后,符号链接将被删除。我想对单元的功能进行单元测试。
问题: 我如何对这个课程进行适当的单元测试,因为大部分功能都是私密的,而且我没有看到任何理由让它们公开?
以下是PHP类的代码:
<?php
namespace Test\BackEnd\MemberBundle\Library;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\KernelInterface;
class DirectoryProtector
{
/** @var SessionInterface $_session */
private $_session;
/** @var ContainerInterface $_kernel */
private $_kernel;
/**
* @param SessionInterface $session
* @param KernelInterface $kernel
*/
public function __construct( SessionInterface $session, KernelInterface $kernel )
{
$this->_session = $session;
$this->_kernel = $kernel;
}
/**
* @param bool|false $protect
* Public method to symlink directories
*/
public function protectDirectory($protect = FALSE)
{
if ($protect) {
if ( ! $this->doesDirectoryExists())
symlink($this->getAppDir() . '/uploads', $this->getViewableSessionDirectory());
} else {
if ($this->doesDirectoryExists())
unlink($this->getViewableSessionDirectory());
}
}
/**
* @return bool
* Check to see if viewable session directory exists or not
*/
private function doesDirectoryExists()
{
if (file_exists($this->getViewableSessionDirectory()))
return TRUE;
return FALSE;
}
/**
* @return string
* Get viewable session full directory path
*/
private function getViewableSessionDirectory()
{
return $this->getAppDir() . '/../web/files/' . $this->getSessionId();
}
/**
* @return string
* Return app root directory
*/
private function getAppDir()
{
return $this->_kernel->getRootDir();
}
/**
* @return string
* Return session id
*/
private function getSessionId()
{
return $this->_session->getId();
}
}
以下是当前测试类的代码:
<?php
namespace Test\BackEnd\MemberBundle\Tests\Library;
use Test\BackEnd\MemberBundle\Library\DirectoryProtector;
class DirectoryProtectorTest extends \PHPUnit_Framework_TestCase
{
public function testProtectDirectory()
{
//$this->markTestIncomplete("WIP on protect directory.");
$request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')
->getMock();
$container = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')
->getMock();
/** @var DirectoryProtector $dp */
$dp = $this->getMockBuilder('Test\BackEnd\MemberBundle\Library\DirectoryProtector')
->setConstructorArgs(array($request, $container))
->setMethods(array(
'getViewableSessionDirectory',
'getAppDir',
'getSessionId'
))
->getMock();
$dp->expects($this->once())
->method('doesDirectoryExists')
->will($this->returnValue(TRUE));
$dp->protectDirectory(TRUE);
}
}
答案 0 :(得分:4)
来自https://phpunit.de/manual/current/en/test-doubles.html
限制:最终,私有和静态方法
请注意,无法对最终,私有和静态方法进行存根或模拟。它们被PHPUnit的测试双重功能忽略,并保留其原始行为。
单元测试私有或受保护的方法不是一个好习惯。您应该测试公开 API 。私有方法应该通过API间接测试。也就是说,您可以使用反射公开方法:
$instance = new DirectoryProtector(...);
$ref = new \ReflectionClass('DirectoryProtector');
$method = $ref->getMethod('doesDirectoryExists');
$method->setAccessible(true);
$this->assertTrue($method->invoke($instance));
答案 1 :(得分:1)
我认为您的方法不正确。你不应该模拟方法,而是通过构造函数中的注入传递的对象。
对于SessionInterface
,您可以通过MockArraySessionStorage
。对于ContainerInterface
,我不知道您正在使用哪个课程,因此我添加了一些演示示例。测试用例:
public function testProtectDirectory()
{
$session = new MockArraySessionStorage();
$session->setId('123123');
$kernel = \Ouzo\Tests\Mock\Mock::create('ContainerInterface');
\Ouzo\Tests\Mock\Mock::when($kernel)->getRootDir()->thenReturn('/root_dir');
$directoryProtector = new DirectoryProtector($session, $kernel);
$directoryProtector->protectDirectory(true);
//asserts
}
您的方法将返回:
getViewableSessionDirectory
- &gt; /root_dir/../web/files/123123
getAppDir
- &gt; /root_dir
请记住,更改可访问的方法总是糟糕的主意。
PS。对于mocking I use utils form Ouzo framework。当然你可以使用其他模拟框架。