Hi and welcome back to Weekly Dev Tips. I’m your host Steve Smith, aka Ardalis.
This is episode 43, with a quick story about dependency injection.
A Dependency Injection Story
Sponsor - devBetter Group Career Coaching for Developers
Are you a software developer looking to advance in your career more quickly? Would you find a mentor and a group of like-minded professionals valuable? If so, check out devBetter.com and read the testimonials at the bottom of the page. Sign up for a risk free membership if you're interested in growing your network and skills with us.
Show Notes / Transcript
A few years and several businesses ago, my wife and I ran an online ad business, Lake Quincy Media, that served banner ads on software developer web sites like W3Schools, www.asp.net, and ASPAlliance.com. I wrote the original ad serving software for the company, and over the years as we grew we built a team and rewrote it a couple of times. We were using ASP.NET, what would now be called web forms, at the time I first really understood how to use dependency injection. I still remember exactly how it happened, and it dramatically changed how I looked at structuring my object-oriented applications from that point forward.
Backing up a bit, I'd learned about and bought into practices like automated testing and continuous integration some years earlier. These practices were not as widespread as they are today, especially in the Microsoft development space, but we were using them at Lake Quincy Media to good effect on our ad server software (which also included publisher and advertiser portals, etc.). We were using a CI server called Cruise Control which included a nice system tray tool called CCTray that would pop up a notification and play a sound any time the build failed or was fixed. It worked great and problems that broke the build were quickly addressed by our small team. However, the application was architected using a traditional N-Tier architecture that was the recommended approach at that time. This meant that the ASP.NET application depended on the business logic application which in turn depended on the data access layer that called the database. Tests of the business logic required a test database, and so our tests ran SQL scripts that reset the database to a known good state before every test. Running several hundred of these tests took about 5-10 minutes on the build server as a result, which wasn't ideal. The point is, we were using automated tests, but our architecture was forcing us to rely more on integration tests rather than unit tests. This background leads to the next part of the story.
I remember distinctly trying to write a test for a method that dealt with saving new banner ad media files once they were uploaded to the server. The method in question needed to save the file, perform some work on the file, and then based on some other factors, call some other methods. I was trying to write tests for this, but I was forced to write tests that actually dealt with the file system, and these were very painful. A configuration file was required to specify the upload path, this path wasn't the same between servers and developer machines. Sometimes the file would be locked and tests would fail, or someone would check in a different version of the config file with the path set wrong, and the tests would fail. It was quite brittle, and the files access really wasn't what was being tested - the conditional logic of the method was.
By chance I was chatting with my friend and fellow MVP and Iraq war veteran, Jeffrey Palermo as I was struggling with this. He hopped on a screenshare with me and showed me how to change my business-level class so it wasn't working directly with the file system. Instead, he created an interface that included the required file operations like save and rename file, and moved the actual logic for working with the file system into a new class that implemented this interface. Then he created an instance of the interface in the business-level class, which was set by the constructor.
However, in our test code, he showed me how to create a fake implementation of the file interface, which we could have the tests configure to return whatever kind of result we needed for the test case we were validating. This was huge! It literally blew my mind and changed how I thought about and wrote code from that day forward. Aaron B., who recently joined my tips mailing list, prompted this tip with his question, "What is one thing you wish you knew when you first started your development career?" and this is what I thought of. Thanks, Aaron, and thanks again, Jeffrey, for showing me this awesome technique for reducing painful coupling in software applications.
Needless to say, armed with this technique and a desire to learn more about the related SOLID principles, my tests quickly started to emphasize unit tests wherever possible instead of intregration tests for everything. Our builds started to get faster, and we found tests were quicker and easier to write as well. This was over ten years ago now, but I wish I'd learned it much sooner in my career, and I hope sharing it here will help some of you.
If you have a story you'd like to share about something you learned later in your career that you wish you'd learned sooner, go to weeklydevtips.com/043 and leave it as a comment. Thanks!
Show Resources and Links
That’s it for this week. If you want to hear more from me, go to ardalis.com/tips to sign up for a free tip in your inbox every Wednesday. I'm also streaming programming topics on twitch.tv/ardalis most Fridays at noon Eastern Time. Thank you for subscribing to Weekly Dev Tips, and I'll see you next week with another great developer tip.