0

I'm sorry I don't have much code to share because I'm not sure how this would work.

I have a test project called API.Tests and I'm writing tests for the NewsController within the API project. I'm not sure how I would pass a dependency from Tests to the API since it's a one-way reference.

NewsController

    private IGetNews _getNews;
    private IAddNews _addNews;
    private ILoggingService _log;

    public NewsController()
    {
        _getNews = RegisterDependencies.container.Resolve<IGetNews>();
        _addNews = RegisterDependencies.container.Resolve<IAddNews>();
        _log = RegisterDependencies.container.Resolve<ILoggingService>();
    }
Christopher Johnson
  • 635
  • 2
  • 7
  • 21

1 Answers1

1

Your current code is using a service locator anti-pattern. This makes the controller tightly coupled to those dependencies and difficult to test in isolation. You need to invert those dependencies. (ie: Dependency Inversion)

Refactor the controller to use explicit dependencies via constructor injection.

public class NewsController {
    private readonly IGetNews getNews;
    private readonly IAddNews addNews;
    private readonly ILoggingService log;

    public NewsController(IGetNews getNews, IAddNews addNews, ILoggingService log) {
        this.getNews = getNews;
        this.addNews = addNews;
        this.log = log;
    }

    //...other code
}

For unit testing you can now mock and inject the dependencies into the subject under test. The following example uses a mocking framework (Moq) to mock the dependencies and inject them into the subject under test

public void ExampleNewsControllerTest() {
    //Arrange
    var getNews = Mock.Of<IGetNews>();
    var addNews = Mock.Of<IAddNews>();
    var log = Mock.Of<ILoggingService>();

    var subject = new NewsController(getNews, addNews, log);

    //Act
    //...exercise the method under test
    subject.SomeAction();

    //Assert
    //...assert that the subject behaves as expected.

}

You would configure the dependencies to suit the test case/scenario.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Yes that's the classic way of doing it and exactly what I used to use but the issue is how do I tell DryIoC in the API project to use a different registered dependency from within the API.Tests project since the implementation of the test concrete class is in the API.Tests project? I should be able to simply tell DryIoc "Hey resolve this implementation of IGetNews instead" without having to pass a mock of something that doesn't need to be mocked. – Christopher Johnson Aug 15 '17 at 12:05
  • I should also mention that this is a Web API controller so it has to have a blank constructor. – Christopher Johnson Aug 15 '17 at 13:01
  • 1
    @ChristopherJohnson, Web API controllers do not **have to have blank constructor**. that is a default misleading error message that the framework throws for any error that happens when trying to initialize controllers. You need to read up on the framework and would need to also review your design, as you have already discovered, that it is difficult to test because of poor design choices. What you are calling DI pattern is actually service locator anit-pattern. – Nkosi Aug 15 '17 at 13:14