Hi!
I have a class which looks like this:
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);
}
}
As you can see there is a method (GetItems) and I want to Unit Test this method. The method just tries to find a value in the MEF container and return eather the result of the found item or a default value. (So I dont want to test the MEF stuff [I think someone else have allready done this :)] I just want to test if the method correctly returns the value of the Default Item or the Imported Item regarding the parameter)
And here is my problem. I can create a test to check the default value but I am not able to create a test with a stub for a specific "Authority" assuming the Directory (For the DirectoryCatalog) is empty. So is there a way to "inject" a Stub[IKnownAuthority] to the allready composed catalog from the test method?
I have tried something like this:
[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() };
}
}
The StubAuthority_02 won't find it's way to the _KnownAuthorities collection. First I thought the reason is the fact that I compose the parts in the constructor and then again (Recomposition without the Attributes set) in the test method. But if I remove the
container.ComposeParts(this);
line from the ItemCollector constructor the StubAuthority_02 isn´t there anyway. I am sure this is just a problem in understanding but I couldn´t figure it out...
The merging of two containers the way you are trying to do is not possible. Whenever you call the constructor of your ItemsCollector
you create a CompositionContainer
and call Compose/ComposeParts. This causes MEF to build its internal structures from the catalogs you provided to be able to provide you with the correct values once you ask for them. But since you said in the unit test your DirectoryCatalog is empty, MEF will not find any exports to fill your imports.
In case of your unit test the second CompositionContainer
you would provide your an instance of ItemsCollector
as already constructed. This would cause MEF not to resolve any imports for it, but just take it as a given.
So if you want to stick to that construct with the encapsulation of the CompositionContainer
within the ItemsCollector
your only way to unit test it properly would be to fill that directory in both cases.
Another option would be to move the CompositionContainer
outside of your ItemsCollector
and provide the catalogs to your container. The catalogs would be different in case of your application code and unit testing code.
If you don't want to move the CompositionContainer
outside your class, you can also just pass a catalog to your ItemsCollector
:
... // Your ItemsCollector constructor
public ItemsCollector(ComposablePartCatalog catalog)
{
var container = new CompositionContainer(catalog);
Container.Compose(this);
}
In case of your application usage you could create your collector instance like this:
// application code
var directoryCatalog = new DirectoryCatalog(
Path.Combine(Environment.CurrentDirectory, AUTHORITY_FOLDER));
var collector = new ItemsCollector(directoryCatalog);
In case of your unit test you could do something like this:
// unit test code
var catalog = new TypeCatalog(new Type[] {typeof(StubAuthority_02)});
var collector = new ItemsCollector(catalog);
If you don't like the MEF types in the public API of your own types create your own type to wrap the catalogs.
Hope this helps - will provide code if you think it would explain it better.