1. 5
  1.  

  2. 8

    The timenow.New accepts a pointer to http.Client, so we can pass nil if we want to let the function itself to instantiate the dependency, or we can specify it.

    Ugh. In an API intended for consumption by downstream users, magic parameters intended for consumption only by the framework’s developers to accomplish internal testing of the framework’s implementation are an awful architectural pattern that unfortunately have way too much industry currency as “the right way to solve mocking”, currently.

    I’ve seen far too many codebases with constructor parameter lists that are an absolute grab-bag of random dependencies with no clear meaning to end users trying to instantiate anything other than “just go with the defaults that magically appear”. It’s a bad pattern that reduces encapsulation (you’ve now given downstream consumers a lot of visibility and reach into your implementation internals, to the point where you’ve got no guarantee that you’re relying on what you expect you’re relying on at runtime), and it seriously harms the communication of intention to other developers who don’t already have the framework’s internal implementation in their heads (because now you’ve confused the public API with a bunch of information about the API’s implementation)

    There are other ways to solve this problem. If you embed httpClient in a wrapper struct, timeNow can depend, directly and internally, on your wrapper, and a good build tool will let you expose an alternate implementation of the wrapper to timeNow when compiling it for testing.

    “Injecting” mocked dependencies at test compile time is going to be a lot cleaner in the long-run than watching every constructor in your application explode into a grab-bag of dynamic injection points that are never expected to dynamically vary during a production run anyways.

    1. 5

      The strictly-better solution would be to define

      type Doer interface {
          Do(*http.Request) (*http.Response, error)
      }
      

      which is implicitly satisfied by net/http.Client. The timenow constructor would take an instance of that interface rather than a concrete object, which would permit the real client in production code, and a mock client in tests.

      1. 3

        I worked at a company where we did this , but in Objective-C. So I simply had two constructors: one with explicit dependencies, and one that conjures them up and then calls the first one, which I think is slightly nicer than the solution presented in the post. The DI version can be in a private header and no one else needs to care about it.

        1. 1

          Do you have golang exemples with your solution? I’d be interested to have the same example as presented but as you describe it!

        2. 1

          Why not just use function pointers? I feel so lost.

          1. 1

            Do you mind elaborating on your question? Might help with getting an answer.

            1. 1

              It’s probably better just to let me go. I don’t think I was the intended audience for this article and I should have known better than to comment lol.