How to share Golang package test data files with consumers? - go

We've created a private go library (utility package) to share common api methods and business logic among services. In the utility package we have dozens of mock JSON responses in '.json' files, for mock api testing.
The services consuming this utility package would also like to access the same mock files, as they rely on the same mock api responses to test various internal business logic. Is there a way I can share these files, either by some relative file path, or precompiling them (bytes or strings) to allow consumer tests to reference the same .json files (filepath or data) via standard pkg variables or methods, after importing via go get github.com/whatever/utility-library?
Ideally, consumer tests can access these files via a sub package (like "http/httptest"), and then reference the internal .json files in their own mock servers (like httptest.GetBusinessObject.Response []byte or ResponseFilePath string, etc). We want to continue storing the mock responses in .json files, within the same utility module, but simply expose them to consumer test files, strictly for testing purposes.
my-api-pkg
├── go.mod
└── api
└── api.go
└── api_test.go // <= we currently access .json files here like utiltest.apiResponse []byte
└── apitest // <= sub pkg that contains testing methods and .json accessors
└── apitest.go
└── responses
└── api.response.json
my-service-pkg
├── go.mod
├── server.go
├── server_test.go
└── sub-pkg
└── subpkg.go
└── subpkg_test.go // <= want to access utiltest.apiResponse []byte for api mocks here

Non-Go files and _test.go files are not compiled into a module. To publish _test.go files, rename them to .go files and export the variables and functions that you want to expose to your clients.
For non-Go files, as of Go 1.16, embed them:
package mycompany.com/common/testing
import _ "embed"
//go:embed responses/api.response.json
var MockApiJsonResponse []byte // or string
where the directory tree looks like:
testing
└── testing.go
└── responses
└── api.response.json
Then your clients will be able to reference those exported variables and functions as usual:
package mycompany.com/service
import (
"testing"
common_testing "mycompany.com/common/testing"
)
func TestThings(t *testing.T) {
mock := common_testing.MockApiJsonResponse
// ...
}

OP Answer! It appears my issue was that there was a single imported utility fn in my apitest package, from it's own internal testing.go file, which meant it's init() function was running and polluting the upstream pkg test runs.
My initial approach was sound, but the erptest pkg was no longer being downloaded to upstream services after I removed it's internal testing.go import. I changed the structure to reference a testing directory at the root like so, which restored the upsrtream downloads of the apitest pkg:
/my-api-pkg
├── go.mod
└── /api
└── api.go
└── api_test.go // <= we currently access .json files here like utiltest.apiResponse []byte
└── /apitest // <= sub pkg that contains testing methods and .json accessors
└── apitest.go
└── /testing // <= moving the files here re-enabled download and access or erptest code to upstream consumers
└── /files
└── /api.response.json
Here's the basic structure of my apitest pkg exports, to enable access to the files (as []byte) upstream via apitest.Domain().GetApiRoute1.Res1
// accessor
type domainAccessor struct {
GetApiRoute1 getApiRoute1
...
}
func DomainAccessor() domainAccessor {
return domainAccessor{
GetApiRoute1: _GetApiRoute1,
...
}
}
// individual file accessors for each route
type getApiRoute1 struct {
Res1 []byte
Res2 []byte
}
var _GetApiRoute1 = getApiRoute1{
Res1: loadFile("testing/files/api/v1/domain/getApiRoute1.res.json"),
Res2: loadFile("testing/files/api/v1/domain/getApiRoute2.res.json"),
}
The loadfile fn to read the files to []byte
func loadFile(filepath string) []byte {
dir := ""
_, filename, _, ok := runtime.Caller(0)
if ok {
dir = path.Join(path.Dir(filename), "..", "..", "..")
}
fullPath := path.Join(dir, filepath)
body, err := ioutil.ReadFile(fullPath)
if err != nil {
log.Println("Error apitest.loadFile: unable to read file", err)
panic(err)
}
return body
}

Related

Testing script won't

I'm a bit confused why Go test won't work properly.
├── datasource
│ ├── cache.go
│ ├── database.go
│ ├── datasource.go
│ └── datasource_test.go
├── main.go
└── README.md
This is my file directory structure.
I have inside datasource_test.go implemented this https://golang.org/pkg/testing/
i.e.,
package datasource
import "testing"
func TestAbs(t *testing.T) {
got := Abs(-1)
if got != 1 {
t.Errorf("Abs(-1) = %d; want 1", got)
}
}
my main directory is like this,
package main
import "datasource/datasource.go"
func main() {
//
}()
}
}
What am I missing here? I assume it's due to not import.
You can use go test ./... to test all packages. The way you import the packages is wrong as well as you don't need to import the packages for testing purposes. go test command will help you run your tests.

Import a github managed go package

