1

I am still new to DI and Unit Tests. I have been tasked with adding Unit Tests to some of our legacy code. I am working on a WCF web service. A lot of refactoring has had to be done. Monster classes split into separate classes that make sense. Monster methods split to multiple methods. And lastly, creating interface classes for external dependencies. This was done initially to facilitate mocking those dependencies for unit tests.

As I have gone about this the list of dependencies keeps growing and growing. I have other web services to call, SQL Servers and DB2 Databases to interact with, a config file to read, a log to write to, and reading from Sharepoint data. So far I have 10 dependencies. And every time I add one it breaks all my Unit Tests since there is a new parameter in the constructor.

If it helps, I am using .Net 4.5, Castle Windsor as my IOC, MSTest, and Moq for testing.

I have looked here How to avoid Dependency Injection constructor madness? but this doesn't provide any real solution. Only to say "your class may be doing too much." I looked into Facade and Aggregate services but that seemed to just move where things were.

So I need some help on how to make this class to "less" but still provide the same output.

public AccountServices(ISomeWebServiceProvider someWebServiceProvider,
        ISomeOtherWebProvider someOtherWebProvider,
        IConfigurationSettings configurationSettings,
        IDB2Connect dB2Connect,
        IDB2SomeOtherData dB2SomeOtherData,
        IDB2DatabaseData dB2DatabaseData,
        ISharepointServiceProvider sharepointServiceProvider,
        ILoggingProvider loggingProvider,
        IAnotherProvider AnotherProvider,
        ISQLConnect SQLConnect)
    {
        _configurationSettings = configurationSettings;
        _someWebServiceProvider = someWebServiceProvider;
        _someOtherWebProvider = someOtherWebProvider;
        _dB2Connect = dB2Connect;
        _dB2SomeOtherData = dB2SomeOtherData;
        _dB2DatabaseData = dB2DatabaseData;
        _sharepointServiceProvider = sharepointServiceProvider;
        _loggingProvider = loggingProvider;
        _AnotherProvider = AnotherProvider;
        _SQLConnect = SQLConnect;
    }

Almost all of the there are in other components but I need to be able to use them in the main application and mock them in unit tests.

Here is how one of the methods is laid out.

public ExpectedResponse GetAccountData(string AccountNumber)
    {
          // Get Needed Config Settings
          ...
          // Do some data validation before processing data
          ...
          // Try to retrieve data for DB2
          ...
          // Try to retrieve data for Sharepoint
          ...
          // Map data to response.
          ...
          // If error, handle it and write error to log
    }

Other methods are very similar but they may be reaching out to SQL Server or one or more web services.

Ideally what I would like to have is an example of an application that needs a lot of dependencies, that has unit tests, and avoids having to keep adding a new dependency to the constructor causing you to update all your unit tests just to add the new parameter.

Thanks

CMason
  • 11
  • 1
  • 5
  • The post you linked was correct, your class is doing WAY too much. Ignoring the dependencies, names like `AccountServices` alone are huge red flags that your class has too many responsibilities. Way too much. All your database stuff could be abstracted away into another class that specifically handles database operations and only pass that class in as a dependency rather than 10 dependencies related to db interop in a class which should not have any knowledge of how the persistent storage works. – DetectivePikachu Oct 25 '19 at 17:13
  • Could you create a base interface that has all these or have you considered encapsulation? – zaggler Oct 25 '19 at 17:15
  • Use a dependency injection framework in your unit test to create the `AccountServices` for you. – André Sanson Oct 25 '19 at 17:39
  • For clarity, the service is not making any database calls directly, that it what the separate classes/projects now do. So I just call a method in the dependency to get the data and in some cases insert the data. But I still need to mock these things. Even if I consolidated all the data dependencies into one that only knocks the number down a little. And I think it would think then those data classes would be doing too much. – CMason Oct 25 '19 at 18:18
  • If you think your class doesn’t do “too much”, then it’s completely fine to have this amount of dependencies. Also, it’s not possible to avoid updating your unit tests each time you inject new instance. That’s the point you tests. They should fail-you should figure why and update them – OlegI Oct 25 '19 at 18:59
  • A class named `AccountServices` should only know about *Accounts* and classes that are directly related to them. Anything that touches a database of any kind is not related to accounts and should be treated that way. That being said, every method in `AccountServices` that does something with databases or connections shouldn't be there. Have separate classes that take care of putting and getting data from databases and have those results fed into your class's methods. Stick to classes like `Account` to exchange data structures. – MikeLimaSierra Oct 28 '19 at 09:03
  • Mike, I want to thank you for that. I think that might be the most helpful comment. Olegi is also helpful. There are so many problems with this code base I am working on. It's been pretty overwhelming. But I think the combination of using a facade and splitting out more into separate components will help my problems in the long run. There's a lot of stuff that was drilled into me years ago that I am having to unlearn and it's hard. – CMason Oct 29 '19 at 14:20

2 Answers2

1

Not sure if this helps, but I came up with a pattern I called GetTester that wraps the constructor and makes handling the parameters a little easier. Here's an example:

 private SmartCache GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis)
 {
     memory = new Mock<IMemoryCache>();
     redis = new Mock<IRedisCache>();
     return new SmartCache(memory.Object, redis.Object);
 }

Callers look like this if they need all the mocks:

 SmartCache cache = GetTester(out Mock<IMemoryCache> memory, out Mock<IRedisCache> redis);

Or like this if they don't:

 SmartCache cache = GetTester(out _, out _);

These still break if you have constructor changes, but you can create overloads to minimize the changes to tests. It's a hassle but easier than it would otherwise be.

Mike Brunner
  • 332
  • 2
  • 10
  • I also created a utility called CreateGetTesterMethod. With this, I copy that big constructor to the clipboard, run the utility, and clipboard contents are replaced with a GetTester method for that class. It really helps. Unfortunately, the code is not mine to give. – Mike Brunner Oct 25 '19 at 17:18
  • Also, stay the course! I recently refactored a 90,000-line code base for DI and unit tests and achieved 100% code coverage. It can be done! (with management support :D) – Mike Brunner Oct 25 '19 at 17:18
  • 2
    This completely ignores that fact that a class about Accounts shouldn't be implementing database CRUD logic. This doesn't solve the problem, it just hides it. – DetectivePikachu Oct 25 '19 at 17:21
  • 1
    I'm giving him a workaround to make it easier to deal with a legacy code base. In an ideal world, he'd scrap everything and rewrite the app from scratch. But that's not the world we live in. – Mike Brunner Oct 25 '19 at 17:32
  • I'm not downvoting this but I find it hard to understand why you would not use a DI framework to handle that many dependency. – André Sanson Oct 25 '19 at 17:47
  • literally the title of the post is "how can I redesign my c sharp application to avoid contructor injection overload" and your suggestion is "dont redesign the app, just hide the bad stuff with even worse stuff!" – DetectivePikachu Oct 25 '19 at 17:48
0

So possibly your classes might be doing too much. If you're finding that you're constantly increasing the work that a class is doing and as a result are needing to provide additional objects to assist in those additional tasks then this is probably the issue and you need to consider breaking up the work.

However, if this isn't the case then another option is to have the classes take in a reference to a dependency class that provides access to either the instantiated concrete objects that implement your various interfaces or instantiated factory objects which can be used to construct objects. Then instead of constantly passing new parameters you can just pass the single object and from there pull or create objects as necessary from that.

misterbee180
  • 112
  • 9