The reason the dependency injection discussions go on forever is because passing state around is so fundamental to how we compose software. Object oriented languages push you into using classes and functional languages use function arguments (and closures).
This hasn't evolved very much so pick your poison:
- Global variables (statics in C#)
- Constructor arguments
- Properties
- Function arguments
- Thread/Async locals
- Global variables (statics in C#)
- Constructor arguments
- Properties
- Function arguments
- Thread/Async locals
The decision about where to get state from will determine how easy it is to unit test that thing. You can hate unit testing all you want but it’s really effective to do it for lots of things.
Things that will ruin your ability to test if you don't mock them:
- Dates and Time
- Random number generation
- Scheduling
- Timers
- Dates and Time
- Random number generation
- Scheduling
- Timers