How to avoid import cycles in mock generation? - go

Simple example.
I have package xxx. This package contains:
struct A
interface B which is a field of A
struct C which is an argument in method of B
type A struct {
SomeField B
}
type B interface {
SomeMethod(c C)
}
Now imagine I want to create unit test for structure A and mock dependency B. For creating mock I am using mock generator. All mocks are stored in the common "mocks" folder.
The problem is that generated mock has a dependency on xxx package. This is happening because SomeMethod of interface B has argument xxx.C.
Whenever I try to import my mock structure in a_test.go it fails because of cycle import problem. xxx package importing mocks package in the a_test.go. and mocks package imports xxx package in my generated mock.
I need a peace of advice, what is the best workaround for this? Maybe my approach is not idiomatic enough. Where do you store your mocks?

You need to put your test under a different package.
a.go is under package xxx
a_test.go is under package xxx_test
a_mock.go is under package xxx_mock
This way a_test.go will be dependent on xxx and xxx_mock and will not cause dependency cycle.
Also, a.go and a_test.go can be under the same folder, like this:
xxx/
- a.go
- a_test.go
mock/
- a_mock.go

Since
interface
struct
user code
unit test for the user code
are all in the same package, the interface should be considered as "in-package" interface, which is invisible for code in other package. So, the mock for this interface should be IN THE SAME PACKAGE as the interface itself.
Conclusion:
interface
struct
user code
unit test for the user code
mock for the interface
PUT ALL OF THEM IN THE SAME PACKAGE.
So put the mock code generated by gomock into the same package as the interface, but not "mock" package. Example(windows version):
mockgen -source=.\foo\bar.go -destination=.\foo\bar_mock.go -package=foo

Compiling a_test.go as different package (ex. a_test) will resolve the cirucular import issue.
This is the io_test.go example from go language source. Associate question with example by replacing package xxx with package io and a_test with io_test.
//io_test.go
package io_test
import{
. "io" //wildcard import
}
// io_test code
However there wont be access to non-exported type in package io. To make the non-exported type to exported type we could add a file export_test.go and define a exported and assign non-exported type to exported type.
//export_test.go
package io
// exported for test
var IOExportedType= ioNonExportedType

Use a top level package that all other packages import from. Put your interfaces there.
For instance:
domain/
interfaces.go
a/
mock.go
b/
mock.go
c/
mock.go
a, b and c should import from domain so they don't have any dependencies on each other. You will use duck typing to implement the interfaces of the domain package in your mocks.
Here's an actual use case using your example:
domain/interfaces.go:
type A interface {
Foo()
}
type B interface {
Bar() string
}
type C interface {
Baz() string
}
a/mock.go:
type A struct {
SomeField domain.B
}
// ...
b/mock.go:
type B struct {
SomeMethod(c domain.C)
}
// ...
c/mock.go:
type C struct {}
// ...
That should compile just fine, because all the mocks import from the top level domain package, and they all implement the respective interfaces.

In case of avoiding self referencing in Go 1.17
Add this arg
-self_package github.com/xxx
So
mockgen -source=<srcName>.go \
-package <pkgName> \
-self_package github.com/<user>/<repo>/<pkgName> \
-destination <dest>.go

Related

Go import scope lookup for protobuf types

I'm building a transpiler and need to understand the protobuf/go scope lookup system. I've been trying to google the docs and finding no luck.
Q: Is there a shared package scope lookup that you can do when importing Types in Go/protobufs?
Here is the example that I'm questioning:
proto1:
package cosmos.crypto.keyring.v1;
...
message Ledger {
hd.v1.BIP44Params path = 1;
}
proto2:
package cosmos.crypto.hd.v1;
message BIP44Params {
...
}
There are two syntaxes I've seen that do make sense so far:
full scope
message Ledger {
cosmos.crypto.hd.v1.BIP44Params path = 1;
}
Or I’ve also seen versions like this
completely unscoped
message Ledger {
BIP44Params path = 1;
}
partially scoped?
But the style I'm seeing is partially scoped
message Ledger {
hd.v1.BIP44Params path = 1;
}
Is the reason they leave off the cosmos.crypto because these two packages share cosmos.crypto in the root of their package name?
Or is it a more generic scope lookup based on the import?
Any insight or reading links appreciated :)
I'm not sure I fully get the question but I will try to answer. Let me know if you need me to change that.
This is a combination of both. You need to have the package and import the .proto file. Let me explain. If you have two file define like this:
proto1.proto
syntax = "proto3";
package cosmos.crypto.keyring.v1;
message Ledger {
hd.v1.BIP44Params path = 1;
}
proto2.proto
syntax = "proto3";
package cosmos.crypto.hd.v1;
message BIP44Params {}
trying to compile will tell you that "hd.v1.BIP44Params" is not defined. This is because the proto1.proto is not aware of other definitions. Now, if you import "proto2.proto"; in the proto1.proto, it will be aware of the BIP44Params definition and will notice the package definition.
With this package definition, it will be able to access the following type definition:
cosmos.crypto.hd.v1.BIP44Params - which is pretty self explanatory
hd.v1.BIP44Params - because the two package matches before the hd part.
but it should be able to access:
BIP44Params - because there is no such type defined in cosmos.crypto.keyring.v1 package
Hope that's clear

Access private fields of a package in golang using reflect [duplicate]

