struct Foo {
}
func (f *Foo) doSomething() {
client := pkg.NewClient()
client.doHttpRequest()
}
vs. struct Foo {
client Client
}
func (f *Foo) doSomething() {
f.client.doHttpRequest()
}
func NewFoo(client Client) *Foo {
return &Foo{ client: client }
}
var FooModule = fx.Provide(NewFoo)
then to use it... struct Bar {
foo *Foo
}
func NewBar(foo *Foo) *Bar {
return &Bar{foo: foo}
}
var BarModule = fx.Provide(NewBar)
how do you unit test the first example? There is no easy way to that without having the client do things special internally, which leads to a rats nest of issues and doesn't scale. Easier to just pass in a mock client and make sure the right http request/responses are being done.fx kind of highly encourages the second approach for all of your code. It is a bit more boilerplate, but by default, I'm always going to be thinking about passing my dependencies around instead of just being lazy and doing NewClient in the function.
I also get to take advantage of the fact that Client is just magically passed into my constructor. If there is some error doing so (like I forget to Provide), then fx will give me errors at start up. My dependency chain is clear. It just feels nicer to me.
Once again, of course I can write it the second way without fx... but it is kind of nice that every time I want to use Foo, I don't have to type NewFoo() in a constructor... I just reference *Foo.
By the way, mockery [1] generates super easy to use mocks for exactly this workflow.
Absolutely. This is true in most, if not all, languages. But when you're in the driver's seat, able to make choices like whether or not to use fx, why would you bolt on testing after the fact?
Not much you can do about legacy code, if that's what you have to deal with, but when dealing with legacy code it is unlikely the authors used fx, so...
Unless you are trying to add tests to legacy code, where you also wouldn't have much say in the use of fx, wouldn't the stated testing difficultly already force you away from that dependency mess?