- A library contains a struct type that exports method
A
which is computed based on unexported field dataa
. - Since
a
is unexported, directly constructing the type will yield empty field data. - The only place
a
is set is when the library instantiates the type in ways not easilsy replicable using exported functions/fields (e.g. data is populated from network API calls)
type S struct {
a []byte
}
func (s *S) A() ... {
// produces a meaningful result using the blob in field a
}
...
type I interface {
DoLibraryThing() (*S)
...
}
struct Client type {...}
func (c *Client) DoLibraryThing() (*S) {
...
}
...
The program I want to write tests for uses DoLibraryThing
and notably depends on and manipulates the result of S.A()
func testMe(client I) {
// do a bunch of things with the client interface
s := client.DoLibraryThing()
data := s.A()
// do more things with client interface and data
}
I have mocked the library Client
that returns this type, but am unable to return an appropriate instance such that calling A
will yield useful data to be used in the remainder of the test. The function being tested makes a number of other calls to the Client/library.
Is there a way to construct the type with a
seeded with useful data or otherwise substitute a mock S
that can return useful data from A
? Having the Client mock return a different struct that exports A
with hardcoded behavior doesn't work since it's not a *S
when being returned from the mock.
Alternatives considered:
- Split the function at the point of this library call, testing the behavior before and after the call, and providing the results of
A
to the test(s) as test inputs when testing the latter portion of the function. Splitting the function here would only really be meaningful for test and would involve the otherwise needless complexity of passing state from first part of function implementation to second part of function implementation.
func testMe(client I) {
kludge, data := testMe1(client)
return testMe2(kludge, data)
}
func testMe1(client I) {
// do a bunch of things with the client interface
s := client.DoLibraryThing()
data := s.A()
return Kludge{ /* pack up all the state */ }, data
}
func testMe2(k Kludge, d data) {
// unpack all the state
// do more things with client interface and data
}
^ testMe1 and testMe2 can now be independently tested and predetermined test data can be passed in when testing testMe2.