4

I use ninject extensively with the factory extension. I also use moq to help with testing and in some cases (particularly complicated view models in my MVVM WPF application) I use the ninject mocking kernel to make it easier to automatically create mocks for resources required by the class under test.

In my regular product code, the ninject factory extension makes it easy to just declare an interface for the factory that looks something like this:

public interface IMyViewModelFactory
{
    MyViewModel CreateMyViewModel(int recordNum);
}

and the factory extension will automatically supply an implementation which maps the creation method onto a constructor for the class while resolving any other dependencies that are constructor parameters from ninject. So If my class has a constructor that looks like this:

public MyViewModel(ILogger logger, int recordNum)
{
    _logger = logger;
    _recordNum = recordNum;
}

then the factory extension will create a factory method implementation that gets the ILogger from the ninject kernel and pass through my explicit parameter.

This all works great for the regular product code, but as near as I can tell I can't get this kind of thing to work in my unit tests when I'm using the mocking kernel.

Normally with the mocking kernel I set it up in the initialization method for my test class, bind a small number of interfaces to concrete classes that I want to test and then let the mocking kernel automatically supply mocks for everything else. These mocks can then be configured as needed for a particular test, and everything works great except for the factory methods. I find myself manually implementing factory methods in setup statements for my mock that look something like this:

Kernel.GetMock<IMyViewModelFactory>()
      .Setup(f => f.CreateMyViewModel(It.IsAny<ILogger>(), It.IsAny<int>()))
      .Returns((ILogger logger, int recordNum) => 
                 new MyViewModel(Kernel.Get<ILogger>, recordNum);

That way if my test needs to interact with the factory (or more likely some class I'm testing needs to use the factory to create some sub-view model) then it can actually create the instance and dependencies come from the mocking kernel so they can be setup to mock as needed.

Most of the time I love ninject and these extensions, but this is one place where the unit test setup becomes really tedious. I probably wouldn't even mind except for the fact that I'm so spoiled by how easy the factory extension makes it in my product code, and I can't figure out how to get the same benefit in my tests. Of course the above example is contrived, and sadly in many of my view models the number of dependencies and real parameters is large (and the number of view models where I have to setup this kind of thing is large), so the tedium becomes a bigger deal.

I'm hoping someone can tell me that I'm just missing something obvious and that there's a simple way to set this up.

Thanks, Danny

simmdan
  • 616
  • 6
  • 9
  • Does the above code i.e Kernel.GetMock()... works for you, and you looking for a way to simplify this? – Spock Nov 26 '13 at 08:58
  • Yes, exactly. I have something that works, but it requires a lot of repetitive, error-prone typing which is exactly what the factory extension removes for the product code--it just doesn't seem to work with the mocking kernel so I can't get that same benefit for my unit tests. – simmdan Nov 26 '13 at 19:19
  • The more I think about your workaround, the more I like it. I wrote an extension: public static class NinjectFactoryExtensions { public static void MoqFactory(this MoqMockingKernel kernel, Expression> expression) where U : class where V : class { kernel.GetMock().Setup(expression).Returns (kernel.GetMock().Object); } } – StefanG May 12 '14 at 11:47
  • Unfortunately, no, I haven't found a satisfactory solution. I'm still putting up with my manual workaround. Maybe I'm missing something, but it seems like the extension method you describe will only work if the factory methods don't take any parameters--in other words if the constructor of the object we are creating with the factory only takes dependencies from the kernel. I have a number of cases with a mix of parameters computed by the caller of the factory as well as dependencies that need to come from the kernel which is what makes the factory extension so nice in real code. – simmdan May 13 '14 at 18:46
  • I'm pretty much in exactly the same situation; writing unit tests where I need an abstract factory mocked in such a way that it is automatically configured to return mock implementations. So I was quite happy to run into this thread, because believe it or not, the factory extension works like a charm for me, also in my unit tests with the MockedKernel. Are you sure have all the latest versions of the mocking kernel and factory extensions? – Rogier Pennink May 15 '14 at 11:52
  • Wow. This is good news. No, I do not have the very latest versions. I think I did at the time when I first posted this, but it sounds like something newer has come out since then. I'm just about to ship a major release, so I can't upgrade right now, but in a few weeks once this release is out the door, I will definitely upgrade and hopefully the problem will be solved. Thanks for sharing your experience. – simmdan May 16 '14 at 17:49

0 Answers0