I've been reading through the documentation and I'm a little confused as to how I would achieve this. I have a WebAPI Controller called NewsController which I'll list below. It has a single constructor that currently takes three dependencies. I've created a static class for DryIoc so I can use it globally throughout the project and it gets initialized on startup.
What I would like to do is register the controller with its dependencies in my DryIoc class then somehow resolve that in the NewsController controller. The reason I want to do this is so that when I start testing I could simply have the test project change the scope of the registered controller and use the stubbed or mocked implementations.
RegisterDependencies
public static class RegisterDependencies
{
public static Container container;
public static void Initialize()
{
container = new Container(rules => rules
.WithDefaultReuseInsteadOfTransient(Reuse.InWebRequest)
.WithoutThrowOnRegisteringDisposableTransient()
.WithoutImplicitCheckForReuseMatchingScope());
container.Register<INewsManager, NewsManager>();
container.Register<IGetNews, NewsManager>();
container.Register<IAddNews, NewsManager>();
container.Register<ILoggingService, Logger>();
container.Register<NewsController>(made: Made.Of(() => new NewsController
(Arg.Of<ILoggingService>(), Arg.Of<IGetNews>(), Arg.Of<IAddNews>(),
Arg.Of<INewsManager>())));
}
}
NewsController (part of it anyway)
public class NewsController : ApiController
{
private INewsManager _nm;
private IGetNews _getNews;
private IAddNews _addNews;
private ILoggingService _log;
public NewsController(ILoggingService logger, IGetNews getNews,
IAddNews addNews, INewsManager newsManager)
{
_getNews = getNews;
_addNews = addNews;
_log = logger;
_nm = newsManager;
}
[HttpGet]
public IHttpActionResult GetNews()
{
var newsItems = _getNews.GetNews();
if (newsItems == null || newsItems.Count() <= 0)
{
_log.Error("News Items couldn't be loaded.");
return NotFound();
}
return Ok(Mapper.Map<List<NewsDto>>(newsItems));
}
UPDATE:
So far I have "automatic" dependency resolution going through WebApiConfig but I'm not sure how to access this from the testing project so I can swap out the real implementations with my stubs.
WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RegisterDependencies.controllerContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
config, throwIfUnresolved: type => type.IsController());
RegisterDependencies.InitializeControllerContainer(RegisterDependencies.controllerContainer);
}
RegisterDependencies
public static class RegisterDependencies
{
public static IContainer controllerContainer;
public static void InitializeControllerContainer(IContainer ControllerContainer)
{
ControllerContainer.RegisterMany<ILoggingService, Logger>(setup: Setup.With(allowDisposableTransient: true));
ControllerContainer.RegisterMany<INews, NewsManager>(setup: Setup.With(allowDisposableTransient: true));
}
NewsController
public class NewsController : ApiController
{
private INews _news;
private ILoggingService _log;
public NewsController(INews news, ILoggingService logger)
{
_news = news;
_log = logger;
}
Alright here is what I came up with.
In the question above under UPDATE: you'll see the code that will automatically register and resolve dependencies using WebAPIConfig. You'll need to use the DryIoc.WebAPI package from NuGet as well as DryIoc.
To be able to swap out test implementations you'll want to create a class for your test/mock dependencies. You can just use RegisterDependencies above but register your stubs or mocks instead of the production implementations.
Then your test class (I'm using xUnit) will look something like this though don't worry about the AutoMapper section in the constructor:
public class NewsControllerTests
{
private IContainer _testContainer;
private NewsController _controller;
public NewsControllerTests()
{
Mapper.Initialize(config =>
{
config.CreateMap<News, NewsDto>();
config.CreateMap<NewsDto, News>();
});
_testContainer = RegisterTestDependencies.testContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
new HttpConfiguration(), throwIfUnresolved: type => type.IsController());
RegisterTestDependencies.InitializeTestContainer(_testContainer);
}
[Fact]
void GetNews_WithoutId_ReturnsAllNewsItems()
{
//Arrange
using (var scope = _testContainer.OpenScope(Reuse.WebRequestScopeName))
{
_controller = scope.Resolve<NewsController>();
//Act
var getNewsResult = _controller.GetNews() as OkNegotiatedContentResult<List<NewsDto>>;
//Assert
getNewsResult.Content.Should().AllBeOfType<NewsDto>();
}
}
}