...
Sometimes after choosing a package layout, the Go compiler complains that cyclical dependencies are not allowed.
We can override this in two ways: either using interfaces or using dependency injection. There is also a trick for using helper packages in unit tests.
Interfaces
Suppose you want to define two packages a
and b
, and define a.Foo()
to call b.Bar()
and b.Bar()
to call a.Foo()
.
...
The portion of the cycle that can be imported separately (e.g. package
a
in the example above) must pass its unit tests independently from the cycle. This implies that the package must define valid behavior even when the injection slots are not populated.There is a substantiated argument that the interface-based approach described above is impossible or impractical.
Test packages
Often we want to use a helper package in a unit test, but just in the unit test, and that import creates a dependency cycle.
For this, Go already has a native functionality: test files (*_test.go
) inside a package named X
can declare another package X_test
.
X_test
can import another package that then recursively depends on X
, and this way there is no cycle.
For example, in CockroachDB we often use testserver
in tests. testserver
depends on pkg/server
, which depends on pkg/sql
. So all the tests in the sql
and server
package that want a test server cannot be part of the sql
or server
packages; at the top of their source file we see package sql_test
and package server_test.
Separately licensed packages inside CockroachDB: CCL and BSL
...