3

I have a class that internally uses an interface that is defined externally. Testing this class becomes difficult since I need to mock out this interface but I am required to reference the external library (which may be quite big) since the test is in a separate test project. Are there any ways to decouple this interface so that I can mock out the interface and test it without having to include the external project or is it an inherit problem of how my solution is setup?

8
  • Why do you need to mock the interface? Can you change the external library? Do you use anything else from that library?
    – David Arno
    Commented Dec 18, 2017 at 10:09
  • I need to mock the interface since the the class calls a method from the interface. I do not use anything else from the library in the testing project. Commented Dec 18, 2017 at 10:12
  • 1
    Can you change the external library?
    – David Arno
    Commented Dec 18, 2017 at 10:13
  • 5
    Then your likely best bet is to use the stairway pattern and move such interfaces out into their own project/assembly. See softwareengineering.stackexchange.com/questions/285276/… for an example of this.
    – David Arno
    Commented Dec 18, 2017 at 10:28
  • 4
    Create your own wrapper interface and inject it into the implementation under test. Then, for unit tests you provide a stub of this interface. Is it feasible to refactor your code like this in your project? Commented Dec 18, 2017 at 10:58

3 Answers 3

3

Bob Martin's post The Clean Architecture recommends that your class's dependencies should point inward, not outward.

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.

Followed to the letter it means that your inner library would not even reference an outer library at all. Instead, it would define its own interfaces, and then additional libraries would create compositions that combine your inner classes with implementations of the interfaces it depends on.

(I've never seen this done. I'm not deriding it as impractical. It sounds awesome and I want to implement it on a large scale before I die.)

On a smaller scale you can get some of the benefits from ensuring that your classes depend on interfaces that you define, not on externally defined interfaces.

Using a really simple example, you might define your own logging interface, like

public interface ILogger
{
    void LogException(Exception ex); 
}

Now if a class needs to log exceptions, it depends only on that interface, not on a Microsoft interface or a Windsor interface, or whatever. Then if you're using some external logging library, you create an implementation of ILogger that wraps a call to that library.

What's great about this is that now you're achieving interface segregation. Your classes are less likely to depend on whatever giant interfaces the world hands to them, and more likely to depend on small interfaces that provide exactly what that class needs.

Then, going back to your original question, those interfaces will be easy to mock. They might even be so small that your find it easier to use simple test double classes instead of a mocking framework. I like using Moq but sometimes all of those setups become difficult to read. If I can just as easily write a class like PermissionsValidatorThatAlwaysReturnsTrue then I might go ahead and write the few extra lines of code. The time might be saved when someone sees that class used in a test, navigates it, and sees what it does, as opposed to trying to figure out which Mock is being used and how it's set up.

1

Maybe you could use a plug-in framework like MEF (Managed Extensibility Framework) to decouple the DLL/Project. But as @Emerson suggests, creating a wrapper may be more feasible.

1

The best answer is to reference the assembly that contains the interface. Sure, it's big, but so what-- this won't trigger any additional assembly loading because the external library would have to be loaded anyway, in order for the class under test to operate.

But if for some reason you really don't want to set that reference, it's possible to do what you want with a bit of reflection. Here is an example that supports a method under test that requires one parameter that conforms to the non-referenced interface.

First, add this extension method:

public static class ExtensionMethods
{
    public static void CallWithMockedParameter(this object objectUnderTest, string methodName, object valuesForMockedParameter)
    {
        var typeUnderTest = objectUnderTest.GetType();
        var methodUnderTest = typeUnderTest.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
        var parameterUnderTest = methodUnderTest.GetParameters()[0];
        var parameterType = parameterUnderTest.ParameterType;
        var typeToPass = parameterType.Assembly.GetTypes().Where( a => parameterType.IsAssignableFrom(a) && a.IsClass).Single();
        var objectToPass = Activator.CreateInstance(typeToPass);
        foreach (var propertyToCopy in valuesForMockedParameter.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            typeToPass.GetProperty(propertyToCopy.Name).SetValue(objectToPass, propertyToCopy.GetValue(valuesForMockedParameter));
        }
        methodUnderTest.Invoke(objectUnderTest, new[] { objectToPass });
    }
}

This bit of code finds the type for the class under test, finds the method of interest, identifies the type of its parameter, and finds a concrete class that matches the parameter's interface. It then instantiates the concrete class and populates it with values that you provided in an anonymous type by mapping properties one by one.

With the above, you can replace this normal call:

//Normal execution
ClassUnderTest o1 = new ClassUnderTest();
IExternalInterface a1 = new ExternalClass { Text = "Normal" };
o1.MethodUnderTest(a1);

With this:

//Mocked
var o2 = new ClassUnderTest();
var a2 = new { Text = "Mocked" };
o2.CallWithMockedParameter(nameof(o2.MethodUnderTest), a2);

See my working example on DotNetFiddle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.