太多的接口和包装器?

时间:2015-08-24 02:19:25

标签: c# unit-testing mocking moq

我正在慢慢开始接受单元测试和嘲笑,但这是一个缓慢的过程。我试过单元测试这个Active Directory代码。这个问题与AD并不严格相关。

class ActiveDirectoryQueryer {    
   DirectorySearcher mSearcher;

   public ActiveDirectoryQueryer() {
      var searcher = new DirectorySearcher(...);
   }

   public void GetAllMailEntries() {
      MailEntries =
         mSearcher
         .FindAll()
         .Select(result => result.GetDirectoryEntry())
         .Select(BuildNewADUser)
         .ToList();
   }

   static ActiveDirectoryUser BuildNewADUser(DirectoryEntry pDirectoryEntry) {
      return ActiveDirectoryUser.Create(
         pDirectoryEntry.Guid,
         (pDirectoryEntry.Properties["name"].Value ?? "").ToString(),
         (pDirectoryEntry.Properties["mail"].Value ?? "").ToString()
      );
   }

所以,我想对GetAllMailEntries方法进行单元测试。为了使用MOQ执行此操作,我必须手动为各种.NET类型生成接口和包装器,并改为将许多上述引用更改为接口(如IDirectoryEntry)。下面的每个IXxxx接口都有一个关联的包装类XxxxWrapper。总共我为这一个测试添加了至少12个新的源文件。以下是我最终用于单元测试的内容:

[TestMethod]
public void TestGetAllMailEntries() {
   var mockSearcher = new Mock<IDirectorySearcher>();
   var mockResultCollection = new Mock<ISearchResultCollection>();
   var mockSearchResult = new Mock<ISearchResult>();
   var mockDirectoryEntry = new Mock<IDirectoryEntry>();
   var mockPropertyCollection = new Mock<IPropertyCollection>();
   var nameMockPropertyValueCollection = new Mock<IPropertyValueCollection>();
   var mailMockPropertyValueCollection = new Mock<IPropertyValueCollection>();

   const string name = "SomeNameValue";
   const string mailAddress = "SomeMailAddress";

   nameMockPropertyValueCollection.SetupGet(pvc => pvc.Value).Returns(name);
   mailMockPropertyValueCollection.SetupGet(pvc => pvc.Value).Returns(mailAddress);
   mockPropertyCollection.SetupGet(pc => pc["name"]).Returns(nameMockPropertyValueCollection.Object);
   mockPropertyCollection.SetupGet(pc => pc["mail"]).Returns(mailMockPropertyValueCollection.Object);
   mockDirectoryEntry.SetupGet(de => de.Properties).Returns(mockPropertyCollection.Object);
   mockSearchResult.Setup(sr => sr.GetDirectoryEntry()).Returns(mockDirectoryEntry.Object);
   mockResultCollection.Setup(results => results.GetEnumerator()).Returns(new List<ISearchResult> { mockSearchResult.Object }.GetEnumerator());
   mockSearcher.Setup(searcher => searcher.FindAll()).Returns(mockResultCollection.Object);

   var queryer = new ActiveDirectoryQueryer(mockSearcher.Object);
   queryer.GetAllMailEntries();
   Assert.AreEqual(1, queryer.MailEntries.Count());
   var entry = queryer.MailEntries.Single();
   Assert.AreEqual(name, entry.Name);
   Assert.AreEqual(mailAddress, entry.EmailAddress);
}

拥有这么多接口和包装类是否正常? (包装器是必要的,因为.NET类型不能实现我的接口。)

1 个答案:

答案 0 :(得分:2)

我认为我的问题是过于密切地反映.NET结构。我不应该将每种.NET类型一直包装下来,直到我得到原语。相反,我应该抓住第一个机会尽快删除所有依赖项。在这种情况下,它使用<canvas id="matrix" style="margin-left:-20px;margin-bottom:-5px;"></canvas> 类和DirectorySearcher方法。

FindAll会返回DirectorySearcher.FindAll,而不是考虑我的&#34;包装&#34; class只是.NET类型的一个适配器,我应该更多地使用它。

忽略SearchResultCollection和其他不必要的代码的实现,我的包装器看起来像这样:

IDisposable

相反,我应该抓住机会在这里停止所有依赖。我不必返回.NET类型,甚至只需返回.NET类型的包装器,我现在可以使用此接口返回我想要的任何内容。 IE:如果我想从public interface IDirectorySearcher : IDisposable { ISearchResultCollection FindAll(); } class DirectorySearcherWrapper : IDirectorySearcher { DirectorySearcher mDirectorySearcher; DirectorySearcherWrapper(DirectorySearcher pDirectorySearcher) { mDirectorySearcher = pDirectorySearcher; } public static IDirectorySearcher Wrap(DirectorySearcher pDirectorySearcher) { return new DirectorySearcherWrapper(pDirectorySearcher); } public ISearchResultCollection FindAll() { return SearchResultCollectionWrapper.Wrap(mDirectorySearcher.FindAll()); } } 方法获得的是一堆FindAll s,那么只返回。

我的代码变为:

ActiveDirectoryUser

public interface IDirectorySearcher : IDisposable { IEnumerable<ActiveDirectoryUser> FindAll(); } class DirectorySearcherWrapper : IDirectorySearcher { DirectorySearcher mDirectorySearcher; DirectorySearcherWrapper(DirectorySearcher pDirectorySearcher) { mDirectorySearcher = pDirectorySearcher; } public static IDirectorySearcher Wrap(DirectorySearcher pDirectorySearcher) { return new DirectorySearcherWrapper(pDirectorySearcher); } public IEnumerable<ActiveDirectoryUser> FindAll() { return mDirectorySearcher .FindAll() .Cast<SearchResult>() .Select(result => result.GetDirectoryEntry()) .Select(/*BuildNewADUser*/) .ToList(); } } 方法变得简单:

GetAllMailEntries

单元测试成为:

public void GetAllMailEntries() {
   MailEntries = mSearcher.FindAll();
}