模拟Symfony Ldap :: create进行单元测试

Sha*_*ane 6 php ldap symfony mediawiki-extensions

最近我一直在为MediaWiki开发LDAP身份验证提供程序.在我看来,我一直试图解决这个问题已经很多天了,无法提出解决方案.

上下文

我开发此插件的方式是允许配置我们将连接的许多服务器.如果我们无法连接到一台服务器,我们将尝试下一个...依此类推,直到所有用完为止.

为了实现这一点,我在我的类中有一个函数循环尝试连接的服务器,直到成功为止:

private function connect( LdapAuthenticationRequest $req ) {
    $dn = $this->config->get( 'BindDN' )[$req->domain];
    $pass = $this->config->get( 'BindPass' )[$req->domain];
    $servers = $this->config->get( 'Servers' )[$req->domain];
    $encryption = $this->config->get( 'EncryptionType' )[$req->domain];
    if ( false === $dn ) {
        $msgkey = 'ldapauth-attempt-bind-search';
        $bind_with = [ null, null ];
    } else {
        $msgkey = 'ldapauth-attempt-bind-dn-search';
        $bind_with = [ $dn, $pass ];
    }
    $message = new Message( $msgkey, [
        'dn' => "{$dn}@{$req->domain}",
    ] );
    $this->logger->info( $message->text() );
    foreach ( $servers as $server ) {
        if ( false === $server ) {
            continue;
        }
        $ldap = Ldap::create( 'ext_ldap', [
            'host' => $server,
            'encryption' => $encryption
        ] );
        // Attempt bind - on failure, throw an exception
        try {
            call_user_func_array( [ $ldap, 'bind' ], $bind_with );
            $this->server = $server;
            $this->encryption = $encryption;
            // log successful bind
            $msgkey = 'ldapauth-bind-success';
            $message = wfMessage( $msgkey )->text();
            $this->logger->info( $message );
            return $ldap;
        } catch ( SymException $e ) {
            if ( false === $dn ) {
                $msgkey = 'ldapauth-no-bind-search';
            } else {
                $msgkey = 'ldapauth-no-bind-dn-search';
            }
            $message = new Message( $msgkey, [
                'dn' => "{$dn}@{$req->domain}",
            ] );
            $message = $message->text();
            $this->logger->info( $message );
            $this->logger->debug( $e->getMessage() );
        }
    }
Run Code Online (Sandbox Code Playgroud)

我一直试图想出一个更好的方法来做到这一点,这将允许我更好地对这个类进行单元测试,但到目前为止,我正在绘制空白.

我坚持这个问题的一个重要原因是Symfony的LDAP适配器本质上是硬耦合到我的代码中,因为对connect的调用是对Symfony代码库的静态调用.即我无法传入某些描述的连接器实例,然后尝试连接.我可能只是Ldap::create使用自己的连接包装器进行包装吗?

Ahm*_*jar 1

由于您使用的是 Symfony,我想您最好的选择是LDap使用框架的依赖项注入来注入对象。然而我不是 Symfony 的专家。所以作为一个简单的黑客,我会这样做:

  private function connect($req)
    {
        $dn = $this->config->get('BindDN')[$req->domain];
        $pass = $this->config->get('BindPass')[$req->domain];
        $servers = $this->config->get('Servers')[$req->domain];
        $encryption = $this->config->get('EncryptionType')[$req->domain];
        if (false === $dn) {
            $msgkey = 'ldapauth-attempt-bind-search';
            $bind_with = [null, null];
        } else {
            $msgkey = 'ldapauth-attempt-bind-dn-search';
            $bind_with = [$dn, $pass];
        }
        $message = new Message($msgkey, [
            'dn' => "{$dn}@{$req->domain}",
        ]);
        $this->logger->info($message->text());
        foreach ($servers as $server) {
            if (false === $server) {
                continue;
            }
            $ldap = $this->createLDAPObject($server, $encryption);
            // Attempt bind - on failure, throw an exception
            try {
                call_user_func_array([$ldap, 'bind'], $bind_with);
                $this->server = $server;
                $this->encryption = $encryption;
                // log successful bind
                $msgkey = 'ldapauth-bind-success';
                $message = wfMessage($msgkey)->text();
                $this->logger->info($message);
                return $ldap;
            } catch (SymException $e) {
                if (false === $dn) {
                    $msgkey = 'ldapauth-no-bind-search';
                } else {
                    $msgkey = 'ldapauth-no-bind-dn-search';
                }
                $message = new Message($msgkey, [
                    'dn' => "{$dn}@{$req->domain}",
                ]);
                $message = $message->text();
                $this->logger->info($message);
                $this->logger->debug($e->getMessage());
            }
        }
    }

    /**
     * @param $server
     * @param $encryption
     * @return mixed
     */
    public function createLDAPObject($server, $encryption)
    {
        return Ldap::create('ext_ldap', [
            'host' => $server,
            'encryption' => $encryption
        ]);
    }
Run Code Online (Sandbox Code Playgroud)

然后,您可以模拟成员方法createLDAPObject而不是模拟静态方法Ldap::create,这应该更容易。

但是,我建议您重构代码,使其更具可读性和可测试性。

1-首先,call_user_func_array()它并不是真正适合测试,而且我认为您的要求不是太动态,因此您可以将该行替换为$ldap->bind($bind_with[0],$bind_with[1]);

2-您的connect方法太大而无法测试。请阅读代码异味 - 长方法

3-通过将表示与逻辑解耦,可以将该方法重构为更小的版本。例如,您要让Message对象从日志中获取文本$msgkey,这对代码的可读性和可测试性没有帮助。

这是我对这件事的第一个想法:)

快乐的编码和测试:)