Go test <function> returns undefined: <function> - go

Trying to run "go test sum_test.go" returns an error:
./sum_test.go:18:13: undefined: SumInt8
FAIL command-line-arguments [build failed]
I'm taking an introductory course in golang. Our teacher distributed a code-file, sum.go, and a testing-file, sum_test.go. Trying to run "go test" on sum_test.go returns the error above. The code runs fine on our teachers mac, and he's having difficulties recreating the problem. Here's my go environment settings: https://pastebin.com/HcuNVcAF
sum.go
package sum
func SumInt8(a, b int8) int8 {
return a + b
}
func SumFloat64(a, b float64) float64 {
return a + b
}
sum_test.go
package sum
import "testing"
// Check https://golang.org/ref/spec#Numeric_types and stress the limits!
var sum_tests_int8 = []struct{
n1 int8
n2 int8
expected int8
}{
{1, 2, 3},
{4, 5, 9},
{120, 1, 121},
}
func TestSumInt8(t *testing.T) {
for _, v := range sum_tests_int8 {
if val := SumInt8(v.n1, v.n2); val != v.expected {
t.Errorf("Sum(%d, %d) returned %d, expected %d",
v.n1, v.n2, val, v.expected)
}
}
}
I see no particular errors, so I expected "go test sum_test.go" to run, and succeed. However it seems it can't find the method SumInt8 in sum.go.

$ go help packages
Many commands apply to a set of packages:
go action [packages]
Usually, [packages] is a list of import paths.
An import path that is a rooted path or that begins with a . or ..
element is interpreted as a file system path and denotes the package
in that directory.
Otherwise, the import path P denotes the package found in the
directory DIR/src/P for some DIR listed in the GOPATH environment
variable (For more details see: 'go help gopath').
If no import paths are given, the action applies to the package in the
current directory.
As a special case, if the package list is a list of .go files from a
single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints
in those files and ignoring any other files in the directory.
List all the files in the current directory used in the test:
go test sum_test.go sum.go
or simply test the complete package in the current directory.
go test
For example,
$ go test -v sum_test.go sum.go
=== RUN TestSumInt8
--- PASS: TestSumInt8 (0.00s)
PASS
ok command-line-arguments 0.002s
$
or, for the complete package
$ go test -v
=== RUN TestSumInt8
--- PASS: TestSumInt8 (0.00s)
PASS
ok so/sum 0.002s
$
Option '-v' produces verbose output.
sum_test.go:
package sum
import "testing"
// Check https://golang.org/ref/spec#Numeric_types and stress the limits!
var sum_tests_int8 = []struct {
n1 int8
n2 int8
expected int8
}{
{1, 2, 3},
{4, 5, 9},
{120, 1, 121},
}
func TestSumInt8(t *testing.T) {
for _, v := range sum_tests_int8 {
if val := SumInt8(v.n1, v.n2); val != v.expected {
t.Errorf("Sum(%d, %d) returned %d, expected %d",
v.n1, v.n2, val, v.expected)
}
}
}
sum.go:
package sum
func SumInt8(a, b int8) int8 {
return a + b
}
func SumFloat64(a, b float64) float64 {
return a + b
}

Related

How to print from go test to console without using verbose option

