Search code examples
c#.net-3.5mockingcovariancecontravariance

How to properly mock in .NET 3.5


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?


Solution

  • You should replace var with IEnumerable<IModule> and replace new Module(dll) with (IModule) (new Module(dll))