Often times in the software development world we are constantly working towards the "best compromise" solution for our customers or employers. In many cases, there isn't one "right" way to do things. Each application will have its own requirements for testability, durability, and longevity. With differences in these requirements, the methods used for development or the need to engineer long-term support situations will vary. To this point, we often find in the .NET Framework that certain methods, API's and processes are more geared towards the "easy to use" solution, rather than the "enterprise" solution. In this post, I dive into this concept/problem a bit, and introduce a workaround that I have been working on.
Ease of Use
Many API's are developed in a manner that makes them easy to use, or discover. For example, the DateTime object is one that provides a number of helpful methods as Static methods that we can use anywhere in our code easily. Such as.
These classes are easy to use, quick to identify and don't require you to do anything. However, with this ease of use come limitations. Static methods are handy, however, when trying to integrate with modern processes they can make life entirely difficult.
As the complexity level or stability requirements increase with our applications we cross into the buzzword land of "Enterprise Software." As much as I hate to use that term, it is the only way to truly describe what we are talking about here.
Enterprise software is software that although we may want to rapidly create and enhance, needs to also support a level of integrity and stability. There are a plethora of concepts in HOW we accomplish this, however, the two key concepts that provide the best ability to improve the integrity & testability of a project are the concepts of Dependency Injection and Unit Testing.
Simply put, dependency injection is a concept that allows an object to have its dependencies injected, rather than building them internally. This allows us to recompose our applications in different situations. For example, by injecting a DbContext into our class as a dependency we can use a Memory-Based provider for the purposes of testing, and we can inject the real connection in the actual running application. Or we could inject a dummy SmtpSender for testing purposes.
The benefits of this are something I could easily write another detailed posting on, however, this is where we start to see some of the problems with the easy to use code elements. DateTime.Now is not something that we can inject, and it really is a dependency. We need something that tells us what the current time is. To understand WHY this might be a problem, we can continue looking at Unit Testing issues.
When we write unit tests, we are working with code that validates that our code is doing what it should. Often times we will want to Mock, or Fake, actual implementations to allow our code to be tested, but not actually generate emails to users. Or to test our application functionality without writing to the database.
How I've Worked Around
There are many ways that developers will work to get around these situations where unit testing isn't easy. Over the past 5 or so years as I have been working to improve our adherence to standards, as I've moved to do more with .NET Core that makes things easier, I've amassed a collection of helpers/shims that allow us to inject some of these "not so easy" elements.
Up to this point, I've copied and pasted this stuff from project-to-project or created an internal shared library to use. However, after thinking about this, it makes much more sense to make this available to everyone. As such, I have created and released the first version of what I'm calling "aspnetcore.utilities." We will be constantly expanding this project, and it is fully open-source, the goal of the project is to provide the needed wrappers/shims for commonly injected items, as well as other helpful and reusable bits that we use to make our projects.
Just a few days ago we released the initial versions of these utilities with a total of 2 NuGet packages available.
The ASPNetCore.Utilities package contains helpful shims allowing you to inject commonly used items. In the initial release we have included.
We make extensive use of thse items to improve the unit testability of our projects and we hope these are helpful for your projects
- ITimeProvider - A shim for System.DateTime
- ITimeSpanProvider - A shim for System.TimeSpan
- IUrlSlugGenerator - A utility that takes an input and converts it to a URL friendly slug
- IdentityExtensions - An extension method to extract a claim value from the current user
We have also found that we have common requirements when writing unit tests and have created the ICG.AspNetCore.Utilities.UnitTesting package with these common items to help provide consistency across projects. At this point in time, there are a few helpers included to make testing max-length validators easier and to simplify the creation of DbContext objects for testing.
Contributions, Suggestions, and Future Releases
The full source is available on GitHub and I welcome any suggestions or pull requests to improve the library. We plan to version these libraries to correlate with the .NET Core Version. As such the current releases are 2.2.x as we have a minimum version requirement of .NET Core 2.2.
I have solicited limited feedback from developers on this already and many have found it helpful, I hope it helps you as well.