-1

I am trying to mock an struct method in test cases but it is not working. I want to mock Validate method here: `

package main

import (
    "fmt"
)

type DemoInterface interface {
    Inc(int) (int, error)
    Validate(int) error
}
type DemoStruct struct{}

func (l DemoStruct) Inc(num int) (int, error) {
    err := l.Validate(num)
    if err != nil {
        return 0, err
    }
    num = num + 100
    return num, nil

}
func (l DemoStruct) Validate(num int) error {// SOME DB LOGIC IS HERE WHICH I CAN NOT POST at Stackoverflow
    if num > 100 {
        return fmt.Errorf("INVALID NUM %v", num)
    }
    return nil
}

func main() {
    s, err := DemoStruct{}.Inc(10)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(s)

}

`

My test cases:

package main

import (
    "fmt"
    "testing"
)

const (
    SUCCESS = "SUCCESS"
    ERROR   = "ERROR"
)

type MockDemoStruct struct {
    DemoStruct
    functionality string
}

func (m MockDemoStruct) Validate(num int) error {
    switch m.functionality {
    case SUCCESS:
        return nil
    case ERROR:
        fmt.Errorf("MOCK ERROR %v", num)

    }
    return fmt.Errorf("MOCK ERROR %v", num)
}

func TestPath(t *testing.T) {

    t.Run("ERROR", func(t *testing.T) {
        ls := MockDemoStruct{DemoStruct{}, ERROR}
        res, err := ls.Inc(110)
        expected := fmt.Errorf("MOCK ERROR %v", 10)
        if err != expected {
            t.Errorf("NOT MATCH  %v  %v", err, expected)
            //NOT MATCH  INVALID NUM 110  MOCK ERROR 10

        }
        fmt.Println(res)
    })
}

Here MockDemoStruct.Validate is not being called. I am getting INVALID NUM 110 from Validate, but it should be MOCK ERROR 110

Prakash Kumar
  • 2,554
  • 2
  • 18
  • 28

3 Answers3

1

In this case the method Inc in the DemoStruct calls the method l.Validate where l is a DemoStruct. The reciever of that method is explicitly a DemoStruct. So the MockDemoStruct.Validate method will not be called.

Go does not have inheritance as you assumed here in your code. You can not override the method of the DemoStruct. The MockDemoStruct composes the DemoStruct. To actually test this method, I suggest passing the DemoStruct a db interface, which can be mocked in your test.

Blokje5
  • 4,763
  • 1
  • 20
  • 37
  • thanks for your comment, Can you suggest some code restructure that can enable the mocking of Validate method with num > 100 logic only – Prakash Kumar Jan 11 '20 at 12:02
  • If you create an interface for your db interaction you can pass that interface to your `DemoStruct`. In your test you can then mock the database interaction so that you can only test the `num > 100` logic. – Blokje5 Jan 11 '20 at 13:24
0

I think you also need to implement 'Inc' receiver for 'MockDemoStruct', here you trying to overuse the inheritance property of struct, looks like GO doesn't support that.

RK Vats
  • 21
  • 3
0

To make the method mockable we will have to use a DI(dependency injection) based code pattern.

**We can mock only those methods which are injectable**.

We have two options to introduce dependency injection in this code.

  1. Use Delegation Design pattern with the help of interface

  2. Introduce Monkey patching using the function as a type

Delegation using interface:

type Deligation interface {
    Validate(num int) error
}

type DemoStruct struct {
    delegate Deligation
}

func (DemoStruct) Validate(num int) error {
    if num > 100 {
        return fmt.Errorf("INVALID NUM %v", num)
    }
    return nil
}
func (l DemoStruct) Inc(num int) (int, error) {
    err := l.delegate.Validate(num) // Call method using delegate
    if err != nil {
        return 0, err
    }
    num = num + 100
    return num, nil

}

func main() {
    s, err := DemoStruct{delegate: DemoStruct{}}.Inc(10) // assign delegate inside DemoStruct
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(s)

}

Using Monkey patching:

func Validate(num int) error {
    if num > 100 {
        return fmt.Errorf("INVALID NUM %v", num)
    }
    return nil
}

type DemoStruct struct {
    Validate func(num int) error //  function as a type
}

func (l DemoStruct) Inc(num int) (int, error) {
    err := l.Validate(num)// It can be replaced in test cases.
    if err != nil {
        return 0, err
    }
    num = num + 100
    return num, nil

}

func main() {
    s, err := DemoStruct{Validate: Validate}.Inc(10) // assign Validate inside DemoStruct
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(s)

}

Ref: https://blog.myhro.info/2018/06/how-to-mock-golang-methods

Prakash Kumar
  • 2,554
  • 2
  • 18
  • 28