How to mock System.DirectoryServices.SearchResult?
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.
精彩评论