81

I've started the very long and arduous quest to learn and apply TDD to my workflow. I'm under the impression that TDD fits in very well with IoC principles.

After browsing some of TDD tagged questions here in SO, I read it's a good idea to program against interfaces, not objects.

Can you provide simple code examples of what this is, and how to apply it in real use cases? Simple examples is key for me (and other people wanting to learn) to grasp the concepts.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
  • 7
    This is kind of an OOP thing rather than a C# specific thing... – Billy ONeal Dec 16 '10 at 00:44
  • 2
    @Billy ONeal: Could be, but since interfaces act differently in say C#/Java, I'd like to learn on the language I'm most familiar with first. –  Dec 16 '10 at 00:46
  • 11
    @Sergio Boombastic: the concept of programming to an *Interface* has nothing to do with an `interface` in either Java or C#. In fact, when the book from which this quote is taken was written, neither Java nor C# even existed. – Jörg W Mittag Dec 16 '10 at 01:25
  • 1
    @Jörg: well, it has *something* to do with it. Interfaces in recent OOP languages are certainly intended to be used as described in the quote. – Michael Petrotta Dec 16 '10 at 01:45
  • 1
    @Michael Petrotta: They aren't very good at it, though. For example, the *Interface* of `List` says that after `add` ing an element to the list, the element is in the list and the length of list increases by `1`. Where does it actually say that in the [ `interface List` ](http://Download.Oracle.Com/javase/7/docs/api/java/util/List.html#add)? – Jörg W Mittag Dec 16 '10 at 02:51
  • @Jörg: An `interface` aids in the definition of, but is not sufficient for, a Capital-I Interface. How does that sound? – Michael Petrotta Dec 16 '10 at 02:57

7 Answers7

86

Consider:

class MyClass
{
    //Implementation
    public void Foo() {}
}

class SomethingYouWantToTest
{
    public bool MyMethod(MyClass c)
    {
        //Code you want to test
        c.Foo();
    }
}

Because MyMethod accepts only a MyClass, if you want to replace MyClass with a mock object in order to unit test, you can't. Better is to use an interface:

interface IMyClass
{
    void Foo();
}

class MyClass : IMyClass
{
    //Implementation
    public void Foo() {}
}

class SomethingYouWantToTest
{
    public bool MyMethod(IMyClass c)
    {
        //Code you want to test
        c.Foo();
    }
}

Now you can test MyMethod, because it uses only an interface, not a particular concrete implementation. Then you can implement that interface to create any kind of mock or fake that you want for test purposes. There are even libraries like Rhino Mocks' Rhino.Mocks.MockRepository.StrictMock<T>(), which take any interface and build you a mock object on the fly.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 14
    Side note: It doesn't always *have* to be an actual interface in the sense the language you are using uses the word. It could as well be an abstract class which is not entirely unreasonable, given the restrictions Java or C# place on inheritance. – Joey Dec 16 '10 at 00:54
  • 2
    @Joey: You could use an abstract class, yes, but if you do that you have to design the class to be intended to be inheritable, which can be a bit more work. Of course, in languages like C++ which don't have a language-level interface you do exactly that -- make an abstract class. Note though that in Java and C# it's still better to use the interface, because you can inherit from multiple interfaces, but only one class. Allowing multiple interface inheritance encourages you to make your interfaces smaller, which is a good thing TM :) – Billy ONeal Dec 16 '10 at 00:56
  • 5
    @Joey: you don't even have to use *anything*. In Ruby, for example, the *Interface* against which you program is typically only documented in English, if at all. That doesn't make it any less of an *Interface*, though. Conversely, slapping `interface` keywords all over your code, doesn't mean it's programming against *Interface* s. Thought experiment: Take horrible tightly coupled incohesive code. For every class, simply copy&paste it, delete all method bodies, replace the `class` keyword with `interface` and update all references in the code to that type. Is the code now any better? – Jörg W Mittag Dec 16 '10 at 01:31
  • Jörg, that's a nice way of looking at it, indeed :-) – Joey Dec 16 '10 at 02:46
  • Beyond mocking, hiding implementation details allows you to change the code underneath to improve performance, etc. without changing the calling code. Java does this with List, as an example. The implementation underneath can be an array, stack, etc. – Brian Pan Dec 17 '10 at 21:48
  • Can someone post a Python version of this? Thanks. – Jeffrey Jose Dec 27 '10 at 08:03
  • @jeffjose: Python doesn't do things like this because it is [Duck typed](http://en.wikipedia.org/wiki/Duck_typing). Unfortunately, I'm not familiar enough with Python to write you a comparable example there. Perhaps you should ask that as another question. – Billy ONeal Dec 27 '10 at 16:04
  • So does this mean that for every class I will need to first define an interface and make the class implement it? Other than testing, what benefit does this have? Ok, so the interface can be replaced with another but in real life it rarely happens for most class implementations. Can someone explain this? – bobbyalex Aug 27 '13 at 09:02
  • @Bobby: That depends on where you draw test boundaries. Sure, you can have tests for every class, and if that's what you're doing, then yes, you'll need an interface per class. You'll at least need to be able to replace the real class with a fake or mock to isolate it from others. – Billy ONeal Aug 27 '13 at 21:26
  • @Bobby: Note of course that depending on the language that explicit interfaces might not be necessary. "interface" in this case doesn't mean "interface" in the C#/Java sense, it means interface in the "this is a component on top of which other things are built" sense. – Billy ONeal Aug 27 '13 at 21:27
  • Why cant you just create an instance of the concrete class for testing? – bobbyalex Aug 28 '13 at 00:41
  • @Bobby: Because sometimes the concrete class is expensive, or has other attributes which are undesirable in a test harness. Sometimes you want to isolate functional units when testing so that a break in one class doesn't break all the tests. Etc. – Billy ONeal Aug 28 '13 at 01:12
  • So does this principle still apply if you dont do mock testing or automated testing? Seems like an overhead to me and very non intuitive. Interfaces for each class when it will be implemented only by one class ever? Really? – bobbyalex Aug 29 '13 at 02:34
  • @Bobby: No, the intent is that most of the time you can define some interface which is a subset of the class. To concertize it: Let's say you have some code that takes a `List` -- but you only walk over the collection once. You should take an `IEnumerable` instead in such cases. "Code against an interface, not an object" is just like any other design rule of thumb. Design is about balancing competing ideas to minimize the badness in a code base. No set of "rules" can teach you design, they can only show you attributes of systems which have good designs. – Billy ONeal Aug 29 '13 at 04:20
