如何与 Active Directory 角色提供程序创建可靠的集成测试?

发布于 2024-08-12 10:27:20 字数 786 浏览 10 评论 0原文

我最近重构了 Active Directory 角色提供程序中的一些代码,以删除对多个域的支持。在此过程中,我的集成测试以我意想不到的方式出现了问题。除非我在测试设置代码和调用被测试方法的代码之间设置显着的延迟,否则测试不会可靠地成功。如果我使用调试器运行测试,它总是会成功,并且我看不到代码有任何问题。如果我使用自动化工具运行测试,一个或多个测试会失败,并且会以意想不到的方式失败。

如何可靠地测试使用 System.Directory.AccountManagement 命名空间类和方法的角色提供程序代码?

注意:为了与 SO 范例保持一致,我提供了我找到的解决方案作为单独的答案。但是,如果您觉得您的解决方案比我的解决方案效果更好,我对其他解决方案持开放态度。之所以提出这个问题,是因为我找不到任何现有的问题可以解决我的问题。

一些相关的问题是:

I recently refactored some code in an Active Directory role provider to remove support for multiple domains. In the process my integration tests broke in ways that I didn't expect. The tests do not reliably succeed unless I put significant delays between the test set up code and the code that invoked the method being tested. If I run the test using the debugger it always succeeds and I can't see any problems with the code. If I run the test using the automated tools one or more tests fail and fail in ways that are unexpected.

How can I reliabily test role provider code that uses the System.Directory.AccountManagement namespace classes and methods?

Note: In keeping with the SO paradigm, I'm providing the solution that I found as a separate answer. I'm open to other solutions, however, if you feel that your solution works better than mine. This question is being contributed because I couldn't find any existing questions on SO that addressed my problem.

Some related questions are:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小ぇ时光︴ 2024-08-19 10:27:20

我发现问题在于我在角色提供程序中使用的 PrimarySearchers 并不总是与设置中使用的代码联系同一个域控制器。由于域控制器之间的传播延迟,这会导致错误。为了解决这个问题,我使用构造函数注入来向角色提供者提供设置中使用的PrincipalContext。这允许角色提供者始终使用与测试代码相同的上下文。此外,我将PrincipalSearcher 上的SearchRoot 替换为基于通过构造函数注入提供的PrincipalContext 的搜索根。相关代码如下。请注意,角色提供程序实现 IDisposable,以便在未从外部提供域上下文的情况下处置域上下文。

private bool DisposeContext { get; set; }
private PrincipalContext DomainContext { get; set; }

public PrintAccountingRoleProvider() : this( null ) { }

public PrintAccountingRoleProvider( PrincipalContext domainContext )
{
    this.DisposeContext = domainContext == null;
    this.DomainContext = domainContext ?? new PrincipalContext( ContextType.Domain );
}

……

private UserPrincipal FindUser( string userName )
{
    using (PrincipalSearcher userSearcher = new PrincipalSearcher())
    {
        UserPrincipal userFilter = new UserPrincipal( this.DomainContext );
        userFilter.SamAccountName = userName;
        userSearcher.QueryFilter = userFilter;

        // Replace the searcher with one directly associated with the context to ensure that any changes
        // made elsewhere will be reflected if we call the search immediately following the change.  This
        // is critical in the integration tests.
        var searcher = userSearcher.GetUnderlyingSearcher() as DirectorySearcher;
        searcher.SearchRoot = new DirectoryEntry( @"LDAP://" + this.DomainContext.ConnectedServer + @"/dc=iowa,dc=uiowa,dc=edu" );

        return userSearcher.FindOne() as UserPrincipal;
    }
}

private void Dispose( bool disposing )
{
    if (!this.disposed)
    {
        if (disposing)
        {
            if (this.DisposeContext && this.DomainContext != null)
            {
                this.DomainContext.Dispose();
                this.DomainContext = null;
            }
        }
        this.disposed = true;
    }
}

I discovered that the problem was that the PrincipalSearchers that I was using in the role provider did not always contact the same domain controller as the code used in set up did. This would result in errors due to propagation delays between domain controllers. To solve this problem I used constructor injection to provide the PrincipalContext used in set up to the role provider. This allows the role provider to always use the same context as the test code. In addition I replaced the SearchRoot on the PrincipalSearcher with a search root based on the PrincipalContext provided via constructor injection. Relevant code below. Note that the role provider implements IDisposable in order to dispose of the domain context if one isn't supplied externally.

private bool DisposeContext { get; set; }
private PrincipalContext DomainContext { get; set; }

public PrintAccountingRoleProvider() : this( null ) { }

public PrintAccountingRoleProvider( PrincipalContext domainContext )
{
    this.DisposeContext = domainContext == null;
    this.DomainContext = domainContext ?? new PrincipalContext( ContextType.Domain );
}

...

private UserPrincipal FindUser( string userName )
{
    using (PrincipalSearcher userSearcher = new PrincipalSearcher())
    {
        UserPrincipal userFilter = new UserPrincipal( this.DomainContext );
        userFilter.SamAccountName = userName;
        userSearcher.QueryFilter = userFilter;

        // Replace the searcher with one directly associated with the context to ensure that any changes
        // made elsewhere will be reflected if we call the search immediately following the change.  This
        // is critical in the integration tests.
        var searcher = userSearcher.GetUnderlyingSearcher() as DirectorySearcher;
        searcher.SearchRoot = new DirectoryEntry( @"LDAP://" + this.DomainContext.ConnectedServer + @"/dc=iowa,dc=uiowa,dc=edu" );

        return userSearcher.FindOne() as UserPrincipal;
    }
}

...

private void Dispose( bool disposing )
{
    if (!this.disposed)
    {
        if (disposing)
        {
            if (this.DisposeContext && this.DomainContext != null)
            {
                this.DomainContext.Dispose();
                this.DomainContext = null;
            }
        }
        this.disposed = true;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文