Testing a non-returning method in go

I have a simple method that just checks if a parameter is empty and then calls one of two methods based on a struct field.
How can I check it?

func (cT *customType) someMethod(message string) {
    if message == ""{
        return
    }

    if cT.value == nil {
        cT.doFunctionOne()
    } else {
        cT.doFunctionTwo()
    }
}

      

In Javascript, I will create a spy on doFunctionOne()

and mock object.
Focus works well in Go

, but how can I do the "spy" part?
Or is there another idiomatic way to test this method?

+3


source to share


3 answers


First: you won't call the method "doFunctionOne" but "methodOne" :-)

If neither doFunctionOne nor doFunctionTwo have an observable effect, then there is no point in testing it. Thus, we can assume that they have an observable side effect, both in the environment and in the customType they were called on.



Now, just check out these side effects. It's trivial if both methods return. If they unfold an endless loop, it becomes more difficult, but still doable.

IMHO there is no need to "check" this method in the sense of "check if it names one or two depending on the value": for me this is at a low level, too much increase in the number of visits for nothing. If you call some method, it should do something (observable effect) and you should test that effect, not the inner workings.

+3


source


The idiomatic way to mock an object in Go is to make it explicit. A healthy interface should be tested on its own. 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 need to provide a way to explicitly change the implementation doFunctionOne

and doFunctionTwo

. We can generalize the behavior someMethod

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})
}

      

There will of course be more ways to enforce this behavior, but it depends on the specific declaration of your type. For example, you can take a look httptest

. That said, if you really want to mock your type in this way (non-idiomatic), you can unsafe update monkeys :

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()
}

      

+1


source


  • You can add a log before every function call.
  • Inject your own logger implementation like [].
  • Check your string snippet for matching strings.

But if you are creating a factory function, it would be better if you return the function to the caller and the caller will run that function.
Then testing is simple.

Second, there are only two kinds of functions, flow function and logic functions. You have mixed flow and logic in the same function. The difficulty of testing is just one symptom of this poor practice.

0


source







All Articles