使用MEF导入的C#单元测试方法

时间:2015-12-09 16:58:34

标签: c# unit-testing mef

嗨!

我有一个看起来像这样的课程:

public class ItemCollector {
  public IKnownAuthority _DefaultAuthority = new DefaultAuthority();

  [ImportMany(typeof(IKnownAuthority))]
  private IEnumerable<Lazy<IKnownAuthority, IKnownAuthorityMetadata>> _KnownAuthorities { get; set; }

  public ItemCollector() {
    var catalog = new AggregateCatalog();
    catalog.Catalogs.Add(new DirectoryCatalog(
                Path.Combine(Environment.CurrentDirectory, AUTHORITY_FOLDER)));

    var container = new CompositionContainer(catalog);
    container.ComposeParts(this);
  }

  public IEnumerable<Items> GetItems(string Auth) {
    var Authority = _KnownAuthorities.FirstOrDefault(x => 
                            x.Metadata.AuthorityName.ToLowerInvariant() == 
                            Auth.ToLowerInvariant());

    if (Authority == null)
      return _DefaultAuthority.GetItems(Address);

    return Authority.Value.GetItems(Address);
  }
}

正如您所看到的,有一种方法( GetItems ),我希望单元测试此方法。该方法只是尝试在MEF容器中查找值并返回搜索找到的项的结果或默认值。 (所以我不想测试MEF的东西[我认为其他人已经完成了这个:)]我只想测试方法是否正确返回默认项的值或关于参数的导入项目)

这是我的问题。我可以创建一个测试来检查默认值但是我无法使用特定“权限”的存根创建测试,假设目录(对于DirectoryCatalog)为空。那么有没有办法从测试方法“注入”Stub [IKnownAuthority]到allready组成的目录?

我尝试过这样的事情:

[TestMethod]
public void GetItems_Ok()
{
  ItemsCollector collector = new ItemsCollector();
  collector._DefaultAuthority = new StubDefaultAuthority_01();

  CompositionContainer container = new CompositionContainer();
  container.ComposeExportedValue<IKnownAuthority>(new StubAuthority_02());
  container.ComposeParts(collector);

  var list = collector.GetItems("testing.test"));      
  Assert.IsTrue(list.Count() > 0);
}

[Export(typeof(IKnownAuthority))]
[ExportMetadata("AuthorityName", "testing.test")]
public class StubAuthority_02 : IKnownAuthority
{
  public IEnumerable<Items> GetItems(string Auth) {
    return new List<Items>() { new Items() };
  }
}

StubAuthority_02找不到它到_KnownAuthorities集合的方式。首先,我认为原因是我在构造函数中组合了部件,然后再次(在没有设置属性的情况下重构)在测试方法中。但是,如果我删除

container.ComposeParts(this);
来自ItemCollector构造函数的

行无论如何都不存在StubAuthority_02。我确信这只是一个理解上的问题,但我无法弄清楚......

1 个答案:

答案 0 :(得分:0)

以您尝试的方式合并两个容器是不可能的。每当您调用ItemsCollector的构造函数时,您都会创建一个CompositionContainer并调用Compose / ComposeParts。这会导致MEF从您提供的目录中构建其内部结构,以便在您提出要求时能够为您提供正确的值。但是,既然您在单元测试中说您的DirectoryCatalog为空,MEF将找不到任何导出来填充您的导入。

如果您的单元测试第二个CompositionContainer,您将提供已构建的ItemsCollector实例。这会导致MEF不为它解析任何导入,而只是将其作为给定的。

因此,如果你想坚持使用CompositionContainer ItemsCollector封装{@ 1}}你唯一的方法进行单元测试就可以在两种情况下都填充该目录。

另一种选择是将CompositionContainer移到ItemsCollector之外,并将目录提供给您的容器。在您的应用程序代码和单元测试代码的情况下,目录会有所不同。

如果您不想将CompositionContainer移到课堂外,您也可以将目录传递给ItemsCollector

... // Your ItemsCollector constructor
public ItemsCollector(ComposablePartCatalog catalog)
{
    var container = new CompositionContainer(catalog);
    Container.Compose(this);
}

如果您的应用程序使用,您可以像这样创建收集器实例:

// application code
var directoryCatalog = new DirectoryCatalog(
        Path.Combine(Environment.CurrentDirectory, AUTHORITY_FOLDER));
var collector = new ItemsCollector(directoryCatalog);

如果您进行单元测试,您可以执行以下操作:

// unit test code
var catalog = new TypeCatalog(new Type[] {typeof(StubAuthority_02)});
var collector = new ItemsCollector(catalog);

如果您不喜欢自己类型的公共API中的MEF类型,请创建自己的类型来包装目录。

希望这会有所帮助 - 如果您认为可以更好地解释它,将提供代码。