开发者

How to mock System.DirectoryServices.SearchResult?

开发者 https://www.devze.com 2022-12-17 12:59 出处:网络
If you have a method that needs to be tested that takes a list of SearchResults public virtual void ProcessResults(IList<SearchResult> list)

If you have a method that needs to be tested that takes a list of SearchResults

public virtual void ProcessResults(IList<SearchResult> list)
{
    //Code to tests here
}

How do you mock up that list of SearchResult?

Note: No low-level injection frameworks 开发者_JAVA技巧(eg TypeMock) allowed.


Currently i have this ugly code

public static class SearchResultFactory
{
    const BindingFlags nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
    const BindingFlags publicInstance = BindingFlags.Public | BindingFlags.Instance;

    public static SearchResult Construct<T>(T anonInstance)
    {
        var searchResult = GetUninitializedObject<SearchResult>();
        SetPropertiesFieled(searchResult);
        var dictionary = (IDictionary)searchResult.Properties;
        var type = typeof(T);
        var propertyInfos = type.GetProperties(publicInstance);
        foreach (var propertyInfo in propertyInfos)
        {
            var value = propertyInfo.GetValue(anonInstance,null);
            var propertyCollection = GetUninitializedObject<ResultPropertyValueCollection>();
            var innerList = GetInnerList(propertyCollection);
            if (propertyInfo.PropertyType.IsArray)
            {
                var stringArray = (String[])value;
                foreach (var subValue in stringArray)
                {
                    innerList.Add(subValue);
                }
            }
            else
            {
                innerList.Add(value);
            }
            var lowerKey = propertyInfo.Name.ToLower(CultureInfo.InvariantCulture);
            dictionary.Add(lowerKey, propertyCollection);
        }
        return searchResult;
    }

    static ArrayList GetInnerList(object resultPropertyCollection)
    {
        var propertyInfo = typeof(ResultPropertyValueCollection).GetProperty("InnerList", nonPublicInstance);
        return (ArrayList) propertyInfo.GetValue(resultPropertyCollection, null);
    }

    static void SetPropertiesFieled(SearchResult searchResult)
    {
        var propertiesFiled = typeof(SearchResult).GetField("properties", nonPublicInstance);
        propertiesFiled.SetValue(searchResult, GetUninitializedObject<ResultPropertyCollection>());
    }

    static T GetUninitializedObject<T>()
    {
        return (T) FormatterServices.GetUninitializedObject(typeof(T));
    }
}

which is used...

var searchResult = SearchResultFactory.Construct(
         new
         {
             name = "test1",
             givenName = "John",
             sn = "Smith",
             rights = new String[] { "READ", "WRITE" }
         });


,If you're going to do any amount of AD programming, and you want to be able to test it, consider writing a wrapper that you can use in place of BOTH SearchResult and DirectoryEntry - that way as an added benefit you don't have to write two of every function that would need to take either a SearchResult or DirectoryEntry.

I did something similar to this. It was not exactly a single-evening project, but totally worth it given that I was working on an ISV AD product. You can probably wrap less and decrease effort. Here's a pseudocode layout to give you an idea:

      DirectoryObject : IDirectoryObject, IDisposable (Important!)
          ctor (DirectoryEntry)
          ctor (SearchResult)
          ctor (string Path)
          string Path
          bool IsValid 
          Search(with a gazillion overloads)
          DirectoryObjectPropertyCollection Properties 
          //(which itself uses DirectoryObjectPropertyValueCollection to wrap PropertyValueCollection)

      //To get at the native underlying objects if necessary since I only wrapped commonly used elements
      DirectoryEntry NativeDirectoryEntry  
      SearchResult NativeSearchResult

      //So I know whether to grab the native SearchResult or DirectoryEntry
      IsDirectoryEntry
      IsSearchResult

This approach - besides the increased testability - saves me from having to do things like the following in our shared libaries:

  public void DoSomethingWithAUser(DirectoryEntry user,...)
  public void DoSomethingWithAUser(SearchResult user,...)
  public void DoSomethingWithAUser(string userPath,...)

and now we just have

  public void DoSomethingWithAUser(DirectoryObject user,...)


You could write your own wrapper class around the SearchResult, implement some ISearchResult interface.

Your concrete implementation uses the SearchResult class internally exposing as much as you require, and then you can mock the Interface at testing.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号