Package structure & testing

115 views
Skip to first unread message

Salvatore Domenick Desiano

unread,
Jun 19, 2023, 1:04:24 AM6/19/23
to golang-nuts
I've been using Go for almost a decade and I still don't have a library package structure I like. I'm hoping I can post what I want here and someone can tell me either (a) what the idiomatic way of doing this is, or (a) how to break the dependency cycle.

Let's imagine we're building the fancy Gopher Client library. What I want is:
  • gopher: public POD (plain old data) types, public interfaces, func NewClient(), struct clientImpl
  • gopher/testing: func NewFakeClient(), struct fakeClientImpl, all testing-specific code (not gopher _test files... just code for other libraries that want a fake of this one)
  • gopher/internal: internal types, implementation details shared between gopher and gopher/testing.
This doesn't work. Having POD types in gopher means that everything else has to depend on it. This means that NewClient() can't be in gopher because it would depend on gopher/internal (shared implementation details) and gopher/internal depends on gopher (PODs).

At various points I've tried:
  • Collapse gopher and gopher/internal. This works, but requires private types to be available to gopher/testing.
  • Collapse all three. This works but I really don't like having Fake code in the same package as real code.
  • Create gopher/gophermodel for the PODs and interfaces. This works, but it's really ugly to have to refer to the PODS as (for example) gophermodel.Response.
  • Create gopher/gopherclient for NewClient() and clientImpl. This is probably the least ugly but it still feel strange to call gopherclient.New() and have the PODs in gopher.
What am I missing?

Thank you!

-- Salvatore
smile.

TheDiveO

unread,
Jun 19, 2023, 1:22:03 AM6/19/23
to golang-nuts
Maybe not the best way either, but the POD part might benefit from slight refactoring and you already in part hinted at it.
  • gopher/api -- some PODs here
  • gopher/model -- don't repeat gopher as in gophermodel; some PODs here
It won't ever perfect, so YMMV. But at the same time it allows your module to grow.

Salvatore Domenick Desiano

unread,
Jun 19, 2023, 9:18:02 PM6/19/23
to golang-nuts
Interesting point. Two questions:
  • Is there a standard library example of a client library that provides a fake?
  • I've always been taught that Go packages stand alone so names like "api" and "model" are verboten. Am I wrong?


will....@gmail.com

unread,
Jun 20, 2023, 12:12:51 AM6/20/23
to golang-nuts
>Collapse all three. This works but I really don't like having Fake code in the same package as real code.

This is the answer, in my opinion. Don't split code across packages unless there's a reason to do so. A fake implementation is a feature. If it hurts that much, just call it something more general, like InMemoryClient, and note in the doc that it's only for testing. Keep it simple.
Reply all
Reply to author
Forward
0 new messages