I am new to go module building. What im trying to do is to create a go library with commonly used methods that other developers can use in their go code.
So first in my internal github, Ive created a repo https://internalgithub/<org>/lib-go.git
The repo structure looks like
$ tree
.
├── README.md
├── go.mod
├── go.sum
└── main.go
And the main.go file has the following content
package main
import (
"flag"
"os"
"path/filepath"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
func main() {}
func someFunctinDevelopersNeed(cluster bool) kubernetes.Interface {
if cluster == false {
var kubeconfig *string
.
.
return clientset
}
The code is available in the master branch of my org github.
The next task to to write code that invokes this library and in turn invokes the methid someFunctinDevelopersNeed
So in my app code, my main.go looks like
package main
import "internalgithub/<org>/lib-go"
func main() {
clientset = someFunctinDevelopersNeed()
.
.
}
But when I try to do go get -t -v internalgithub/<org>/lib-go, I get
runtime.main_main·f: function main is undeclared in the main package
Is there something I have missed ?
EDIT:
Based on suggestions, now I have done the following
$ tree
.
├── README.md
├── go.mod
├── go.sum
└── kubernetes
└── kubernetes.go
And kubernetes.go starts with
package kubernetes
import (
"flag"
"os"
"path/filepath"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
func someFunctinDevelopersNeed(cluster bool) kubernetes.Interface {
if cluster == false {
var kubeconfig *string
.
.
return clientset
}
Even with these changes, I run into the same error
If you write your own library, do not add main package.
You can see some popular golang libraries, like go-gin(uses gin.go ), gorm (uses gorm.go)
For your case, if you just want to create a collection for most commonly used functions, you can add a package utils and put your implementation into utils.go or other xxx.go files

Issue with go package declaration containing more than 2 words separated by underscore

everyone, I'm confused by what I'm seeing; I have the following tree:
├── go.mod
├── main.go
└── server
├── server.go
└── server_integration_test.go
Let's say my module name (mod.go) is gotest. Content of server.go:
package server
type MyStruct struct {
Hello string
}
func (m MyStruct) SayHello() string {
return m.Hello
}
Contents of server_integration_test.go:
package server_integration_test
import (
"testing"
)
func TestIntegration(t *testing.T) {
t.Errorf("just gonna fail!")
}
And finally my main.go`:
package main
import (
"fmt"
"gotest/server"
)
func main() {
my := server.MyStruct{Hello: "my-struct"}
fmt.Println("from mystruct", my.SayHello())
}
When I run go build (or go test ./...), I receive the following error:
main.go:5:2: found packages server (server.go) and server_integration (server_integration_test.go) in /tmp/gotest/server
But if I change my server_integration_test.go to be:
package server_test
// ...
Everything works.
Can someone please explain what's happening here?
The supported package names for server package tests are server and server_test.
See test packages:
'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go". These additional files can contain test functions, benchmark functions, and example functions. ...
Test files that declare a package with the suffix "_test" will be compiled as a separate package, and then linked and run with the main test binary.
The _test suffix is applied to the name of the package under test (the documentation can be improved to make this fact more explicit).

Import specific package

I'm trying to solve a dependency problem : let's say I want to keep the code in my main.go totally decoupled from my database, I created 2 packages for that : dummy & postgres.
/app/
-- main.go
/dummy/
-- dummy.go
/postgres/
-- postgres.go
Everything is working fine, I just have to select in my main.go which package I would like to import to get a behavior or another... but is there a way to choose that when building main.go ?
If there's a more idiomatic way to achieve this, I'm of course very interested !
You can utilize the Go conditional build via build tags and target your compilation of main.go.
Refer this article and put your thoughts into action.
For Example:
Directory structure
build-tags
├── build-tags
├── dummy
│   └── dummy.go
├── main_dummy.go
├── main_postgres.go
└── postgres
└── postgres.go
Sample implementation:
dummy/dummy.go
package dummy
import "fmt"
func PrintName() {
fmt.Println("My name is dummy package")
}
postgres/postgres.go
package postgres
import "fmt"
func PrintName() {
fmt.Println("My name is postgres package")
}
main_dummy.go
// +build dummy
package main
import "build-tags/dummy"
func main() {
dummy.PrintName()
}
postgres.go
// +build postgres
package main
import "build-tags/postgres"
func main() {
postgres.PrintName()
}
Now let's build targeting dummy tag, same way you can do for postgres tag.
go build --tags="dummy"
# run the program
./build-tags
My name is dummy package

Undefined Variables Within a Package During Build

I have two files within one package named db, one of which has a few unexported variables defined. Another one is a test file and would need to use these variables like so:
(This is the structure of the project)
$GOPATH/src/gitlab.com/myname/projectdir
├── main.go
└── db
├── add.go
└── add_test.go
(Here is a terse variation of the files)
db/add.go
package db
func Add(x, y int) int {
return x + y
}
// some other functions that use a and b from `add_test.go`
db/add_test.go
package db
import (
"testing"
)
var (
a = 1
b = 2
)
// test function use variables from add.go
func testAdd(t *testing.T) {
result := add(a, b)
if result != 3 {
t.Error(err)
}
}
Running go test within db/ directory passed, but once I ran go run main go it produced the following error:
db/add.go:: undefined: a
db/add.go:: undefined: b
Seems like add.go cannot find a and b from add_test.go during the build.
main.go
package main
import (
"fmt"
"gitlab.com/myname/projectdir/db"
)
func main() {
res := db.Add(1, 2)
fmt.Println(res)
}
Is this because add_test.go is not included during the build?
This is just the way how go tool works.
_test.go files are compiled only when you run go test. When a package is imported from another package any code from its _test.go files is not used.
Try running go build or go install from inside db package. It will fail.
Relative paths are touchy in Go. For one, I think you need to prefix them with import "./db". Another thing is that you should be in your $GOPATH/src location.
Try this:
move your files under the $GOPATH/src/project and $GOPATH/src/project/db directories.
prefix your import path with ./db for the DB package.
As for the IDE, that's all up to whatever plugins you are using. Try running the tools yourself: golint, go vet, oracle, etc to see the actual go warnings and errors.
Test functions should start with Test. that is what the documentation says.
func TestAdd(t *testing.T) {
result := Add(a, b)
if result != 3 {
t.Errorf("expected 3, got %d ", result)
}
}
Cheers.

Resources