Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

...