我已经使用PHPUnit有一段时间了,但突然遇到了一个大难题:如何模拟LDAP。我有一个小的抽象层,用于使用默认的LDAP PHP扩展与LDAP服务器通信。目前,我不知道如何模拟该扩展的连接和功能,以便正确测试我的类。
文件系统和数据库的模拟非常常见且易于设置,但目录服务器呢? :(
// Load user 12345
$user = UserModel::find(12345);
通常情况下,此调用将发送到数据库并查询用户12345。然而,我们已经模拟了PDO适配器,并告诉它当它的query()
或execute()
方法被调用时带有预期参数时,回应数据。因此,虽然看起来我们已经模拟了整个数据库,但我们实际上只是模拟了距离数据库最近但距离您自己的代码最远的类。
希望您正在使用具有LDAP适配器的身份验证系统,可以将其替换为模拟对象。或者使用PHP的ldap函数的包装类。
更新
大问题在于您在几乎每个方法中都使用基本的ldap函数。这不是代码的问题,但很难进行单元测试。我通过创建一个单独的方法来处理所有通信并对其进行断言来解决了这个问题:
(免责声明:该代码没有逻辑意义,根本无法工作。仅供示例)
class LDAP_Auth {
public function authenticate($username, $password) {
// Extra business logic or other things that need to be tested
return $this->_callLdap('ldap_bind', $username, $password);
}
protected function _callLdap() {
$args = func_get_args();
$functionName = array_shift($args); // First argument should be the function name
return call_user_func_array($functionName, $args);
}
}
ldap_*
函数都是从同一个_callLdap()
方法调用的。如果您想测试authenticate()
方法,您所需要做的就是:
_callLdap
方法,并断言它使用正确的参数被调用了一次authenticate()
$ldapMock = $this->getMock('LDAP_Auth', array('_callLdap');
$ldapMock->expects($this->once())
->method('_callLdap')
->with(array('ldap_bind', 'mike', 'password'))
->will($this->returnValue(true));
$ldapMock->authenticate('mike', 'password');
_callLdap
方法被调用一次,参数为 array('ldap_bind', 'mike', 'password')
,以确保 authenticate()
正常工作。