How to pretend a class implements an interface in C#

C# interfaces are great, and since C# 8.0 they’re even cooler than they were before. Personally, I find them great for unit testing since I can use dependency injection to have my code reference a vital object by its interface, then pass in either a real or simulated version of that object depending on whether this is a unit test or real use. But how are we supposed to simulate a class that doesn’t implement an interface? I have just the trick!

Ideal simulation situation (no tricks needed)

The code below is an example of the ideal situation where you’re using a class Library.Foo that already implements an interface Library.IFoo. Our application’s method MyAppCode.UseFoo.CallBar takes a Library.IFoo instance called usuallyFoo as input. During normal execution of the application usuallyFoo will be an instance of Library.Foo, but during unit testing we’ll pass in an instance of our custom MyTestCode.SimFoo class instead.

namespace Library
{
    public interface IFoo
    {
        void Bar();
    }

    public class Foo : IFoo
    {
        public void Bar()
        {
            // library implementation
        }
    }
}

namespace MyAppCode
{
    public static class UseFoo
    {
        public static void CallBar(Library.IFoo usuallyFoo)
        {
            usuallyFoo.Bar();
        }
    }
}

namespace MyTestCode
{
    class SimFoo : Library.IFoo
    {
        public void Bar()
        {
            // simulated implementation
        }
    }

    static class TestUseFoo
    {
        public static void TestCallBar()
        {
            MyAppCode.UseFoo.CallBar(new SimFoo());
        }
    }
}

Time to pretend

I discovered this trick because my current job involves the System.IO.Ports.SerialPort class a lot, and that class does not implement an interface conducive for testing. Rather than a contrived abstract example with more Foo and Bar, let’s base this section on the real SerialPort class.

First, we need to know what members of the SerialPort class our application is using. If our code looks like the code below, then we need three members: IsOpen, Open(), and ReadLine().

using System.IO.Ports;

namespace MyAppCode
{
    public static class MyClass
    {
        public static string ReadFromSerialPort(SerialPort serialPort)
        {
            if (!serialPort.IsOpen)
                serialPort.Open();

            return serialPort.ReadLine();
        }
    }
}

Refactor the code above by making an interface MyAppCode.ISerialPort that declares the three members of System.IO.Ports.SerialPort that we need.

namespace MyAppCode
{
    public interface ISerialPort
    {
        bool IsOpen { get; }
        void Open();
        string ReadLine();
    }

    public static class MyClass
    {
        public static string ReadFromSerialPort(ISerialPort serialPort)
        {
            if (!serialPort.IsOpen)
                serialPort.Open();

            return serialPort.ReadLine();
        }
    }
}

Now time for the trick. We’ll make a new class MyAppCode.MySerialPort that inherits from System.IO.Ports.SerialPort and implements MyAppCode.ISerialPort.

using System.IO.Ports;

namespace MyAppCode
{
    public class MySerialPort : SerialPort, ISerialPort
    {

    }
}

That’s all we need! MyAppCode.MySerialPort doesn’t need any code to implement MyAppCode.ISerialPort because its base class already has members with the signature of each member of MyAppCode.ISerialPort.

We can now make and use a simulated serial port like so:

using System;

namespace MyTestCode
{
    public class SimSerialPort : MyAppCode.ISerialPort
    {
        public bool IsOpen { get; private set; } = false;

        public void Open() => IsOpen = true;

        public string ReadLine()
        {
            if (!IsOpen)
                throw new InvalidOperationException();

            return "Hello simulated world!";
        }
    }

    static class TestMyClass
    {
        public static void TestReadFromSerialPort()
        {
            MyAppCode.MyClass.ReadFromSerialPort(new SimSerialPort());
        }
    }
}

Remember to /// <inheritdoc/>

There’s one last change to make. Since in our code we’ll now be referencing MyAppCode.ISerialPort instead of System.IO.Ports.SerialPort we’re not going to see all the great documentation we’re used to in Visual Studio when we mouse over members like Open() and ReadLine(). Not to worry, though, there’s a very simple fix. Just add /// <inheritdoc cref="SerialPort.X"/> to each of ISerialPort‘s members, where X is the member name. Here’s our final interface:

using System.IO.Ports;

namespace MyAppCode
{
    public interface ISerialPort
    {
        /// <inheritdoc cref="SerialPort.IsOpen"/>
        bool IsOpen { get; }

        /// <inheritdoc cref="SerialPort.Open"/>
        void Open();

        /// <inheritdoc cref="SerialPort.ReadLine"/>
        string ReadLine();
    }
}

And here’s a screenshot of how the documentation looks when we mouse over something:

Restricting a class

This trick mostly restricts a class to only those members that are declared in the interface that you create. This could be useful if the class contains members that you don’t want people to use for whatever reason. However, I say “mostly restricts” since the the full class can still be accessed via casting, like in the code below.

public static void Cast(ISerialPort serialPort)
{
    var cast = serialPort as SerialPort;
    // Close() is not a member of ISerialPort
    cast!.Close();
}

Sealed classes

Obviously, this trick won’t work on a class marked sealed since such a class cannot be inherited. However, you can still implement your interface through a sealed class by using composition like in the code below. The only downside is that you’ll probably get tired of writing all those lambdas.

using System.IO.Ports;

namespace MyAppCode
{
    class CompositionSerialPort : ISerialPort
    {
        readonly SerialPort _serialPort = new();

        public bool IsOpen => _serialPort.IsOpen;

        public void Open() => _serialPort.Open();

        public string ReadLine() => _serialPort.ReadLine();
    }
}

Conclusion

I’ll admit, this trick is far from groundbreaking, but I still think it’s cool. When I first tried this and found that it actually compiled I had a big sense of “What? No way!” since I had thought that I would need to explicitly define all of ISerialPort‘s members on MySerialPort.

Source code for these examples is freely available here.