This question already has answers here:
How to access unexported struct fields
(4 answers)
Closed 1 year ago.
I am writing a unit test for a package in golang. I need to create a variable of private field type which resides in a different package to pass it as a parameter to the function that I am testing.
package : go/abc/a.go
type newType int
package : go/edf/b.go
import "go/abc"
func init(abc newType){
// ommitted for brevity
}
Now I am writing unit test for b.go say
package : go/edf/b_test.go
func TestInit(){
// Now I need to create a variable of private field type newType in abc package
// for passing as parameter to the init function....
}
Is it possible to achieve this using reflect or packages package in golang
I need to access a private field in a package
You cannot do this. The sole purpose of being unexported (what you call private) is to be inaccessable from outside.

Referencing a enum defined in another proto

In A.proto, I defined an enum called Foo. Then in B.proto, I referenced this enum as:
message Bar {
.pathofA.Foo foo = 1;
}
Notice that A.proto does not have a go_proto_library declared. It's only in java. While B.proto has both go_proto_library and java_proto_library declared.
Now in C.go, I'm trying to build a Bar object, but I continuously got errors when trying to set the value for Foo. I could not import A.proto in C.go, as A does not have a go proto library. Would there be a way to reference/set value for Foo in C with only B.proto imported?
Many thanks for any help!

Passing function as parameter: Incompatible types in Golang

Fairly new to Go, but still no idea why this isn't working. There's a function (A) being defined and passed as argument to another function (B), which takes A's arguments in the exact correct way (AFAIK):
// Function A
func writeToPumps(keys []interface{}, job *health.Job, startTime time.Time) {..}
// Function B
func GenerateDemoData(start time.Time, days int, orgId string, writer func(keys []interface{}, job *health.Job, startTime time.Time)) {...}
//calling Function B with Function A as last argument:
demo.GenerateDemoData(time.Now().AddDate(0, 0, -30), 30, buildDemoData, writeToPumps)
That yields:
./main.go:191:56: cannot use writeToPumps (type func([]interface {}, *"tyk-pump/vendor/github.com/gocraft/health".Job, time.Time)) as type func([]interface {}, *"github.com/TykTechnologies/tyk-pump/vendor/github.com/gocraft/health".Job, time.Time) in argument to demo.GenerateDemoData
How can I fix this?
EDIT
Import section in main.go:
import (
"github.com/TykTechnologies/tyk-pump/analytics/demo"
"time"
"os"
"fmt"
"github.com/gocraft/health"
"gopkg.in/vmihailenco/msgpack.v2"
"github.com/TykTechnologies/logrus"
"github.com/TykTechnologies/logrus-prefixed-formatter"
"github.com/TykTechnologies/tyk-pump/analytics"
"github.com/TykTechnologies/tyk-pump/pumps"
"github.com/TykTechnologies/tyk-pump/storage"
"github.com/TykTechnologies/tykcommon-logger"
"gopkg.in/alecthomas/kingpin.v2"
)
Other useful info:
GOPATH: /home/nilsnolde/dev/go/uni
Project structure:
../uni/src/
|
--tyk-pump/
|
--vendor/
main.go
...
Your code should live under /uni/src/github.com/TykTechnologies/tyk-pump. Since it doesn't, when your code internally references that package hierarchy, it re-vendors your code, generating duplicate import paths.
So the fix is probably just to move /uni/src/tyk-pump to /uni/src/github.com/TykTechnologies/tyk-pump then re-import all your vendored packages.
The file in which function A is defined is likely not the same file where you have B defined. The two files are both importing health, where the definition of health.Job seems to live.
However they seem to be using a different import path, therefore GO thinks that the definition of health.Job isn't the same.
You should check the import statements in both places and make them the same. If they are already the same, there is something in the project setup that needs to be adjusted, but there isn't enough context to figure out what that might be.

Golang Circular import structs

My code is simple.
Project/
main.go
pokemons/
pokemon.go
pokeTrainers/
pokeTrainer.go
I have some Pokemon trainers which are defined like this :
package pokeTrainers
import "../pokemons"
type PokeTrainer struct {
name string
pokemon []Pokemon
}
And some Pokemon :
package pokemons
import "../pokeTrainers"
type Pokemon struct {
name string
pokeTrainers PokeTrainer
}
main package is:
package main
import (
"fmt"
"./pokemons"
"./pokeTrainers"
)
func main() {
fmt.Printf("ERROR CIRCULAR IMPORT")
As always when I have an error, I copy-paste it on google. I learn what's a circular import and how people fix it with interface to use methods from another package without import it. But in my case, the problem is not to use methods but a real Type defined in another package.
So you will probably say: "Man you are dumb ! You just have to put everything on the same package and it's fine !"
Sorry, I come from Java and I would like that my code stays clean. ;)
I saw this link which suggest to use a third party packages ? But you have to imagine that I don't have only pokemon and pokeTrainer, I have many more packages with a type inside and a lot of methods..
What do you think ? How can I fix this error proprelly ? I'm a beginner in Go
Either use a single package, so no import is needed.
Or designate one of your packages to be a "master", and put "everything" that is needed from both packages into this "master", and so only the other package needs to import "master" by definition (and "master" doesn't need to import the other).
Or you have to create a 3rd package for the stuff that is needed from both, and your 2 current packages only have to import this 3rd common package.
P.S. And don't use relative imports. Always use the complete package path.
I suggest you using interface for Pokemon. Perhaps you will add a several type of a pokemon and PokeTrainer should to train each one.
Example:
poketrainer.go
package pokeTrainers
type IPokemon interface {
train() error
}
type PokeTrainer struct {
name string
pokemon []IPokemon
}
pokemon.go
package pokemons
import "github.com/pokemonWorld/pokeTrainers"
type Pikachu struct {
name string
sensei *PokeTrainer
}
func (o *Pikachu) train() error {
// your code here
}
One extra point of a recommendation - use a pointer of structure but not as a value.

Resources