I am changing a project of mine to use plug-ins for certain components (called
FeedReaders
), instead of having them defined in the main solution. I am following the technique present
here by Duke of Haren. I am also using
NUnit as by unit testing framework.
So I have created a
FeedReaderPool
class which will find and load the plug-in DLLs. It does so using this code:
private IEnumerable<INewFeedReader> LoadPluginDlls()
{
_fs.Directory.GetFiles(PluginDirectory)
.Where(fname => fname.EndsWith(".dll"))
.ToList()
.ForEach(dll => Assembly.LoadFile(_fs.Path.GetFullPath(dll)));
var pluginMasterType = typeof(INewFeedReader);
var pluginTypes =
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(ass => ass.GetTypes())
.Where(t => t.IsClass && pluginMasterType.IsAssignableFrom(t))
.ToList();
var plugins =
pluginTypes
.Select(pit => (INewFeedReader)Activator.CreateInstance(pit));
return plugins;
}
The
ToList()
has been added just for debugging.
Now, I have a unit test,
ValidateFailsWhenNoPluginsLoaded
, that ensures when the
FeedReaderPool
runs the above code that it will throw an exception if it returns no plugins. I am using the
System.IO.Abstractions package to create a mock
FileSystem
so that
PlugInDirectory
will be a valid directory with no files in it.
[Test]
public void ValidatesFailsWhenNoPluginsLoaded()
{
var testName = "testPool";
var testDir = @"C:\testdir";
var testFileSystem = new MockFileSystem();
testFileSystem.AddDirectory(testDir);
var expectedMessage = $"{testName}: PluginDirectory does not contain any plugins [";
var pool = new FeedReaderPool(testName, testFileSystem)
{
PluginDirectory = testDir,
};
var ex = Assert.Throws<InvalidOperationException>(() => pool.ValidateIsReadyToWork(), "Improperly initialized pool did not fail validation");
Assert.That(ex.Message, Does.StartWith(expectedMessage), "Incorrect exception message for missing plugins");
}
When I run this test on its own it works. But when I run it with other unit tests it fails, indicating that it did find DLL to load.
I inserted the
ToList()
in
LoadPluginDlls
and set a breakpoint. What I see is that an
INewFeedReaderProxy
type has been found in the
DynamicProxyGenAssembly2
assembly. My other unit tests are creating mock
INewFeedReader
objects and this is causing my
LoadPluginDlls
to accept the Moq-generated Types as valid plugins.
I know I could change
LoadPluginDlls
to exclude
Types
created by
Moq (
.Where(ass => !ass.AssemblyQualifiedName.StartsWith("Castle")
), or something similar. But that seems wrong to me. Why should my 'production' code have to know about unit testing artifacts? It seems to me that there should be a way, in my unit tests, to clear out any of these generated
Types
before calling my test.
Does anyone know how I would do that?
What I have tried:
Moq documentation does not contain an answer, or I just can't tell how to find it (probably the later).
Could not find an answer through Google.
Or is there a more generic way to ensure that LoadPluginDlls only loads the DLLs I want? Can I tell that the Types I find are not from a valid directory, or something like that?