Search code examples
c#unit-testingdependency-injectionencapsulationfactory-pattern

How to implement Factory for production code, dependency injection for Unit Tests


I want to give a caller of my class ability to choose provider by name, instead of passing the provider concrete class as standard DI recommends. It will allow to hide actual implementation details from client, still giving control which provider to use. We've done it by implementing factory

public ICurrencyProvider GetCurrencyServiceProvider(string providerName)
    {
        switch (providerName)
        {
            case "CurrencyLayerAPI":
                {  currencyService = new CurrencyLayerWrapper(); }
                break;
            case "XE":
                { currencyProvider = new XEWrapper(); }
                break;
            }
        return _currencyProvider;
    }

and constuctor expects providerName as a parameter.

However for unit tests I wish to use Substitute, not concrete class of provider. I ended up with 2 parameters, responsible for the same choice- name for production code and interface for calls from tests.

    public CurrencyProcessor(string providerName, ICurrencyProvider substituteCurrencyProvider =null)
   {
          if(!providerName .IsNullOrEmpty()) 
          {         
             _currencyProvider = GetCurrencyServiceProvider(providerName);
            }
          else
          {  _currencyProvider =substituteCurrencyProvider; 
          }
    }

Slightly alternative implementation is to read providerName from configuration instead of passing it as a parameter.

public CurrencyProcessor(IConfigurationProvider configurationProvider,  ICurrencyProvider substituteCurrencyProvider =null)
 {
     _providerName = _configurationProvider.GetAppSetting("CurrencyProviderToUse"); 
      if(!providerName .IsNullOrEmpty()) 
      {         
         _currencyProvider = GetCurrencyServiceProvider(providerName);
      }
      else
      {  _currencyProvider =substituteCurrencyProvider;
      }
}

I wander, is any better way exist to have single parameter to control creation of internal object, but avoiding giving responsibility to create object to a client.

Related discussions How to use Dependency Injection without breaking encapsulation?
Preferable way of making code testable: Dependency injection vs encapsulation
https://softwareengineering.stackexchange.com/questions/344442/dependency-injection-with-default-construction


Solution

  • since in your constructor your are statically creating your provider, just inject the provider.

    create a factory as you describe....

     public class CurrencyFactory
        {
            public static ICurrencyProvider  GetCurrencyServiceProvider(string providerName)
            {
                return null;
            }
        }
    

    then use standard dependency injection :-

    public class CurrencyProcessor
    {
        private ICurrencyProvider _currencyProvider;
    
        public CurrencyProcessor(ICurrencyProvider currencyProvider)
        {
            _currencyProvider = currencyProvider;
        }
    }
    

    and then use like so

    var p = new CurrencyProcessor(CurrencyFactory.GetCurrencyServiceProvider("bitcoin"));
    

    then in your test mock it

    var mock = new Mock<ICurrencyProvider>(). // mock stuff