Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / testing

Interfaces, Testing and Dependency Injection

5.00/5 (1 vote)
24 Oct 2014CPOL2 min read 5.9K  
Interfaces, testing and dependency injection

Introduction

When I first started out as developer, the usefulness of interfaces was one of the things I never quite worked out. However, as I got more into design and testing then everything suddenly became much more clear. By developing to interfaces, we can make our applications much more extendable, easier to test, and making changes to the functionality much easier, simply by removing the dependency on a particular service.

Here, I will demonstrate using interfaces to support testing through the use of dependency injection. To do this, I will use the principle of a simple logger. Imagine we have written an application with a logger that logs errors to a file system or database.

C#
class Log
    {
        public void WriteMessage(string message)
        {
            // Write message out to database or file system
        }
    }

Now, if we have created a dependency on the logger itself, we straight away hit two problems. Firstly, we may need the file path or database to be in place before we can run our application. Secondly, we cannot write a unit test for this application as a unit test should explicitly test the code, and not the integration with the file system/database. Further to this, we also cannot test the code is working without checking the output to see if something is written.

C#
class DoWorkClassA
    {
        public void DoWorkClassA(Log log)
        {
            int a = 1;
            int b = 2;

            if (a != b)
            {
                log.WriteMessage("Value mismatch");
            }
        }
    }

The solution to this is to extrapolate an interface from our logger and use this as our dependency instead, as follows:

C#
interface ILog
    {
        void WriteMessage(string message);
    }

We can then change our class objects to use the interface rather than the log object itself.

C#
public void DoWorkClassA(ILog log)
        {
            int a = 1;
            int b = 2;

            if (a != b)
            {
                log.WriteMessage("Value mismatch");
            }
        }

Now if we write a unit test [or want to provide alternate logger implementations to make development easier], it’s as simple as providing a second implementation of the ILog interface and injecting this into our classes.

C#
class LogForTesting : ILog
    {
        public void WriteMessage(string message)
        {
            Console.WriteLine(message);
        }
    }

Now, we can write a test, and inject our test log into the class we’re testing, so we can run the test without the need for the filesystem, the database or to use the ‘real’ log implementation that would be with the release version. This example also gives us the added benefit that we can output what our log is doing to the console so can see in real-time what our application is logging.

C#
[TestFixture]
    class Test
    {
        ILog log;

        [TestFixtureSetUp]
        public void SetUp()
        {
            this.log = new LogForTesting();
        }

        [Test]
        public void TestClassADoesWork()
        {
            DoWorkClassA classA = new DoWorkClassA(this.log);

            // Do test
        }
    }

Tagged: C#, dependency injection, interface, unit testing

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)