为 PHPUnit 测试套件模拟 LDAP 连接

3

我已经使用PHPUnit有一段时间了,但突然遇到了一个大难题:如何模拟LDAP。我有一个小的抽象层,用于使用默认的LDAP PHP扩展与LDAP服务器通信。目前,我不知道如何模拟该扩展的连接和功能,以便正确测试我的类。

文件系统和数据库的模拟非常常见且易于设置,但目录服务器呢? :(

2个回答

5
你应该模拟你的LDAP适配器,而不是PHP扩展。文件系统和数据库模拟工作方式相同...它们实际上并不创建文件系统或数据库,只是代表通常与这些数据源交互的类,并模仿某些行为,就像这些数据源真的存在一样。
例如:
// 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() 正常工作。

1
问题是,它已经是一个包装类了。这就是我想要模拟的东西。 - Klaus S.
你能否提供一个你想要测试的方法的小例子以及你想要测试什么? - Mike B
哦,这是我的一个开源项目:https://github.com/klaussilveira/SimpleLDAP - Klaus S.
谢谢,它像代理一样工作。我甚至可以使用__call更轻松地获得这个。 - Klaus S.

1

或者,您可以使用UnboundID Ldap SDK内存服务器创建一个用于测试目的的功能性目录服务器。另请参阅:内存目录服务器


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接