This has to be trivial, but being quite new to TDD, I can't figure out the proper way to do it.
Let's say we have a WinForms application that, as part of its startup process, looks for DLLs in given folders to dynamically populate its main menu bar.
Those DLLs are mere plug-ins: they all implement an interface, and they all will be displayed as menu entries. It's really as simple as that.
Let's call each DLL a Module
.
Now, I thought "Hey, Steve, you're gonna have to mock this Module
objects. Let's define an interface named IModule
, and make the Module
class implement it. That way you can mock them as you please".
So, let me write a couple of lines of code, specifically IModule
and Module
(I'm writing this straight without the support of a compiler, so it might not compile):
<!-- language: c# -->
public interface IModule {
string Id { get; }
string DisplayName { get; }
}
public class Module : IModule {
public string Id { get; }
public string DisplayName { get; }
public string Path { get; private set; }
public Module(FileInfo path) {
Path = path.FullName;
}
}
And, while we are at it, let's implement the class that will do the actual loading:
public class ModuleLoader {
IEnumerable<DirectoryInfo> SearchPaths;
public ModuleLoader(IEnumerable<DirectoryInfo> searchPaths) {
SearchPaths = searchPaths;
}
public IEnumerable<IModule> LoadModules() {
var modules = SearchPaths
.Where(dir => dir.Exists)
.SelectMany(dir => dir.GetFiles("*.dll").Select(dll => new Module(dll)));
return modules;
}
}
Here comes the problem: LoadModules()
won't compile because of—from what I could gather—variance issues.
The error message is as follows:
Cannot implicitly convert type System.Collections.Generic.IEnumerable<Module> to System.Collections.Generic.IEnumerable<IModule>
Which is the same as this question.
Now, there must be something trivial that escapes me. To the best of my knowledge, making LoadModules()
return IEnumerable<IModule>
qualifies as a Good Thing™. Changing the return type to IEnumerable<Module>
of course makes the code compile, but it only moves the problem to the unit tests.
I'm a bit confused, and maybe I'm doing it all wrong (I apologize if that's the case).
So, how would you go about this? How do I make Module
mockable?
You should replace var
with IEnumerable<IModule>
and replace new Module(dll)
with (IModule) (new Module(dll))