Let me give you two examples to hopefully clarify my point:
Suppose you’re testing a particular function: myFunction: CoolRecord -> AwesomeRecord -> String. In order to test this function you’ll need to pass in a CoolRecord and an AwesomeRecord and you’ll need to know how to construct these. When I say that a unit test needs to be aware of the dependencies of the unit under test, I’m thinking that CoolRecord and AwesomeRecord are things the test would need to know about. Now myFunction could call all sorts of functions in the process of generating a String given a CoolRecord and an AwesomeRecord. My test doesn’t need to know about those functions called by myFunction. Like I said above, the test only needs to know about other units that are dependencies of the unit under test.
Elmer allows me to adopt a higher-level testing strategy, where I can often treat ‘the unit under test’ as some unit of application behavior that a user would encounter. So, if I’m writing an RSS Feed reader, I might structure my tests around ‘viewing a list of feeds’, ‘reading a particular feed’, ‘reading a particular feed item’ etc, with particular tests describing the expected behavior in each of those cases. In order to write these tests, Elmer needs to know about an initial model, the view function, and update function of my app. I’ll then need to do some stuff in my test setup to get me to the right place of the app to exercise the behavior (click on a button or input text etc). Then I can make expectations about the behavior. However, in most cases, there are additional dependencies that need to be in place to simulate the behavior I want to describe. For example, if I’m writing a test that describes the behavior of viewing a list of feeds, I’ll need to stub the http request that fetches the list of feeds. I do that by treating Http.send as a dependency that I need to provide, because (1) I don’t want to make http requests during my unit tests and (2) Elmer doesn’t know how to process Http.send. Instead, with Elmer I can inject a fake command to replace Http.send during the test (there are various strategies to do this which I’ve talked about above), which allows me to make sure the proper request is being sent (that’s part of my application’s behavior when a user attempts to view a list of feeds) and which allows me to stub various return values (successes, errors) so I can describe the expected behavior under various conditions.
Given this kind of test setup, I have tons of freedom to refactor my code and to experiment with different approaches for structuring the app. My tests do expect there to be an http request made, and they need a reference to a default model, a view function, and an update function. But that’s about it; the tests don’t need to know about any other functions called. It’s been fun to learn Elm alongside writing tests with Elmer, because as I learn more about how to structure Elm apps, I can refactor the code and just re-run my tests to be confident my app still has all the behavior I expect.