Lets say I have a UserRepository struct which incapsulates logic for interacting with a database. This struct has a set of methods like:
There is another struct (let's call it UserService for instance) which depends on UserRepository struct.
To test UserService I need to mock functionality of UserRepository. The only way I know to do this is to provide the interface for UserRepository and make UserService be dependent on it instead of UserRepository struct. It will allow to create a mocked implementation of interface and set it as dependency of UserService in the test.
What is the most idiomatic way to do it?
1) If UserService depends only on 1 UserRepository's method (let's say findAll) - should I still define an interface which will have all repository methods or it's better to define a separate interface for this method only and use it as a dependency of UserService? If so, what is the best name for it (interface)? If another struct will depend on findAll() and findById() methods should I again create another interface?
2) Where is a best place to store mocks for these interfaces? Is it possible to reuse them? Or for tests of different structs I will need redefine the mocks?
P.S. as for me unit tests is a very important part of the project. I would like to make them as readable as possible avoiding boilerplate code and focusing on their logic. So creating several mock implementations for same interfaces in different test files looks for me a bad option since it makes test code less readable.
At the command line in the greetings directory, run the go test command to execute the test. The go test command executes test functions (whose names begin with Test ) in test files (whose names end with _test.go). You can add the -v flag to get verbose output that lists all of the tests and their results.
Go comes with a testing package which provides support for automated testing of Go packages. The command go test automates the execution of any test function which is found in “*_test.go” files corresponding to the package under test.
1) I'd go with what elevine said, i.e. only require the methods that you need for that struct. So for example: you have UserService
that needs FindByName
and FindAll
, and UserAdminService
that needs FindById
, FindAll
and Save
. In that case you should have two interfaces:
UserProvider
with FindByName
and FindAll
UserAdminProvider
with FindById
, FindAll
and Save
.This also lets you keep your UserProvider
in check, e.g. you know it can't call Save
, so it can't modify a user.
You'll likely only need one actual implementation that satisfies both interfaces.
2) Check out testify/mock and mockery. Mockery will generate mocks for your interfaces in a mocks
subpackage, one for each interface.
This does mean you can't use the same mock struct for both tests, but it doesn't matter, because the code is generated. You don't mock up the behavior of the interface in the mock struct, you do it in the test by setting up expectations, e.g.:
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's 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, i.e. FindByName was called with "joe"
userRepository.AssertExpectations(t)
}
This actually makes the test easy to understand, since you don't have to go check in some other file what the mock does when you call FindByName("joe")
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With