I want to print an informative message to console from a test, but not have the verbose test output.
I have tried printing from the test using:
fmt.Println("my message") // STDOUT
fmt.Fprintln(os.Stderr, "my message") // STDERR
t.Log("my message\n") // testing.T log
which all produce the same effect of showing in the console if go test -v is used, but not showing if just go test is used.
However, with go test -v, I also get all the verbose test output like:
=== RUN My_Test
--- PASS: My_Test (0.07s)
go help testflag says:
-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
but what I want is to not log all tests as they are run, but still print all text from Log and Logf calls even if the test succeeds
Is there a way to print a visible message from within a test, but not see the RUN and PASS messages?
The testing framework "hijacks" the standard output and error streams for obvious reasons. So no matter what, whether writing to those streams appears in the output is controlled by the testing framework, and it provides no means to "customize" it other than showing or hiding all using the -v flag.
What you may do is use the -json testing flag:
-json
Log verbose output and test results in JSON. This presents the
same information as the -v flag in a machine-readable format.
So you get all the output you would otherwise get with -v, but you have a separate JSON object for each line.
Having this test function:
func TestMy_Test(t *testing.T) {
fmt.Println("[custom] my message from fmt.Println")
}
Output of go test -v .
=== RUN TestMy_Test
[custom] my message from fmt.Println
--- PASS: TestMy_Test (0.00s)
PASS
ok github.com/icza/play 0.002s
Output of go test -json .
{"Time":"2022-05-10T09:26:26.712800797+02:00","Action":"run","Package":"github.com/icza/play","Test":"TestMy_Test"}
{"Time":"2022-05-10T09:26:26.71293072+02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"=== RUN TestMy_Test\n"}
{"Time":"2022-05-10T09:26:26.712946548+02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}
{"Time":"2022-05-10T09:26:26.712954637+02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"--- PASS: TestMy_Test (0.00s)\n"}
{"Time":"2022-05-10T09:26:26.712958774+02:00","Action":"pass","Package":"github.com/icza/play","Test":"TestMy_Test","Elapsed":0}
{"Time":"2022-05-10T09:26:26.712964812+02:00","Action":"output","Package":"github.com/icza/play","Output":"PASS\n"}
{"Time":"2022-05-10T09:26:26.713170439+02:00","Action":"output","Package":"github.com/icza/play","Output":"ok \tgithub.com/icza/play\t0.002s\n"}
{"Time":"2022-05-10T09:26:26.713573313+02:00","Action":"pass","Package":"github.com/icza/play","Elapsed":0.003}
You can write a simple app that processes and filters these JSON objects. Or you can filter the output as you could filter any other output.
Output of go test -json . |grep '\[custom\]'
{"Time":"2022-05-10T09:28:24.197077681+02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}
If you also want the "pass" or "fail" messages, run go test -json . |grep '"pass"\|"fail"\|\[custom\]'
{"Time":"2022-05-10T09:29:26.069181336+02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}
{"Time":"2022-05-10T09:29:26.069189228+02:00","Action":"pass","Package":"github.com/icza/play","Test":"TestMy_Test","Elapsed":0}
{"Time":"2022-05-10T09:29:26.069199239+02:00","Action":"pass","Package":"github.com/icza/play","Elapsed":0}
You can create you own Log function and use it for printing on screen
func Log(args ...interface{}) {
fmt.Fprintln(os.Stdout, args...)
}
You can also make it to print log based on condition passed through flag
var p = flag.Bool("p", false, "Enable Local Logging")
func MyLog(args ...interface{}) {
if *p {
fmt.Fprintln(os.Stdout, args...)
}
}
Example
package main
import (
"fmt"
"testing"
"os"
"flag"
)
var p = flag.Bool("p", false, "Enable Local Logging")
func Log(args ...interface{}) {
if *p {
fmt.Fprintln(os.Stdout, args...)
}
}
func IntMin(a, b int) int {
if a < b {
return a
}
return b
}
func TestIntMinBasic(t *testing.T) {
ans := IntMin(2, -2)
if ans != -2 {
t.Errorf("IntMin(2, -2) = %d; want -2", ans)
}
}
func TestIntMinTableDriven(t *testing.T) {
var tests = []struct {
a, b int
want int
}{
{0, 1, 0},
{1, 0, 0},
{2, -2, -2},
{0, -1, -1},
{-1, 0, -1},
}
Log("Print to Screen")
for _, tt := range tests {
testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
t.Run(testname, func(t *testing.T) {
ans := IntMin(tt.a, tt.b)
if ans != tt.want {
t.Errorf("got %d, want %d", ans, tt.want)
}
})
}
}
func BenchmarkIntMin(b *testing.B) {
for i := 0; i < b.N; i++ {
IntMin(1, 2)
}
}
And to pass the flag you can use -args
-args Pass the remainder of the command line (everything after -args) to the test binary, uninterpreted and unchanged. Because this flag consumes the remainder of the command line, the package list (if present) must appear before this flag.
Cmd Example:
go test -args -p
Although this doesn't print during the test, it prints straight after, which is better than not printing at all.
Define an os.File in your test file to write messages to:
var messagesFile *os.File
func messages() *os.File {
if messagesFile == nil {
messagesFile, _ = os.Create("tests.out")
}
return messagesFile
}
os.Create creates a new file if one doesn't exist, otherwise truncates the existing file.
Within your tests, write messages to that file:
messages().WriteString("my message")
Run your tests using go test, then cat the file. In my case, I use make:
test:
go test .
#cat tests.out
Output looks like:
ok <path to tests>
my message

go 1.18 generic compile error when use maps.Copy on map with struct key

I implemented a Set based on generic, and everything ok until i use struct as Set element instead of base type. I got an compliation error.
go version: go version go1.18 windows/amd64
Below code is failed to complie in function AddSet.
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
type Key struct {
A, B int
}
func main() {
s := SetOf(
Key{1, 1},
Key{2, 2},
Key{3, 3},
)
s.AddSet(SetOf(
Key{3, 3},
Key{4, 4},
Key{5, 5},
))
fmt.Println(s)
}
type Set[T comparable] map[T]struct{}
func SetOf[T comparable](vs ...T) Set[T] {
s := Set[T]{}
for _, v := range vs {
s[v] = struct{}{}
}
return s
}
func (s Set[T]) AddSet(another Set[T]) {
maps.Copy(s, another)
}
when run it:
> go run .\main.go
# command-line-arguments
.\main.go:19:10: cannot use &.autotmp_29 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
<autogenerated>:1: cannot use &.autotmp_12 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
if Key only has 1 field, it can be compiled successful.
if i use for v := range another { s[v]=struct{}{} }, it can be compiled successful.
i think it's strange, can someone explain please?
It looks like this compiler error. It is fixed in Go 1.19 and backported to Go 1.18.2.
If you are on an older version, I'd recommend simply forgoing the maps package and doing things by hand, as you already tried. It's just a simple loop:
func (s Set[T]) AddSet(another Set[T]) {
for k := range another {
s[k] = struct{}{}
}
}
#icza's comment of explicitly converting the named map type to its underlying type also works:
maps.Copy(map[T]struct{}(s), another)
In case you use functions that expect more than one map type parameter (with the same constraints), as maps.Equal or maps.EqualFunc, you have to convert both arguments:
func (s Set[T]) Compare(another Set[T]) bool {
// signature is Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
return maps.Equal(map[T]struct{}(s), map[T]struct{}(another))
}
It seems the crash was reproduced also with parametrized map types instantiated with arrays with len >= 2.

Run Golang project with multiple files on Goland

I'm new to the Go language and have a problem when I'm trying to run a simple project which contains two files, on Goland IDE.
The first file called main -
package main
import "fmt"
func main() {
card := list{0, add(0)}
cards := append(card, add(3))
cards = append(card, add(4))
for i, c := range cards {
fmt.Println(i, c)
}
}
func add(x int) int {
return x + 1
}
And the second file called list -
package main
type list []int
When I'm trying to use the second file from the first file (use list type) I get compilation failed and -
command-line-arguments
.\main.go:6:10: undefined: list
What have I missed?
Ok, I got it, the Package option should be chosen instead of the File option -

How to call function from other file

structure
.
├── deck.go
└── main.go
Here is the code in each .go file
main.go
package main
func main() {
cards := newDeck()
cards.print()
}
deck.go
package main
import "fmt"
type card struct {
value string
suit string
}
type deck []card
func newDeck() deck {
cards := deck{}
cardSuits := []string{"Spades", "Diamonds", "Hearts", "Clubs"}
cardValues := []string{"Ace", "Two", "Three"}
for _, suit := range cardSuits {
for _, value := range cardValues {
cards = append(cards, card{
suit: suit,
value: value,
})
}
}
return cards
}
func (d deck) print() {
for i, card := range d {
fmt.Printf("%d) %s of %s\n", i, card.value, card.suit)
}
}
Why I can't run main.go file? please help TT
❯ go version
go version go1.14.3 darwin/amd64
❯ go run main.go
# command-line-arguments
./main.go:4:11: undefined: newDeck
Modules in Golang are determined by their parent folder. Across modules, the object must be capitalized to be exported. This is not your case.
Your error is in the compilation stage; this is similar to gcc when it can't find header files. You have to tell the Go compiler to search all files in the current module.
go run .
This tells go to include all files in the current (.) module (folder). Since newDeck is in a different file and the compiler is only running main, it can't find newDeck. But if you run all files, it will search and find the func in deck.go.
go run main.go runs only that file. You need to do go build . and run the executable.

Call function from another file

Inside my directory /go/src/lodo I have two files, main.go and uniqueElement.
uniqueElement.go
package main
import "fmt"
func unique(a []int) {
var value int
for i:= range a {
value = value ^ a[i]
}
fmt.Println(value)
}
main.go
package main
func main() {
var a = []int{1, 4, 2, 1, 3, 4, 2}
unique(a[0:])
}
I get an error
./main.go:7: undefined: unique
How can I correctly call unique from main?
You probably ran your code with go run main.go that only compiles and runs main.go try running go run main.go uniqueElement.go or building and running the binary generated
Change the name from unique to Unique.
The following codes work for me:
//module github.com/go-restful/article
package article
func IndexPage(w http.ResponseWriter, r *http.Request) {}
This func must be Exported, Capitalized, and Comment added
The use in main.go
//module github.com/go-restful
package main
import (article "github.com/go-restful/article")
func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/", article.IndexPage)
}

Resources