PHPUnit测试套件的模拟LDAP连接


Mocking LDAP connection for PHPUnit Test Suite

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

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

您应该模拟LDAP适配器,而不是PHP扩展。文件系统和数据库模拟的工作方式相同。。。它们实际上并没有创建文件系统或数据库,它们只是表示通常与这些数据源交互的类,并模仿某些行为,就好像真的存在一样。

例如:

// Load user 12345
$user = UserModel::find(12345); 

通常情况下,此调用将转到数据库并查询用户12345。然而,我们已经模拟了PDO适配器,并告诉它在使用预期参数调用其query()execute()方法时使用数据进行响应。因此,虽然我们似乎已经模拟了整个数据库,但我们真正做的只是模拟离数据库最近但离您自己的代码最远的类。

希望您使用的是一个带有LDAP适配器的身份验证系统,您可以用mock交换该适配器。或者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()方法,你所要做的就是:

  • 创建类本身的mock对象
  • 模拟_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()正常工作

或者,您可以在内存服务器中使用UnboundID Ldap SDK来创建一个正常运行的目录服务器以进行测试。另请参阅:内存中的目录服务器。