4

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?

2
  • As is, your method doesn't make sense: you don't use the parameter for anything, and the check isn't related. Makes me think that it should be a function at all… Commented Jun 5, 2015 at 8:12
  • dev.to/gustavolopess/… Nice explanation, scroll to the bottom of article to check example. No third-party framework or libraries needed. Commented Nov 17, 2023 at 10:59

3 Answers 3

5

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()
}
Sign up to request clarification or add additional context in comments.

Comments

3

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.

1 Comment

You're right. I'm still new to testing, Maybe this method wasn' t a good example. I wanted to ask for a general way to do this kind of "spying". Because I often times have these methods which just don't return and only call another method. For example a method, which listens on a channnel, processes the received data and invokes another method with this data as arguments.
0
  1. You can add log before each function call.
  2. Inject your own logger implementation, []string for example.
  3. Check the string slice for a matching strings.

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.