Suppose I have the following interface:
type UserRepository interface {
StoreUser(user User) (User, error)
}
And the following implementations of that interface:
type InMemoryRepository struct {
...
}
func (repo InMemoryRepository) StoreUser(user User) (User, error) {
...
}
type PostgresqlRepository struct {
...
}
func (repo PostgresqlRepository) StoreUser(user User) (User, error) {
...
}
I want to write a unit test for the StoreUser method. But I only need to write it once since the requirements are the same for all the implementation of that interface.
What's the best design to write the test for the interface and then run it with whatever implementation I have?
So far I came up with either:
a) Wrap the test in a test-case loop with all the implementations
testCases := []struct {
name string
repo UserRepository
}{
{ name: "Memory", repo: NewInMemoryRepository()},
{ name: "Postgresql", repo: NewPosrgresqlRepository()},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing) {
...
}
}
b) Create a test class
type UserRepositoryTests struct {
UserRepository
}
func NewUserRepositoryTests(userRepository UserRepository) UserRepositoryTests {
return UserRepositoryTests{
UserRepository: userRepository,
}
}
func (userRepository UserRepositoryTests) TestStoreUser(t *testing.T) {
...
}
and call it from the implementation package
type InMemoryUserRepository struct{}
func (ss InMemoryUserRepository) StoreUser(user users.User) (users.User, error)
func TestRunInterfaceTests(t *testing.T) {
dsfnjkf := InMemoryUserRepository{}
testing := users.NewUserRepositoryTests(dsfnjkf)
testing.TestStoreUser(t)
}
Although both ways don't sound right to me.
Is there a better approach?