How can you Unit Test control your controllers without an IoC container?

I start working in Unit Testing, Dependancy Injection and all this jazz, creating my latest ASP.NET MVC project.

Now I want to want Unit Test my controllers, and it's hard for me to figure out how to do this without an IoC container.

Take, for example, a simple controller:

public class QuestionsController : ControllerBase { private IQuestionsRepository _repository = new SqlQuestionsRepository(); // ... Continue with various controller actions } 

This class is not a fully testable module because of its direct creation by SqlQuestionsRepository. So, let's go down the injection route and do the following:

 public class QuestionsController : ControllerBase { private IQuestionsRepository _repository; public QuestionsController(IQuestionsRepository repository) { _repository = repository; } } 

It seems better. Now I can easily write unit tests with mock IQuestionsRepository. However, what will now create the controller instance? Somewhere further down the SqlQuestionRepository call chain, an instance should be created. It seems that I just shifted the problem elsewhere, did not get rid of it.

Now I know that this is a good example of where the IoC container can help you by connecting the controller requirements for me, while at the same time so that my controller can easily test the unit.

My question is, how can unit testing be done on such things without an IoC container?

Note. I am not opposed to IoC containers, and I will likely go down this road soon. However, I am curious that there is an alternative for people who do not use them.

+6
unit-testing asp.net-mvc inversion-of-control ioc-container controller
source share
4 answers

Can't save a direct field instantiation, as well as provide a setter? In this case, you only call the setter during unit testing. Something like that:

 public class QuestionsController : ControllerBase { private IQuestionsRepository _repository = new SqlQuestionsRepository(); // Really only called during unit testing... public QuestionsController(IQuestionsRepository repository) { _repository = repository; } } 

I'm not very familiar with .NET, but as a side note in Java, this is a common way to reorganize existing code to improve testability. I.E., if you have classes that are already in use, and you need to modify them to improve code coverage without breaking existing functions.

Our team has done this before, and usually we set the setter visibility to private-package and keep the test class package the same so that it can call setter.

+2
source share

You may have a default constructor with your controller that will have some kind of default behavior.

Something like...

 public QuestionsController() : this(new QuestionsRepository()) { } 

Thus, by default, when the factory controller creates a new controller instance, it will use the default constructor behavior. Then in your unit tests, you can use a fake framework to pass the layout to another constructor.

+2
source share

One option is to use fakes.

 public class FakeQuestionsRepository : IQuestionsRepository { public FakeQuestionsRepository() { } //simple constructor //implement the interface, without going to the database } [TestFixture] public class QuestionsControllerTest { [Test] public void should_be_able_to_instantiate_the_controller() { //setup the scenario var repository = new FakeQuestionsRepository(); var controller = new QuestionsController(repository); //assert some things on the controller } } 

Other options are to use the mocks and mocking framework, which can automatically generate these mocks on the fly.

 [TestFixture] public class QuestionsControllerTest { [Test] public void should_be_able_to_instantiate_the_controller() { //setup the scenario var repositoryMock = new Moq.Mock<IQuestionsRepository>(); repositoryMock .SetupGet(o => o.FirstQuestion) .Returns(new Question { X = 10 }); //repositoryMock.Object is of type IQuestionsRepository: var controller = new QuestionsController(repositoryMock.Object); //assert some things on the controller } } 

Regarding where all the facilities are built. In unit test, you create a minimal set of objects: the real object that is under testing, and some fake or bullying dependencies that the real object under the test requires. For example, the tested real object is an instance of the QuestionsController - it has an IQuestionsRepository , so we give it either a fake IQuestionsRepository , as in the first example, or the IQuestionsRepository layout, as in the second example.

On a real system, however, you configured the entire container at the highest level of software. For example, in a web application, you configure a container by passing all the interfaces and implementing classes to GlobalApplication.Application_Start .

+1
source share

I’ll talk a little about Peter.

In applications with a large number of entity types, it is usually not required that the controller require references to several repositories, services, etc. It is very difficult for me to manually transfer all these dependencies in my test code (especially since this test can include only one or two of them). In these scenarios, I prefer the setter-injection style IOC over the constructor injector. I am using this template:

 public class QuestionsController : ControllerBase { private IQuestionsRepository Repository { get { return _repo ?? (_repo = IoC.GetInstance<IQuestionsRepository>()); } set { _repo = value; } } private IQuestionsRepository _repo; // Don't need anything fancy in the ctor public QuestionsController() { } } 

Replace IoC.GetInstance<> any syntax that your specific IOC structure uses.

During use, the property installer will not call anything, so the first time the receiver is called, the controller is called into your IOC infrastructure, receives an instance, and saves it.

In the test, you just need to call the installer before calling any controller methods:

 var controller = new QuestionsController { Repository = MakeANewMockHoweverYouNormallyDo(...); } 

The advantages of this approach, IMHO:

  • Still using IOC in production.
  • It’s easier to manually create controllers during testing. You only need to initialize the dependencies that your test will use.
  • It is possible to create test IOC configurations if you do not want to manually configure common dependencies.
0
source share

All Articles