4

I am starting a small ASP.NET MVC project, each task on the board must come with unit tests. The project is small, just a few pages with not that much processing, so I decided not to implement DI. My thoughts being DI is for larger projects with dependency graphs.

However, now I am writing tests it has made me realise testing is more difficult. There is no constructor injection going on, so mocking these objects being used by the controller actions is proving troublesome.

If there are objects being instantiated on the action due to lack of contructor injection how can you mock them to test?

10
  • 6
    Why don't you use constructor injection? That gives you 90% of the DI benefits for virtually no work. Commented Aug 18, 2015 at 15:11
  • Well, in retrospect I would probably set up a DI container and use that. I had it in my head that DI setup was not worth the time on small projects. I am just looking to see if there is an alternative without implementing one (reversing the decision)
    – Lotok
    Commented Aug 18, 2015 at 15:13
  • 2
    Try Microsoft Shims.
    – apocalypse
    Commented Aug 18, 2015 at 15:36
  • 1
    I find that fighting against the DI pattern is a losing battle when writing unit tests. You wind-up with crazy schemes or 3rd-party tools that are harder than simple constructor injection
    – Moby Disk
    Commented Aug 18, 2015 at 16:56
  • 2
    DI != IoC Container. You can ctor inject your dependencies without a fancy framework.
    – RubberDuck
    Commented Aug 19, 2015 at 9:47

2 Answers 2

10

Here's a question: does this look like DI?

public class MyCar
{
    private IEngine _engine;

    public MyCar(IEngine engine)
    {
        _engine = engine;
    }

    public MyCar()
        : this(new MyV8Engine())
    {

    }
}

It should, because it IS DI. Dependency Injection is about injecting a dependency into an object, instead of placing the burden of new'ing up or finding the dependency on the object that requires it. DI is not about using a framework, it is a design pattern that solves an issue.

It is very valid for small applications to use constructor-based injection and new'ing up objects in default constructors. The reason you don't see this as an example of DI is because often times your dependency graph quickly becomes very big and unwieldy and the move to a DI framework rapidly becomes advantageous as your application grows.

3

If there are objects being instantiated on the action due to lack of contructor injection how can you mock them to test?

You can refactor your code to encapsulate all code that should be mocked away into a seperate protected methods and then use partial-mocks to replace that logic.

Example (in java)

Original

protected void moveFiles(File[] destFiles, File[] sourceFiles) {
    int pos = 0;
    while (pos < fileCount) {
        File sourceFile = sourceFiles[pos];
        File destFile = destFiles[pos];

        // do some processing. i.e. rename destFile if it already exists.

        // do the copy. the test should not execute this
        sourceFile.renameTo(destFile)
        pos++;
    }
}

refactored:

protected void moveFiles(File[] destFiles, File[] sourceFiles) {
    int pos = 0;
    while (pos < fileCount) {
        File sourceFile = sourceFiles[pos];
        File destFile = destFiles[pos];

        // do some processing. i.e. rename destFile if it already exists.
        osFileMove(destFile, sourceFile);
        pos++;
    }
}

/** can be replaced by mock/stub in unittests */
protected boolean osFileMove(File destFile, File sourceFile) {
    return sourceFile.renameTo(destFile);
}

in the test you can replace osFileMove with a fake (example uses java org.mockito.Mockito)

import static org.mockito.Mockito.*;

FileCommands sut;
@Before
public void setup() {
    sut = spy(new FileCommands());
    doReturn(true).when(sut).osCreateDirIfNeccessary(any(File.class));
    doReturn(true).when(sut).osFileCopy(any(File.class), any(File.class));
    doReturn(true).when(sut).osFileMove(any(File.class), any(File.class));
    doReturn(true).when(sut).osDeleteFile(any(File.class));
}

If you later decide to use ioc you can move the os-spcific file operations osxXXXX into a seperate class plus an interface

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.