1) I would go with what the elevator said, i.e. only requires the methods you need for this structure. For example, you have a UserService that needs FindByName and FindAll , and a UserAdminService that needs FindById , FindAll and Save . In this case, you should have two interfaces:
UserProvider with FindByName and FindAllUserAdminProvider with FindById , FindAll and Save .
It also allows you to keep your UserProvider in check, for example. you know that it cannot invoke Save , therefore it cannot modify the user.
You will probably only need one real implementation that satisfies both interfaces.
2) Check testify / mock and mockery . Mockery will generate mocks for your interfaces in the mocks , one for each interface. This means that you cannot use the same mock structure for both tests, but that does not matter since the code is generated. You do not mock the behavior of the interface in the mock structure, you do this in the test, setting expectations, for example:
func TestThatYouCantLoginWithNonexistentUser(t *testing.T) { userRepository := new(mocks.UserRepository) userService := user.NewService(userRepository) // if the userService calls UserRepository.FindByName("joe"), it will return nil, since there no such user. userRepository.On("FindByName", "joe").Return(nil) _, err := userService.Login("joe", "password") // err should not be nil and the message should be "user does not exist" assert.EqualError(t, err, "user does not exist") // assert that the expectations were met, ie FindByName was called with "joe" userRepository.AssertExpectations(t) }
This really makes the test easier to understand, since you donβt need to check any other file, which makes the layout when you call FindByName("joe") .
source share