I have a simple method, which just checks if a parameter is empty and then calls one of 2 methods based on a struct field.
How would I test it?
func (cT *customType) someMethod(message string) {
if message == ""{
return
}
if cT.value == nil {
cT.doFunctionOne()
} else {
cT.doFunctionTwo()
}
}
In Javascript, I would create a spy on doFunctionOne()
and mock up the object.
Mocking up works well in Go
as well, but how would I do the 'spying' part?
Or is there another idiomatic way to test this kind of method?
First: You wouldn't name a method "doFunctionOne" but "methodOne" :-)
If neither doFunctionOne nor doFunctionTwo has any observable effect, then there is absolutely no point in testing it. So we may assume that they do have observable side effect, either on the environment or on the customType they have been invoked on.
Now just test these side effects. This is trivial if both methods do return. If they spin up an endless loop it becomes harder, but still doable.
IMHO there is no need to "test" this method in the sense of "test whether it calls One or Two depending on value": For me this is to lowlevel, too much increasing coverage count for nothing. If you call some method it has to do something (observable effect) and you should check this effect, not the inner workings.
The idiomatic way of mocking an object in Go is to make it explicit. A healthy interface should be testable by itself. So if we have something like this:
type customType struct {
value int
}
func (c customType) doFunctionOne() {
fmt.Println("Method #1")
}
func (c customType) doFunctionTwo() {
fmt.Println("Method #2")
}
func (c customType) someMethod() {
if c.value <= 0 {
c.doFunctionOne()
} else {
c.doFunctionTwo()
}
}
We have to provide a way to change the implementation of doFunctionOne
and doFunctionTwo
explicitly. We can generalize the someMethod
behavior using interfaces:
type customType struct {
myValue int
}
func (c customType) doFunctionOne() {
fmt.Println("Method #1")
}
func (c customType) doFunctionTwo() {
fmt.Println("Method #2")
}
func (c customType) value() int {
return c.myValue
}
type Interface interface {
value() int
doFunctionOne()
doFunctionTwo()
}
func someMethod(i Interface) {
if i.value() <= 0 {
i.doFunctionOne()
} else {
i.doFunctionTwo()
}
}
type customTestingType struct {
t *testing.T
}
func (c customTestingType) doFunctionOne() {
c.t.Log("Working")
}
func (c customTestingType) doFunctionTwo() {
c.t.Error("Not working")
}
func (c customTestingType) value() int {
return 0
}
func TestInterface(t *testing.T) {
someMethod(customTestingType{t})
}
Surely there will be more ways to provide this behavior but it depends on the particular declaration of your type. As an example, you can look at httptest
package. That said, if you really want to mock your type in that way (nonidiomatic), you can some unsafe monkey patching:
package main
import (
"fmt"
"reflect"
"github.com/bouk/monkey"
)
type customType struct {
myValue int
}
func (c customType) doFunctionOne() {
fmt.Println("Method #1")
}
func (c customType) doFunctionTwo() {
fmt.Println("Method #2")
}
func (c customType) someMethod() {
if c.myValue <= 0 {
c.doFunctionOne()
} else {
c.doFunctionTwo()
}
}
func main() {
c := customType{0}
monkey.PatchInstanceMethod(reflect.TypeOf(c), "doFunctionOne",
func(c customType) {
fmt.Println("Method #1, but patched")
})
monkey.PatchInstanceMethod(reflect.TypeOf(c), "doFunctionTwo",
func(c customType) {
fmt.Println("Method #2, but patched")
})
c.someMethod()
}
But, if you are creating a function factory it would be better if you return the function to the caller and the caller will run the function.
Then testing is straightforward.
Second but, there is only two kind of functions, flow function and logic functions. You mixed flow and logic in the same function. Testing difficulty is just one symptom of this bad practice.