
Outsmarting Go Dependencies in Testing Code - orangechairs
https://www.cockroachlabs.com/blog/outsmarting-go-dependencies-testing-code/#
======
domsj
I don't get it: why would they need/want to set up a server to test the sql
module in the first place? The behaviour of the sql module doesn't depend on
server, so logically I would assume the tests shouldn't either... And if some
tests really need a server, then they are not testing the sql layer anymore,
and shouldn't need access to internals of the sql module.

~~~
radub
The sql module needs other aspects of the server set up to function (e.g. the
KV layer). You are free to browse the code at
[https://github.com/cockroachdb/cockroach](https://github.com/cockroachdb/cockroach)
if you want a more in-depth look.

------
jacques_chester
I guess my question is: what is the level of test?

If it's a feature or integration test, why is it a problem to pull in the
other packages?

If it's a unit test, why not mock out the other dependencies to focus on the
narrow behaviour?

Go makes the latter both easier (implicit interface typing) and harder (lots
of libraries only providing physical structs).

I have to admit I've grown nervous about tests that require you to crack open
an object to inspect its internal state. I've often seen designs that "need"
private variable inspection recomposed into a more functional style or broken
into smaller, cooperating modules/objects.

Put another way: if the module's behaviour is so complex that you _must_
inspect private state to assure behaviour, perhaps the module is too complex
overall.

Of course YMMV, consult a doctor etc etc

~~~
radub
The relevant tests fall somewhere between unit tests and integration tests.
Many are intended as unit tests, but we prefer to test them on top of the
"real" implementations of the layers underneath (instead of mocks) so they are
integration tests as well.

> If it's a feature or integration test, why is it a problem to pull in the
> other packages?

The compiler does not allow the cyclic dependency (even if it's only caused by
testing code).

~~~
jacques_chester
It's been a while since I touched golang, so I don't recall the nitty-gritty
of the cyclic problem.

And to pick a nit, to _me_ , if there are several units of your system pulled
in, it's arguably not a unit test. It's an integration test.

I don't love mocks, but I don't hate them either. They have their place as
part of an overall testing pyramid.

~~~
sirclueless
I write a lot of these tests in my code as well. They are not exactly unit
tests, because they test code at multiple levels of abstraction at once, but
they are written in exactly the same way as a unit test and are intended for
the same purpose: to make sure that some unit of your code functions as it
should and does not stop functioning.

To me, mocks are useful for one thing only: there is some expensive or flaky
dependency of your code that you may have poor visibility into, and you want
to write a test that does not rely on its availability. Mocks for databases,
file systems, REST APIs, externally maintained libraries, etc. all make sense
to me. But if some library is developed locally (or in the extreme case, by
you) and you can easily depend on its existence and stability, then I see no
reason not to write code that depends on that library and test against that
library. This is a testing strategy for a pragmatist, not a purist.

Go in particular makes it easy to separate a single codebase into separate
logical packages, and if you do so, it often makes sense to test them
together. That's why this is a pretty Go-specific issue.

------
kough
This is very clever, and I wish I had figured this out when I ran into the
same problem last summer. A _little_ "smelly" but better than being forced to
publically export implementation details.

That said, to play devil's argument, why not just make all implementation
details public? I will say I remember being pissed off by Go packages that
don't publicize all of their types and interfaces, which made writing tests
against their behavior (usually requiring mocking, hence the need for the
publicized types/interfaces) to be painful/impossible.

~~~
radub
Thank you!

Making everything public runs the risk of other modules using internal
implementation details, and that entanglement makes changing things a lot
harder.

For a small project where you can easily control this, it's likely not an
issue. But we are big enough that no one person knows all the code changes
that are going on in all modules.

Exported vs non exported also serves as implicit documentation, it makes
understanding things easier when you can tell right away what is internal and
what is not.

