The approach that mostly used in test other than mock is fake --- fakes are merely structs that fit an interface and are a form of dependency injection where the consumers control the behavior. Some distinctions based on their definitions are discussed in this post.
is it a practice that not to mock interface
I'm not sure if there is such a practice, because otherwise there won't be mock libraries at all.
However, there are tradeoff between fakes and mocks regardless of language.
- Mocks increase coupling, and coupling makes refactoring harder (ref), while fakes decrease such coupling.
- (ref) Mocks are assuming the client library is implemented right, and are assuming we know how the library actually behaves. As a result, mocking the library only mocks assumptions and the test is subject to change when the code is updated.
- While fakes have the real implementation of the interface, its most use case is to test how the code is handling errors properly, and it can't test any implementation logic (code path, expected output, argument validation, ..etc) inside. So we still use mock to verify an object returns a value that is set to the tested class, at the same time use Stub to mimic an Interface or Abstract class to be tested.
Should I just make the function to be tested
Functions are generally not mockable. To address this issue, we could either
- Implement a certain interface that the function is allowed to pass into to test the different branches of code. An code example from this post (note this is fake test):
package a
type DoSomethingInterface interface {
DoSomething() error
}
func DoSomething(a DoSomethingInterface) {
if err := a.DoSomething(); err != nil {
fmt.Println("error occurred")
return
}
fmt.Println("no error occurred")
return
}
package a_test
import (
"testing"
"<path to a>/a"
)
type simpleMock struct {
err error
}
func (m *simpleMock) DoSomething() error {
return m.err
}
func TestDoSomething(t *testing.T) {
errorMock := &simpleMock{errors.New("some error")}
a.DoSomething(errorMock)
// test that "an error occurred" is logged
regularMock := &simpleMock{}
a.DoSomething(regularMock)
// test "no error occurred" is logged
}
or,
- Instantiate a dummy object in order to call the method, if the method inside the function is a method of an interface or struct, as discussed in this post.