19

It's all a matter of intimacy. If you code to an implementation (a realized object) you are in a pretty intimate relationship with that "other" code, as a consumer of it. It means you have to know how to construct it (ie, what dependencies it has, possibly as constructor params, possibly as setters), when to dispose of it, and you probably can't do much without it.

An interface in front of the realized object lets you do a few things -

  1. For one you can/should leverage a factory to construct instances of the object. IOC containers do this very well for you, or you can make your own. With construction duties outside of your responsibility, your code can just assume it is getting what it needs. On the other side of the factory wall, you can either construct real instances, or mock instances of the class. In production you would use real of course, but for testing, you may want to create stubbed or dynamically mocked instances to test various system states without having to run the system.
  2. You don't have to know where the object is. This is useful in distributed systems where the object you want to talk to may or may not be local to your process or even system. If you ever programmed Java RMI or old skool EJB you know the routine of "talking to the interface" that was hiding a proxy that did the remote networking and marshalling duties that your client didn't have to care about. WCF has a similar philosophy of "talk to the interface" and let the system determine how to communicate with the target object/service.

** UPDATE ** There was a request for an example of an IOC Container (Factory). There are many out there for pretty much all platforms, but at their core they work like this:

  1. You initialize the container on your applications startup routine. Some frameworks do this via config files or code or both.

  2. You "Register" the implementations that you want the container to create for you as a factory for the interfaces they implement (eg: register MyServiceImpl for the Service interface). During this registration process there is typically some behavioral policy you can provide such as if a new instance is created each time or a single(ton) instance is used

  3. When the container creates objects for you, it injects any dependencies into those objects as part of the creation process (ie, if your object depends on another interface, an implementation of that interface is in turn provided and so on).

Pseudo-codishly it could look like this:

IocContainer container = new IocContainer();

//Register my impl for the Service Interface, with a Singleton policy
container.RegisterType(Service, ServiceImpl, LifecyclePolicy.SINGLETON);

//Use the container as a factory
Service myService = container.Resolve<Service>();

//Blissfully unaware of the implementation, call the service method.
myService.DoGoodWork();
hoserdude
  • 831
  • 1
  • 6
  • 14
  • +1 for point 1. That was very clear and understandable.Point 2 went *woosh* over my head. :P –  Dec 16 '10 at 01:05
  • 1
    @Sergio: What hoserdude means is that there are plenty of cases where your code never knows what the actual implementor of the object is, because it's implemented by the Framework, or by some other library, on your behalf, automatically. – Billy ONeal Dec 16 '10 at 01:20
  • Can you give a good, basic example of an IoC container factory? – bulltorious Feb 16 '11 at 17:34
9

