How to mock a file with interface imported from another file

3.2k views Asked by At

I try to create a mock of a file with an interface imported from another file. I have try with `aux_files` and `imports` but I did non succeed to have a correct mock file. I think I'm missing something.
So I have a `mockgen/main.go` like this :
package main
import (
    "log"
    o "mockgen/otheri"
)

type bar struct {
    a o.Foo
}

func NewBar(a o.Foo) *bar {
    return &bar{a}
}

func main() {
    var t = NewBar(&o.FunctionStuct{})
    if err := t.a.Ask(); err != nil {
        log.Fatal(err)
    }
}

And the interface imported is in `mockgen/otheri/otheri.go` :
package otheri

import "log"

type Foo interface {
    Ask() error
}

type FunctionStuct struct {
}

func (f *FunctionStuct) Ask() error {
    log.Println("Hello")
    return nil
}

The command I tried is :
mockgen -source main.go -aux_files o=otheri/otheri.go executed at the same level as the main.go
But my mockgen file is empty....
Does anyone has an idea ? My goal is to mock the interface o.Foo contains in main.go me without changing my architecture
I need to mock it to test it with unit tests. The architecture is like this because I follow clean architecture. Thanks for all

1

There are 1 answers

0
isavinof On

You can generate mocks only for interfaces. So, in your example you should run mockgen for file mockgen/otheri/otheri.go because target interface presented where.

But as Elias Van Ootegem pointed out, it's a bad practice to have an interface with the struct which conforming it. You should separate interface and implementation. So, it should be something like:

File /bar/bar.go

package bar

import (
    "log"
)

type Foo interface {
    Ask() error
}

type bar struct {
    foo Foo
}

func NewBar(a Foo) *bar {
    return &bar{a}
}

func (b *bar) Ask() {
    if err := b.foo.Ask(); err != nil {
        log.Fatal(err)
    }
}

File otheri/otheri.go

package otheri

import "log"

type FunctionStruct struct {
}

func (f *FunctionStruct) Ask() error {
    log.Println("Hello")
    return nil
}

File main.go

package main

import (
    "bar"
    "otheri"
)

func main() {
    fs := &otheri.FunctionStruct{}
    b := bar.NewBar(fs)
    b.Ask()
}

And generate a mock mockgen -source=bar/bar.go -destination=bar/mock/foo_mock.go Foo

Furthermore, follow the rules described in effective go the best way to use your FunctionStruct - hide the type in the package:

If a type exists only to implement an interface and will never have exported methods beyond that interface, there is no need to export the type itself

So, the final solution will move interface to a separate package:

File /foo/foo.go

package foo

type Foo interface {
    Ask() error
}

File /bar/bar.go

package bar

import (
    "log"
    "foo"
)

type bar struct {
    foo foo.Foo
}

func NewBar(a foo.Foo) *bar {
    return &bar{a}
}

func (b *bar) Ask() {
    if err := b.foo.Ask(); err != nil {
        log.Fatal(err)
    }
}

File otheri/otheri.go

package otheri

import ( 
   "log"
   "foo"
)

func New() foo.Foo {
    return &functionStruct{}
}

type functionStruct struct {
}

func (f *functionStruct) Ask() error {
    log.Println("Hello")
    return nil
}

File main.go

package main

import (
    "bar"
    "otheri"
)

func main() {
    b := bar.NewBar(otheri.New())
    b.Ask()
}

And mockgen: mockgen -source=foo/foo.go -destination=foo/mock/foo_mock.go Foo