Hacker News new | past | comments | ask | show | jobs | submit login

Wow! No wonder you find this tedious.

Your gateway struct hopefully looks something like

    type Fooer interface{ ... }
    type Barer interface{ ... }
    type gateway struct{ f Fooer; b Barer; ... }
    newGateway(f Fooer, b Barer, ...) (*gateway, error) { ... }
    func (g *gateway) StartTransaction(...)
That is, a gateway is something that depends on other things, modeled as interfaces, and provides capabilities as methods.

                 +--------------------+
                 | gateway            |
                 | - f Fooer          |
                 | - b Barer          |
                 | - ...              |
                 |                    |
    input -------> StartTransaction -----> output
                 | ...                |
                 +--------------------+
When you want to exercise this code, you want to construct an instance with mock/deterministic dependencies, so that you have predictable results when you apply input and receive output. That's the model: give input, assert output.

But your linked code is kind of different! Each subtest varies not the input but the behavior of the mocked dependencies. I understand the point: you want to run through all the codepaths in the gateway method. But is that worth testing? Do the tests meaningfully reduce risk? I dunno. It's not obvious to me that they do.

The use of gomock is also a big smell. Generating mocks kind of defeats the purpose of using them. I would definitely write a bespoke client:

    type mockClient struct {
     StartTransaction func(...) (xxx.Transaction, error)
     Upsert           func(...) (xxx.Result, error)
     AbortTransavtion func(...) (xxx.Xxx, error)
    }
Then each test case is simpler to express as

    for _, tc := range []struct{
     name        string
     client      *mockClient
     input       UpdateConfigRequest
     startRes    xxx.Transaction
     startErr    error
     upsertRes   xxx.Result
     upsertErr   error
     res         xxx.UpdateConfigResponse
     err         error
    } {
     {
      name:      "success",
      client:    &mockClient{StartTransaction: good, ...},
      startRes:  ...,
      upsertRes: ...,
      res:       ...,
     },
     {
      name:      "bad start",
      client:    &mockClient{StartTransaction: bad, ...},
      startErr:  ...,
     },
     {
      name:      "bad upsert",
      client:    &mockClient{StartTransaction: good, Upsert: bad, ...},
      startRes:  ...,
      upsertErr: ...,
     },
     ...
    } {
     t.Run(tc.name, func(t *testing.T) {
      g := newGateway{tc.client}
      output := g.StartTransaction(tc.input)
      // asserts
     })
    }



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: