Test dependencies are used in place of live dependencies so that we can control how the dependency behaves. This allows our tests to be fast and deterministic since they do not need to interact with the outside world. But, designing test dependencies can go well beyond these benefits. They can even allow us to exhaustively describe what dependencies a feature needs to do its job, and even improve our SwiftUI previews and production code.
Well-designed dependencies and the Composable Architecture go hand-in-hand. If your dependencies are properly designed, then you will instantly unlock deep testing abilities of your application, including the ability to exhaustively assert the lifecycle of effects. To learn more watch our series of episodes on dependency management in the Composable Architecture.
The technique of replacing protocols with simple data types goes far deeper than what we discuss above. Almost any protocol can be rewritten as a simple data type, and when you do so you can uncover a lot of interesting forms of composition that were previously hidden from us in the protocol world. To learn more watch our series of episodes on the “protocol witness” technique.