Search code examples
c#unit-testingdependency-injectionmoqautofac

Is it the best practice to inject ILifetimeScope to the constructor?


I'm using static function Resolve() in ILifetimeScope instance injected from constructor.

private readonly ILifetimeScope _container;

public MyService(ILifetimeScope container)
{
    _container = container;
}

private void Save()
{
    using (var scope = _container.BeginLifetimeScope())
    {
        var rdb = scope.Resolve<IDatabase>();
        bool result = rdb.Save(name:"newAccount"); // I can't Assert the result.
    }
}

In this case, I got a problem when I made a unit test. Since ILifetimeScope's Resolve() function is a static function, it couldn't be mocked and couldn't be asserted. As far as I understood, It means two classes are tightly coupled. However, if ILifetimeScope is not injected, too many classes will have to be injected. In this situation, I would like to ask the following questions.

  1. I would like to ask if it is best practice to use Resolve() function which is from injected ILifetimeScope instance. if is not true, all the dependencies should be injected by constructor?
public MyService(ILogger logger, IDatabase database, IAccountServer accountService /*more and more*/)
{

}
  1. if there is the way I can mock the Reslove() function, Please let me know.

Thank you in advance.


Solution

  • To echo @TravisIllig above, this would be an instance of the generally-maligned Service Locator anti-pattern. To take your questions as posed:

    1. It is not considered a best (or even good) practice to use Resolve() in this way. Yes, all dependencies should be injected via constructor. This gives a very clean approach for understanding what services your component actually requires. If you have too many then it is possible that your component is taking on too many responsibilities. (Side note: I find this happens fairly often in Controllers whose actions are grouped by route (which the framework can be seen as encouraging) and not by common dependencies.) This is pretty subjective though. If there is a common set that are injected together, you might consider aggregate interfaces.

    2. Again, using constructor injection, you shouldn't be putting yourself in a situation where it needs to be mocked. Conversely, the fact that your usage is causing difficulty with mocks is an indication that it is not a good practice.