使用 MEF 导入的 C# 单元测试方法
C# Unit Test method with MEF Imports
嗨!
我有一个 class 看起来像这样:
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 容器中查找一个值,然后 return 将找到的项目的结果或默认值组合在一起。 (所以我不想测试 MEF 的东西 [我认为其他人已经这样做了 :)] 我只想测试该方法是否正确 return 是关于参数的默认项或导入项的值)
这是我的问题。我可以创建一个测试来检查默认值,但假设目录(对于 DirectoryCatalog)为空,我无法为特定 "Authority" 创建一个带有存根的测试。那么有没有一种方法可以从测试方法"inject"一个Stub[IKnownAuthority]到所有已经组成的目录?
我试过这样的方法:
[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 无论如何都不存在。我确定这只是理解上的问题,但我无法弄清楚...
无法按照您尝试的方式合并两个容器。每当您调用 ItemsCollector
的构造函数时,您都会创建一个 CompositionContainer
并调用 Compose/ComposeParts。这会导致 MEF 根据您提供的目录构建其内部结构,以便能够在您请求时为您提供正确的值。但是由于您在单元测试中说过您的 DirectoryCatalog 是空的,MEF 将找不到任何导出来填充您的导入。
在单元测试的情况下,第二个 CompositionContainer
您将提供一个已经构建的 ItemsCollector
实例。这将导致 MEF 不为其解析任何导入,而只是将其视为给定。
因此,如果您想坚持将 CompositionContainer
封装在 ItemsCollector
中的结构,正确进行单元测试的唯一方法是在两种情况下都填充该目录。
另一种选择是将 CompositionContainer
移出您的 ItemsCollector
并将目录提供给您的容器。对于您的应用程序代码和单元测试代码,目录会有所不同。
如果您不想将 CompositionContainer
移到您的 class 之外,您也可以将目录传递给您的 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);
如果您不喜欢自己类型 public API 中的 MEF 类型,请创建自己的类型来包装目录。
希望这对您有所帮助 - 如果您认为代码可以更好地解释它,我们将提供代码。
嗨!
我有一个 class 看起来像这样:
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 容器中查找一个值,然后 return 将找到的项目的结果或默认值组合在一起。 (所以我不想测试 MEF 的东西 [我认为其他人已经这样做了 :)] 我只想测试该方法是否正确 return 是关于参数的默认项或导入项的值)
这是我的问题。我可以创建一个测试来检查默认值,但假设目录(对于 DirectoryCatalog)为空,我无法为特定 "Authority" 创建一个带有存根的测试。那么有没有一种方法可以从测试方法"inject"一个Stub[IKnownAuthority]到所有已经组成的目录?
我试过这样的方法:
[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 无论如何都不存在。我确定这只是理解上的问题,但我无法弄清楚...
无法按照您尝试的方式合并两个容器。每当您调用 ItemsCollector
的构造函数时,您都会创建一个 CompositionContainer
并调用 Compose/ComposeParts。这会导致 MEF 根据您提供的目录构建其内部结构,以便能够在您请求时为您提供正确的值。但是由于您在单元测试中说过您的 DirectoryCatalog 是空的,MEF 将找不到任何导出来填充您的导入。
在单元测试的情况下,第二个 CompositionContainer
您将提供一个已经构建的 ItemsCollector
实例。这将导致 MEF 不为其解析任何导入,而只是将其视为给定。
因此,如果您想坚持将 CompositionContainer
封装在 ItemsCollector
中的结构,正确进行单元测试的唯一方法是在两种情况下都填充该目录。
另一种选择是将 CompositionContainer
移出您的 ItemsCollector
并将目录提供给您的容器。对于您的应用程序代码和单元测试代码,目录会有所不同。
如果您不想将 CompositionContainer
移到您的 class 之外,您也可以将目录传递给您的 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);
如果您不喜欢自己类型 public API 中的 MEF 类型,请创建自己的类型来包装目录。
希望这对您有所帮助 - 如果您认为代码可以更好地解释它,我们将提供代码。