What is the correct way to setup mocking for this unit test involving named Unity resources?

StackOverflow https://stackoverflow.com/questions/21947363

سؤال

I am wanting to write up a unit test that checks that a specific Unity IoC object was instantiated.

For example, here is the class I am testing.

using Microsoft.Practices.Unity;
using System.Threading;

namespace Client.Engine
{
    public sealed class Uploader : IUploader
    {
        private readonly IUnityContainer _container;

        public Uploader(IUnityContainer container)
        {
            _container = container;
        }

        public void PerformUpload(CancellationToken token, int batchSize)
        {
            token.ThrowIfCancellationRequested();
            _container.Resolve<IUploadModule>("Clients").Upload(token, batchSize);

            token.ThrowIfCancellationRequested();
            _container.Resolve<IUploadModule>("Patients").Upload(token, batchSize);
        }
    }
}

And here is the unit tests I set up

[TestClass()]
public class UploadClientsTests : UploadModuleTestsBase
{
    [TestMethod()]
    public override void UploaderRegrestrationTest()
    {
        PerformRegistrationTest("Clients");
    }
}

[TestClass()]
public class UploadPatientsTests : UploadModuleTestsBase
{
    [TestMethod()]
    public override void UploaderRegrestrationTest()
    {
        PerformUpladerRegistrationTest("Patients");
    }
}

public class UploadPatientsTests : UploadModuleTestsBase
{
    protected static void PerformUpladerRegistrationTest(string moduleName)
    {
        var container = new UnityContainer();
        var mocker = new AutoMoqer(container);
        var random = new Random();
        int batchSize = random.Next(int.MaxValue);
        var token = new CancellationToken();

        var uploadModuleMock = new Mock<IUploadModule>();
        uploadModuleMock.Setup(module => module.Upload(token, batchSize)).Verifiable();

        container.RegisterInstance(typeof(IUploadModule), moduleName, uploadModuleMock.Object, new ContainerControlledLifetimeManager());
        container.RegisterInstance(typeof(IUploadModule), Mock.Of<IUploadModule>());
        var uploader = mocker.Resolve<Uploader>();
        uploader.PerformUpload(token, batchSize);

        uploadModuleMock.Verify();
    }
}

The problem I run in to is Unity 2.0 does not fall back to the default instance if a named type is not available. So if I comment out the _container.Resolve<IUploadModule>("Patients") line the clients test works perfectly, and if I comment out _container.Resolve<IUploadModule>("Clients") the patients test works perfectly, I just don't know how to make it so both can co-exist.


EDIT: In normal operation I am restringing the two objects like this.

public static Bootstrap(IUnityContainer container)
{
    container.RegisterType<IUploadModule, UploadClients>("Clients", new HierarchicalLifetimeManager());
    container.RegisterType<IUploadModule, UploadPatients>("Patients", new HierarchicalLifetimeManager());
}
هل كانت مفيدة؟

المحلول

Why not actually just mock IUnityContainer directly, instead of using a real UnityContainer?

From the point of view of unit testing the PerformUpload method you should only assert that indeed both Resolve<> and Upload methods are called and with the corresponding parameters. Not the actual internals of how Unity resolves.

In fact you could even remove the dependency on the IUnityContainer and have your constructor receive a factory method instead, that you can register in Unity. I tend to avoid such dependencies, and declare the factory directly as Func<string, IUploadModule>.

The following example is untested but something like the following may work:

container.RegisterType<Func<string, IUploadModule>>(new InjectionFactory(c => 
    new Func<string, IUploadModule>(name => 
        c.Resolve<IUploadModule>(name)
        )
    )
);

This is an additional registration. You would also have to have your uploaders registered just as you have them now.

With that registered, you can have your constructor use Func<string, IUploadModule> as a parameter:

public Uploader(Func<string, IUploadModule> factory)
{
    _factory = factory;
}

And invoke it in the following manner to get your uploader:

_factory("Patients");

نصائح أخرى

Without seeing how everything is designed in your application, IMO I think your Uploader class should be restructured a bit. The PerformUpload method has dependencies to IUploadModule but instead of taking them in as dependencies as you should for IoC, you're resolving them manually in Uploader. If you had a new IUploadModule, you would have to change your PerformUpload method, which kind of defeats the purpose of doing IoC.

My suggestion is something like this:

using Microsoft.Practices.Unity;
using System.Threading;

namespace Client.Engine
{
    public sealed class Uploader : IUploader
    {
        public Uploader()
        {
        }

        public void PerformUpload(IUploadModule uploadModule, CancellationToken token, int batchSize)
        {
            token.ThrowIfCancellationRequested();
            uploadModule.Upload(token, batchSize);
        }
    }
}

This will also simplify your unit test.

[TestMethod()]
public void PerformUpladerRegistrationTest()
{
    var random = new Random();
    int batchSize = random.Next(int.MaxValue);
    var token = new CancellationToken();

    var uploadModuleMock = new Mock<IUploadModule>();
    uploadModuleMock.Setup(module => module.Upload(token, batchSize)).Verifiable();

    uploader.PerformUpload(uploadModuleMock.Object, token, batchSize);

    uploadModuleMock.Verify();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top