2

I consider myself to be a 'mockist' on the 'mockist' vs 'classicist' debate, but I am trying to understand the other point of view.

Imagine I have this class, where a concrete instance of IDependency is injected by an IoC container.

IDependency does not access a remote resource like a database or web service (in which case I would definately mock it), it is just another class containing some business logic.

public class UnitUnderTest
{
    public UnitUnderTest ( IDependency dependency )
    {
    }
}

Using this pattern, I am free to change the concrete implementation of IDependency by changing the class injected by the IoC container. I am not tied to any particular implementation.

If I was to test this class without mocking IDependency, wouldn't I have to instantiate my real IDependency to pass in to the constructor?

public class My_Unit_Test()
{
    IDependency concreteDependency = new ConcreteDependency();
    var unitUnderTest = new UnitUnderTest(concreteDependency);
}

In this example, if I write a replacement to ConcreteDependency, I would have to not only change my IoC container configuration, but all of my unit tests that use ConcreteDependency.

Or.. do I use the same IoC container within my unit tests (which seems like it goes against the idea of testing things in isolation)?

  • Isn't the distinction only relevant if IDependency were a concrete class, rather than merely an interface? – DougM May 14 '14 at 16:19
  • Please stop... DHH put together a strawman and attacked it brutally, but it was a strawman nonetheless. His argument was directly that a lot of our techniques for abstracting dependencies away is a direct result of the requirements placed by unit testing. How can I test for a dependency I haven't made yet? I mock it and inject the dependency in the unit under test. For projects under 10 developers, this might be over kill. For large scale enterprise projects it could be the difference between success and failure – Michael Brown May 14 '14 at 16:36
  • @MikeBrown I agree, which is why I posted this question. I am trying to understand the other side of the argument and how it works in practice. – Dave S May 14 '14 at 16:38
  • Who is DHH? ... – Robert Harvey May 14 '14 at 16:39
  • @RobertHarvey: David Heinemeier Hannson. david.heinemeierhansson.com – pdr May 14 '14 at 17:43
7

If I was to test this class without mocking IDependency, wouldn't I have to instantiate my real IDependency to pass in to the constructor?

No, you could create a fake IDependency for your specific needs. You may even make your "real" IDependency but parameterize it specifically to your needs. Though depending on who you talk to, these are considered mock objects since they're not used in production.

This is the common argument against mocking everything: you already have objects to do this stuff that are robust, flexible and trustworthy. You don't mock IList in your tests because List is a dependency do you?

Like most arguments, the best approach is often a balance of the two.

In this example, if I write a replacement to ConcreteDependency, I would have to not only change my IoC container configuration, but all of my unit tests that use ConcreteDependency.

Yes, just like you'd have to change all of your mocks should you replace your mock objects.

How to unit test without mocks and not be tied to a concrete implementations of an interface

You literally can not.

You need to create a concrete implementation of that interface somewhere. You could share that instance between tests, but that is against best practice (tests are not isolated). You could have a factory to generate the object, but then you have added complexity and it's uncommon that a single type of instance can be used for every test. But in the end, the test needs a concrete implementation to work, no matter how much indirection you put between the test and the thing.

  • The difference as I see it is that if I use mocks, I can use a 'fakes' framework to create them from an interface and use those in the tests. If I use the real thing, I am tying my unit tests to those concrete implementations. Is this unavoidable? If so, it seems like using a 'fakes' framework like NSubstitute would be preferable as the tests are less coupled to the 'real' implementation. – Dave S May 14 '14 at 16:35
  • @DaveS - sure, you can use something like Moq to create test objects from an interfaces, but there's still a concrete object your test depends on. The argument arises if it's quicker/easier/more robust to do that versus make your own test object by hand or use an existing object. – Telastyn May 14 '14 at 16:48
  • Agreed. That has clarified it for me (although I think I will have to try it before having a real opinion). – Dave S May 14 '14 at 18:12
4

You only need an IoC container if you want to centralize your dependency declarations. For everything else, there's ordinary constructor injection.

To put it another way, you don't need an IoC container in your unit tests. You're not testing the IoC container, after all.

To put it still another way, if you're stitching together a bunch of mock dependencies in your unit tests using an IoC container, they're no longer unit tests; they're integration tests (mock integration tests, if there is such a thing).

  • This was my point, I shouldn't be using an IoC container. This means that I have to hard code all of my concrete implementations in to my unit tests. – Dave S May 14 '14 at 16:33
  • Is that a problem? – Robert Harvey May 14 '14 at 16:34
  • Only in that if change the class that I use as the concrete implementation in my real application, I need to modify all of my unit tests. If I mock them, I only need to change the mocks if the interface changes. – Dave S May 14 '14 at 16:40
  • @DaveS: If you're using an IoC container, you're not really testing a unit, you're testing a bunch of units. – DougM May 14 '14 at 16:41
  • @DougM This is exactly my question, I cannot use an IoC container, so I am forced to hard-code my concrete classes. – Dave S May 14 '14 at 16:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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