When programming against an interface you will write code that uses an instance of an interface, not a concrete type. For instance you might use the following pattern, which incorporates constructor injection. Constructor injection and other parts of inversion of control aren't required to be able to program against interfaces, however since you're coming from the TDD and IoC perspective I've wired it up this way to give you some context you're hopefully familiar with.

public class PersonService
{
    private readonly IPersonRepository repository;

    public PersonService(IPersonRepository repository)
    {
        this.repository = repository;
    }

    public IList<Person> PeopleOverEighteen
    {
        get
        {
            return (from e in repository.Entities where e.Age > 18 select e).ToList();
        }
    }
}

The repository object is passed in and is an interface type. The benefit of passing in an interface is the ability to 'swap out' the concrete implementation without changing the usage.

For instance one would assume that at runtime the IoC container will inject a repository that is wired to hit the database. During testing time, you can pass in a mock or stub repository to exercise your PeopleOverEighteen method.

Michael Shimmins
  • 19,961
  • 7
  • 57
  • 90
  • 3
    +1 -- it should be noted that there's no reason that you really need something like an IoC container in order to use interfaces effectively. – Billy ONeal Dec 16 '10 at 00:48
  • So basically, the only benefit I gain is the ability to use a Mocking framework? –  Dec 16 '10 at 00:51
  • And extensibility. Testability is a nice side affect of a low coupled, highly cohesive system. By passing around interfaces you're removing the concern about what the actual class does. You are no longer concerned with *how* it does it, but only that it claims that it *does* do it. This enforces separation of concerns, and allows you to focus on the specific requirement of the current work. – Michael Shimmins Dec 16 '10 at 00:53
  • @Sergio: You need something like this to do any kind of mock. Not just frameworks. @Michael: I thought "DDD" was "Developer Driven Development" ... is that supposed to be TDD? – Billy ONeal Dec 16 '10 at 00:54
  • 1
    @Billy - I was assuming Domain Driven Development – Michael Shimmins Dec 16 '10 at 00:55
  • @Billy - and I just got it. Read DDD, even though it was TDD three times in the question heh. Updated, luckily it fits still ;) – Michael Shimmins Dec 16 '10 at 00:58
3

It means think generic. Not specific.

Suppose you have an application that notify the user sending him some message. If you work using an interface IMessage for example

interface IMessage
{
    public void Send();
}

you can customize, per user, the way they receive the message. For example somebody want to be notified wih an Email and so your IoC will create an EmailMessage concrete class. Some other wants SMS, and you create an instance of SMSMessage.

In all these case the code for notifying the user will never be changed. Even if you add another concrete class.

Lorenzo
  • 29,081
  • 49
  • 125
  • 222
2

The big advantage of programming against interfaces when performing unit testing is that it allows you to isolate a piece of code from any dependencies you want to test separately or simulate during the testing.

An example I've mentioned here before somewhere is the use of an interface to access configuration values. Rather than looking directly at ConfigurationManager you can provide one or more interfaces that let you access config values. Normally you would supply an implementation that reads from the config file but for testing you can use one that just returns test values or throws exceptions or whatever.

Consider also your data access layer. Having your business logic tightly coupled to a particular data access implementation makes it hard to test without having a whole database handy with the data you need. If your data access is hidden behind interfaces you can supply just the data you need for the test.

Using interfaces increases the "surface area" available for testing allowing for finer grained tests that really do test individual units of your code.

Andrew Kennan
  • 13,947
  • 3
  • 24
  • 33
2

Test your code like someone who would use it after reading the documentation. Do not test anything based on knowledge you have because you have written or read the code. You want to make sure that your code behaves as expected.

In the best case you should be able to use your tests as examples, doctests in Python are a good example for this.

If you follow these guidelines changing the implementation shouldn't be an issue.

Also in my experience it is good practice to test each "layer" of your application. You will have atomic units, which in itself have no dependencies and you will have units which depend on other units until you eventually get to the application which in itself is a unit.

You should test each layer, do not rely on the fact that by testing unit A you also test unit B which unit A depends on (the rule applies to inheritance as well.) This, too, should be treated as an implementation detail, even though you might feel as if you are repeating yourself.

Keep in mind that once written tests are unlikely to change while the code they test will change almost definitely.

In practice there is also the problem of IO and the outside world, so you want to use interfaces so that you can create mocks if necessary.

In more dynamic languages this is not that much of an issue, here you can use duck typing, multiple inheritance and mixins to compose test cases. If you start disliking inheritance in general you are probably doing it right.

DasIch
  • 2,549
  • 1
  • 15
  • 23
1

This screencast explains agile development and TDD in practice for c#.

By coding against an interface means that in your test, you can use a mock object instead of the real object. By using a good mock framework, you can do in your mock object whatever you like.

BЈовић
  • 62,405
  • 41
  • 173
  • 273