我是一个相对较新的单元测试转换器,我遇到了一个绊脚石:
如何使用PHP的内置ftp函数测试连接到远程FTP服务器并在远程FTP服务器上执行操作的代码?一些谷歌搜索出现了Java(MockFtpServer)的快速模拟选项,但没有什么可用于PHP.
我怀疑答案可能是为PHP的ftp函数创建一个包装类,随后可以对其进行存根/模拟来模仿成功/不成功的ftp操作,但我真的很感激那些比我聪明的人的一些意见!
请注意,我一直在使用PHPUnit,并且需要专门针对该框架提供帮助.
根据@hakre的请求,我想测试的简化代码如下所示.我基本上要求最好的测试方法:
public function connect($conn_name, $opt=array())
{
if ($this->ping($conn_name)) {
return TRUE;
}
$r = FALSE;
try {
if ($this->conns[$conn_name] = ftp_connect($opt['host'])) {
ftp_login($this->conns[$conn_name], $opt['user'], $opt['pass']);
}
$r = TRUE;
} catch(FtpException $e) {
// there was a problem with the ftp operation and the
// custom error handler threw an exception
}
return $r;
}
Run Code Online (Sandbox Code Playgroud)
更新/解决方案摘要
问题摘要
我不确定如何单独测试需要与远程FTP服务器通信的方法.您如何测试能够连接到您无法控制的外部资源,对吧?
解决方案摘要
为FTP操作创建适配器类(方法:连接,ping等).然后,在测试使用适配器执行FTP操作的其他代码时,此适配器类很容易存根以返回特定值.
更新2
我最近在测试中遇到了一个使用命名空间的漂亮技巧,它允许你"模拟"PHP的内置函数.虽然适配器是我的特定情况下的正确方法,但这可能对类似情况下的其他人有所帮助:
小智 10
想到两种方法:
为FTP类创建两个适配器:
一个"模拟"的实际上没有连接到任何东西,只返回种子数据.
然后,FTP类的connect()
方法如下所示:
public function connect($name, $opt=array())
{
return $this->getAdapter()->connect($name, $opt);
}
Run Code Online (Sandbox Code Playgroud)
模拟适配器可能看起来像这样:
class FTPMockAdapter
implements IFTPAdapter
{
protected $_seeded = array();
public function connect($name, $opt=array())
{
return $this->_seeded['connect'][serialize(compact('name', 'opt'))];
}
public function seed($data, $method, $opt)
{
$this->_seeded[$method][serialize($opt)] = $data;
}
}
Run Code Online (Sandbox Code Playgroud)
在您的测试中,您将使用结果为适配器播种并验证是否connect()
正确调用了该适配器:
public function setUp( )
{
$this->_adapter = new FTPMockAdapter();
$this->_fixture->setAdapter($this->_adapter);
}
/** This test is worthless for testing the FTP class, as it
* basically only tests the mock adapter, but hopefully
* it at least illustrates the idea at work.
*/
public function testConnect( )
{
$name = '...';
$opt = array(...);
$success = true
// Seed the connection response to the adapter.
$this->_adapter->seed($success, 'connect', compact('name', 'opt'));
// Invoke the fixture's connect() method and make sure it invokes the
// adapter properly.
$this->assertEquals($success, $this->_fixture->connect($name, $opt),
'Expected connect() to connect to correct server.'
);
}
Run Code Online (Sandbox Code Playgroud)在上面的测试用例中,setUp()
注入模拟适配器,以便测试可以在connect()
不实际触发FTP连接的情况下调用FTP类的方法.然后测试为适配器播种,结果只有在connect()
使用正确的参数调用适配器的方法时才会返回.
此方法的优点是您可以自定义模拟对象的行为,并且如果您需要在多个测试用例中使用模拟,它会将所有代码保存在一个位置.
这种方法的缺点是你必须复制已经构建的许多功能(参见方法#2),并且可以说你现在已经引入了另一个必须编写测试的类.
另一种方法是使用PHPUnit的模拟框架在测试中创建动态模拟对象.您仍然需要注入适配器,但您可以即时创建它:
public function setUp( )
{
$this->_adapter = $this->getMock('FTPAdapter');
$this->_fixture->setAdapter($this->_adapter);
}
public function testConnect( )
{
$name = '...';
$opt = array(...);
$this->_adapter
->expects($this->once())
->method('connect')
->with($this->equalTo($name), $this->equalTo($opt))
->will($this->returnValue(true));
$this->assertTrue($this->_fixture->connect($name, $opt),
'Expected connect() to connect to correct server.'
);
}
Run Code Online (Sandbox Code Playgroud)
请注意,上面的测试模拟FTP类的适配器,而不是FTP类本身,因为这将是一个相当愚蠢的事情.
这种方法优于以前的方法:
但是,这种方法有一些缺点:
有关更多信息,请参见http://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects.
归档时间: |
|
查看次数: |
3273 次 |
最近记录: |