Based on my understanding of what dependency injection means, I believe that the post introduced at least three ways to inject your dependencies to the function that needs it:
via env config and
via passing the callback function as argument with defaults
def my_function(heavy_work \\ &SomeDependency.heavy_work/2)
via passing the module fulfilling a contract as argument with defaults
def my_function(dependency \\ SomeDependency)
I think I’m kinda lost here, I assume you mean if you want your services to be testable without touching the DB?
If we’re talking about Ecto, then if we want to check the parameters, I think the way to go is to test the your changeset validations. We’re passing changesets as the arguments to Repo operations, then it is reasonable to test whether the changeset functions generate the right validations. We are sure that the Repo function would convert the changesets to a proper query, since Ecto should have tested that for us.
It makes little sense for me to test whether the query generated by Ecto is correct by, for example, testing if the generated query matches an SQL string; that’s kinda like you’re testing Ecto’s query generators, which should be done by the Ecto authors. The only way to test if your query logic is correct is by providing some test db, run the query against it, and assert the results.
I believe Ecto and Phoenix already allow async DB tests?
From my POV, integration tests should be done for the integrators. José’s reply on the mailing list are structured as such because in that post’s context, the controller is the integrator, such that we call Repo functions from inside the controller function.
However, your code extracts the integrator role of the controller to your
UserManager service. Therefore, the integration test should be done against your service. Your controller should have faith that the service would fulfil its own contract.
Do correct me if I’m missing something!