简体   繁体   English

模拟对象实例化以进行单元测试

[英]Mocking the instantiation of an object for a unit test

I am currently refactoring some code which performs Windows Impersonation for testability and have run into a bit of a roadblock. 我目前正在重构一些代码,这些代码执行Windows Impersonation以实现可测试性,并且遇到了一些障碍。 This is the bit of code that I am having trouble with: 这是我遇到麻烦的一些代码:

...
if (LogonUserA(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) > 0)
{
    if (DuplicateToken(token, 2, ref tokenDuplicate))
    {
        var tempWindowsIdentity = new System.Security.Principal.WindowsIdentity(tokenDuplicate);
        var impersonationContext = tempWindowsIdentity.Impersonate();
        ...
    }
...
}

How do I mock the behaviour of instantiating a WindowsIdentity object out? 如何模拟实例化WindowsIdentity对象的行为? I have thought of various alternatives: 我想到了多种选择:

  • Pass in a factory class that would create the instance and mock the behaviour of that 传递一个将创建实例并模拟其行为的工厂类
  • Pass in a delegate that handles the creation of the instance (ie like a C++ function pointer) 传递处理实例创建的委托(例如,像C ++函数指针一样)

None of these alternatives seem particularly good to me because I'm afraid they would blur the intent of the method as the method signature would look something like the following: 这些替代方法对我来说似乎都不是特别好,因为恐怕它们会模糊方法的意图,因为方法签名看起来像以下内容:

public bool Impersonate(string user, string password, string domain, Factory factory)

or 要么

public bool Impersonate(string user, string password, string domain, delegate WinIDCreator)

Because the intent of the method is to impersonate a particular user, it doesn't make sense to me that either a Factory class or Delegate should be provided to it. 因为该方法的目的是模拟特定的用户,所以对我来说,提供Factory类或Delegate都不有意义。 I do want to isolate and mock this behaviour out however as I am uncomfortable with the thought of a new WindowsIdentity instance being created every time I run a bunch of unit tests. 我确实想隔离并模拟这种行为,因为我对每次运行一堆单元测试时都会创建一个新的WindowsIdentity实例感到不安。

Any ideas or comments? 有什么想法或意见吗?

I think you are on the right track with the Factory idea, but I'd inject the Factory in the class constructor not as a method parameter. 我认为您对Factory的想法是正确的,但是我会将Factory注入类构造函数中,而不是将其作为方法参数。 Your default constructor can create an instance of the default Factory if one is not supplied. 如果未提供默认工厂,则默认构造函数可以创建默认工厂的实例。

You are also going to have a few problems -- like needing real login ids and passwords in your unit tests -- if you don't factor out the LogonUserA and DuplicateToken methods as well. 如果您还不考虑LogonUserA和DuplicateToken方法,您还将遇到一些问题-例如在单元测试中需要真实的登录ID和密码。 I'd suggest a thin wrapper around this implementing an interface that you can also inject in the constructor. 我建议围绕此实现接口的薄包装器,也可以将其注入构造函数中。

Below are some of the highlights to show you how to start structuring it. 以下是一些要点,向您展示如何开始构建它。

public interface ILogonHelpers
{
     bool LogonUser( string user, string domain, string password, ref int token );
     void DuplicateToken(  int token, ref int duplicateToken );
}

public class MyClass
{
    public MyClass( ILogonHelper logonHelper, IIdentityFactory factory )
    {
        this.LogonHelper = logonHelper ?? new DefaultLogonHelper();
        this.IdentityFactory = factory ?? new DefaultIdentityFactory();
    }

    ...

if (this.LogonHelper.Logon(user, domain, password, ref token) > 0)
{
    if (this.LogonHelper.DuplicateToken(token, ref tokenDuplicate))
    {
        var tempWindowsIdentity = this.IdentityFactory.CreateIdentity(tokenDuplicate);
        var impersonationContext = tempWindowsIdentity.Impersonate();
        ...
    }
...
}

I'm a Java dev but ... 我是Java开发人员,但...

Why not make the "Factory" an attribute of the the class that contains the Impersonate method? 为什么不将“ Factory”设置为包含Impersonate方法的类的属性?

The "Factory" attribute, perhaps, "windowIdentityFactory", could be set in the constructor or via a setter method (using some type of dependency injection). 可以在构造函数中或通过setter方法(使用某种类型的依赖注入)来设置“ Factory”属性,也许是“ windowIdentityFactory”。

The the test, you would supply the class with a mock Factory (as you've suggested). 在测试中,您将为类提供一个模拟Factory(如您所建议)。 In production, you give it the real deal. 在生产中,您可以提供真正的交易。

...
if (LogonUserA(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) > 0)
{
    if (DuplicateToken(token, 2, ref tokenDuplicate))
    {
        var tempWindowsIdentity = windowIdentityFactory.newInstance(tokenDuplicate);
        var impersonationContext = tempWindowsIdentity.Impersonate();
        ...
    }
...
}

I would create a virtual Impersonate method that you could mock. 我将创建一个可以模拟的虚拟模拟方法。 The Impersonate method would be as follows: 模拟方法如下:


public virtual WindowsImpersonationContext Impersonate(string tokenDuplicate) {
    var tempWindowsIdentity = new System.Security.Principal.WindowsIdentity(tokenDuplicate);  
    var impersonationContext = tempWindowsIdentity.Impersonate();

    return impersonationContext;
}

I agree with tvanfosson about injecting the factory via the constructor (or possibly through a property if you regard the factory as optional). 我同意tvanfosson关于通过构造函数注入工厂(或者,如果您认为工厂是可选的,则可以通过属性注入工厂)。 However, since you're not too happy about that, I suggest you take a look at TypeMock Isolator , which will let you mock instances in a rather unconventional way. 但是,由于您对此不太满意,因此建议您看一下TypeMock Isolator ,它可以让您以一种非常规的方式模拟实例。 It works by injecting IL similar to what profilers do and it allows you to use mocks without changing your object setups. 它通过注入IL来进行工作,类似于探查器所做的事情,并且它允许您使用模拟而无需更改对象设置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM