2

I'm just learning how to do proper unit testing, and a lot of resources make reference to using explicit dependencies, ie dependencies that are passed into the constructor, to allow for easy mocking/stubbing, etc.

This obviously makes sense.

What isn't clear to me is where should these objects be created, and how do you unit test those methods?

As an example, say you have an ASP.net ActionMethod. This makes use of a class that requires 3 dependencies, would you simply 'new' those up in the ActionMethod? If so, how then would you test that ActionMethod?

  • In addition to the answers already given, have a look into inversion of control containers. AutoFac takes care of this dependency injection for you, although there's some overhead in installing and configuring it. – JᴀʏMᴇᴇ Mar 25 '15 at 16:17
1

Most likely those objects are created from a factory or from a single point where the object graph is created as indicated from @guillaume31 comments. If from factory it would look something like this:

namespace Repository.Lookup
{
    public class LookupRepositoryFactory
    {
        public static ILookupRepository Create()
        {
            ILookupRepository lookupRepository = new LookupRepository(); 

            return lookupRepository;
        }
    }
}

Then the test for the factory:

namespace RepositoryTests.Lookup
{
    [TestClass]
    public class LookupRepositoryFactoryTests
    {
        [TestMethod]
        public void TestCreateLookupRepository()
        {
            var expectedType = typeof(LookupRepository);

            var repository = LookupRepositoryFactory.Create();

            Assert.IsInstanceOfType(repository, expectedType);
        }
    }
}

Then usage:

var lookupRepository = LookupRepositoryFactory.Create();

Then you just pass in that on the constructor as you have mentioned, like so:

namespace CommonLookupAPI
{
    public class GenericLookup
    {
        private readonly ILookupRepository _repository;

        public GenericLookup(ILookupRepository repository)
        {
            _repository = repository;
        }

        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            var commonLookups = _repository.GetCommonLookup(lineOfBusiness, classificationType, state, grouping, subGrouping);
            return commonLookups;
        }
    }
}

This allows one to mock the repository with a fake implementation so you can test generic lookup without using the real repository.

Here are the tests:

namespace CommonLookupAPITests
{
    [TestClass]
    public class GenericLookupTests
    {
        [TestMethod]
        public void TestGenericLookupReturnsLookup()
        {
            ILookupRepository repository = MockRepositories.CreateMockSuccessRepository();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);

            Assert.AreEqual(lookups.Count, 0);
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void TestGenericLookupReturnsFailUnExpected()
        {
            ILookupRepository repository = MockRepositories.CreateMockFailRepositoryException();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);
        }

        [TestMethod]
        [ExpectedException(typeof(DataAccessException))]
        public void TestGenericLookupReturnsFailDataAccess()
        {
            ILookupRepository repository = MockRepositories.CreateMockFailRepositoryDataAccessException();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);
        }
    }
}

And finally the Mock Repositories that the tests use:

namespace CommonLookupAPITests
{

    public class MockRepositories
    {
        public static ILookupRepository CreateMockSuccessRepository()
        {
            ILookupRepository repository = new MockSuccessRepository();
            return repository;
        }

        public static ILookupRepository CreateMockFailRepositoryException()
        {
            ILookupRepository repository = new MockFailRepositoryException();
            return repository;
        }

        public static ILookupRepository CreateMockFailRepositoryDataAccessException()
        {
            ILookupRepository repository = new MockFailRepositoryDataAccessException();
            return repository;
        }
    }

    public class MockSuccessRepository : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            var commonLookup = new List<CommonLookup>();
            return commonLookup;
        }
    }

    public class MockFailRepositoryException : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            throw new Exception();
        }
    }

    public class MockFailRepositoryDataAccessException : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            throw new DataAccessException();
        }
    }
}
  • Thanks Jon. How would you test the client of the GenericLookup, the method that is using the factory to create the repository? That's the main part that I'm struggling to understand. Would you even have to test that, given that you've already tested the factory and the logic class/method? Is this somewhere where IOC comes in? – Dave Leonard Mar 25 '15 at 13:56
  • 2
    @JonRaynor what's the point of a Factory here ? – guillaume31 Mar 25 '15 at 14:03
  • I've added the tests for testing generic lookup class using the mock repositories which allows to test GenericLookup in isolation. The real client would call repository factory which would instantiate the real repository. Hope that helps. – Jon Raynor Mar 25 '15 at 14:03
  • @guillaume31 Factory allows other type of implementations to be created. Your right in this case, not much use here. But, in most cases, the factory would return multiple implementations. Consider a case where you have a performance environment, you might have an fake implementation to avoid the 3rd party callouts and the factory would be driven by sort of configuration value passed to it. – Jon Raynor Mar 25 '15 at 14:08
  • In my experience, Factories are overkill most of the time, especially when you have a well-identified Composition Root, i.e. a single entry point where the whole object graph is assembled (it might use a DI container or not). In your example, that would be when the configuration value is read and the correct implementation chosen accordingly. – guillaume31 Mar 25 '15 at 14:37
2

I don't know ASP.NET, but generally your dependencies would be on interfaces, and in your unit tests you provide mock, stub, or fake implementations of those interfaces. This lets you test your class in isolation, since the only code under test is the class itself plus your "skeleton" dependencies.

0

What isn't clear to me is where should these objects be created

Anywhere except in the dependent object itself, obviously. The point is that the consumer object doesn't control its dependency's lifecycle, someone else does.

One possible global approach to this is to compose the entire application's object graph in a single place, the Composition Root, located as close as possible to the application entry point. What it does concretely is new up all objects that are predictably instantiatable at that point and wire them up together using constructors.

As an example, say you have an ASP.net ActionMethod. This makes use of a class that requires 3 dependencies, would you simply 'new' those up in the ActionMethod?

Of course not -- the ActionMethod doesn't know how the class it uses is built, let alone controls how this dependency's dependencies are newed up :)

how then would you test that ActionMethod?

As you said, by passing mocks or stubs in the Controller's constructor or as arguments to the ActionMethod itself.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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