Having issue creating unit test of one of my source file ( commonutil.go )
package util
import "github.com/nu7hatch/gouuid"
// GenerateUUID Returns generated UUID sequence
func GenerateUniqueID(hostname string) (string, error) {
var result, err = uuid.NewV4()
return hostname + ":" + result.String(), err
}
For the above source, I created the test file "commonutil_test.go" ( in the same package )
package util
import "testing"
func TestCommonUtil(t *testing.T) {
t.Run("Test generate UUID", func(t *testing.T) {
var uuid, _ = GenerateUniqueID ("test")
//fmt.Printf("UUID isa %v \n", uuid)
if uuid == "" {
t.Errorf("UUID expected, but result is empty ")
}
})
However when trying executing "go test util/commonutil_test.go" it shows :
util\commonutil_test.go:8: undefined: GenerateUniqueID
FAIL command-line-arguments [build failed]
Changing to util.GenerateUniqueID in the test solve the problem, however when running coverage using Goconvey will cause build failure :
can't load package: import cycle not allowed
package rudygunawan.com/MyProject/HFLC-Go/util
imports rudygunawan.com/MyProject/HFLC-Go/util
Any idea to solve this issue? I am confused.
Go version is go1.7.1 windows/386
I've run into a similar problem, when I was trying to run a single test file.
I wanted that, as it was a kind of test driven development thing, where I wanted to run tests only for the code I was working on at the moment, and not all the x-minutes running tests.
The solution turned out to be not running tests from a file, but rather running a specific test by name (actually a regex). So in your case I guess it would be:
go test ./util -run TestCommonUtil
An alternative seems to be listing all the files needed to build your test code:
go test util/commonutil_test.go util/commonutil.go
Just realize it is a silly mistake. The package for the test should be "util_test". Putting the test in the separate package ( but still in the same folder) help solve import cycle issue, yet still allow to solve the undefined error.
The way I normally write Go unit tests is to have one (or more) ..._test.go files, in the same package as the code being tested, with one Test... function for each broad set of tests to be done.
package util
import "testing
func TestGenerateUniqueID(t *testing.T) {
var uuid1, uuid2 string
uuid1, err = GenerateUniqueID("test")
if err != nil {
t.Errorf("Expected no error, got %s", err) // Maybe Fatalf?
}
if uuid1 == "" {
t.Errorf("Expected non-empty string, got empty string (uuid1)")
}
uuid2, err = GenerateUniqueID("test")
if err != nil {
t.Errorf("Expected no error, got %s", err) // Maybe Fatalf?
}
if uuid2 == "" {
t.Errorf("Expected non-empty string, got empty string (uuid2)")
}
if uuid1 == uuid2 {
t.Errorf("Expected uuid1 and uuid2 to be different, both are %s", uuid1)
}
}
One of the reasons I tend towards whitebox testing (where I can do "blackbox testing" by carefully not accessing package internals) is that there's usually a whole slew of non-exported code that really should be tested as well. In this specific, small, example, there's no massive argument for one over the other, since all the functionality that can be tested is already exported.
Related
I have a file with serialized array in PHP.
The content of the file locks like this
a:2:{i:250;s:7:"my_catz";s:7:"abcd.jp";a:2:{s:11:"category_id";i:250;s:13:"category_name";s:7:"my_catz";}}
The array unserialized is this
(
[250] => my_catz
[abcd.jp] => Array
(
[category_id] => 250
[category_name] => my_catz
)
)
Now, i want to get the content of the file in GO, unserialize it convert it to an array.
In GO i can get the content of the file using
dat, err := os.ReadFile("/etc/squid3/compiled-categories.db")
if err != nil {
if e.Debug {
log.Printf("error reading /etc/squid3/compiled-categories.db: ", err)
}
}
And unserialized it using github.com/techoner/gophp library
package categorization
import (
"fmt"
"os"
"github.com/techoner/gophp"
"log"
"errors"
)
type Data struct {
Website string
Debug bool
}
func (e Data) CheckPersonalCategories() (int,string) {
if e.Debug {
log.Printf("Checking Personal Categories")
}
if _, err := os.Stat("/etc/squid3/compiled-categories.db"); errors.Is(err, os.ErrNotExist) {
if e.Debug {
log.Printf("/etc/squid3/compiled-categories.db not exit: ", err)
}
return 0,""
}
dat, err := os.ReadFile("/etc/squid3/compiled-categories.db")
if err != nil {
if e.Debug {
log.Printf("error reading /etc/squid3/compiled-categories.db: ", err)
}
}
out, _ := gophp.Unserialize(dat)
fmt.Println(out["abcd.jp"])
return 0,""
}
But I can't access to the array, for example, when I try access to array key using out["abcd.jp"] i get this error message
invalid operation: out["abcd.jp"] (type interface {} does not support indexing)
The result of out is
map[250:my_catz abcd.jp:map[category_id:250 category_name:my_catz]]
Seams that is unserializing
Don't make assumptions about what is and isn't succeeding in your code. Error responses are the only reliable way to know whether a function succeeded. In this case the assumption may hold, but ignoring errors is always a mistake. Invest time in catching errors and at least panic them - don't instead waste your time ignoring errors and then trying to debug unreliable code.
invalid operation: out["abcd.jp"] (type interface {} does not support indexing)
The package you're using unfortunately doesn't provide any documentation so you have to read the source to understand that gophp.Unserialize returns (interface{}, error). This makes sense; php can serialize any value, so Unserialize must be able to return any value.
out is therefore an interface{} whose underlying value depends on the data. To turn an interface{} into a particular value requires a type assertion. In this case, we think the underlying data should be map[string]interface{}. So we need to do a type assertion:
mout, ok := out.(map[string]interface{})
Before we get to the working code, one more point I'd like you to think about. Look at the code below: I started it from your code, but the resemblance is very slight. I took out almost all the code because it was completely irrelevant to your question. I added the input data to the code to make a minimal reproduction of your code (as I asked you to do and you declined to do). This is a very good use of your time for 2 reasons: first, it makes it a lot easier to get answers (both because it shows sufficient effort on your part and because it simplifies the description of the problem), and second, because it's excellent practice for debugging. I make minimal reproductions of code flows all the time to better understand how to do things.
You'll notice you can run this code now without any additional effort. That's the right way to provide a minimal reproducible example - not with a chunk of mostly irrelevant code which still can't be executed by anybody.
The Go Plaground is a great way to demonstrate go-specific code that others can execute and investigate. You can also see the code below at https://go.dev/play/p/QfCl08Gx53e
package main
import (
"fmt"
"github.com/techoner/gophp"
)
type Data struct {
Website string
Debug bool
}
func main() {
var dat = []byte(`a:2:{i:250;s:7:"my_catz";s:7:"abcd.jp";a:2:{s:11:"category_id";i:250;s:13:"category_name";s:7:"my_catz";}}`)
out, err := gophp.Unserialize(dat)
if err != nil {
panic(err)
}
if mout, ok := out.(map[string]interface{}); ok {
fmt.Println(mout["abcd.jp"])
}
}
Go newbie here, I have written a simple main_test.go file to run some test cases for main.go, when I run go test it says testing: warning: no tests to run
PASS
ok Solution 0.878s
my main.go:
package main
func normalizePhoneNum(phoneNumber string) string {
return ""
}
func main() {
}
main_test.go:
package main
import (
"testing"
)
func testNormalizePhoneNum(t *testing.T) {
testCase := []struct {
input string
output string
}{
{"1234567890", "1234567890"},
{"123 456 7891", "123 456 7891"},
{"(123) 456 7892", "(123) 456 7892"},
{"(123) 456-7893", "(123) 456-7893"},
{"123-456-7894", "123-456-7894"},
{"123-456-7890", "123-456-7890"},
{"1234567892", "1234567892"},
{"(123)456-7892", "(123)456-7892"},
}
for _, tc := range testCase {
t.Run(tc.input, func(t *testing.T) {
actual := normalizePhoneNum(tc.input)
if actual != tc.output {
t.Errorf("for %s: got %s, expected %s", tc.input, actual, tc.output)
}
})
}
}
Can anyone please tell, why it's not running the test cases?
Elementary! See the documentation for the go test command:
A test function is one named TestXxx (where Xxx does not start with a lower case letter) and should have the signature,
func TestXxx(t *testing.T) { ... }
Note that the first letter must be an uppercase T. You must respect this naming convention for test functions or the testing tool will simply ignore them.
Rename your test function to TestNormalizePhoneNum and try running go test again.
Alternatively—and although that is most likely not what you want here—you can force the testing tool to run a "test function" that doesn't adhere to the naming convention by specifying its name (or, more generally, a regular expression that its name matches) in the -run flag:
go test -run=testNormalizePhoneNum
I would like to know how can I check the type of error returned by plugin.Open, e.g:
package main
import "plugin"
func main() {
_, err := plugin.Open("./module.so")
// here
}
I would like to do something different if the error is:
plugin.Open("./module.so"): realpath failed
Which basically means that the file doesn't exist.
Example of desired result:
package main
import "plugin"
func main() {
_, err := plugin.Open("./module.so")
if err.Error() == "plugin.Open(\"./module.so\"): realpath failed" {
// do something different here
} else {
log.Fatal(err)
}
}
The string that I pass to plugin.Open can have other values, so it needs to be something more smart than that.
Thanks in advance.
Inspection of the code for plugin.Open() reveals the package calls out to some C code to determine whether the path exists. If it doesn't, it returns a plain error value. In particular, the package does not define any sentinel errors which you can compare against, nor does it return its own concrete implementer of the error interface which carries custom metadata. This is the code which produces that error:
return nil, errors.New(`plugin.Open("` + name + `"): realpath failed`)
errors.New is a basic implementation of the error interface which doesn't allow any additional information to be passed. Unlike other locations in the standard library which return errors (such as path non-existent errors from the os package), you can't get such metadata in this instance.
Check whether the module file exists first
My preference would be to verify whether the module exists before attempting to load it, using the native capabilities provided by the os package:
modulePath := "./module.so"
if _, err := os.Stat(modulePath); os.IsNotExist(err) {
// Do whatever is required on module not existing
}
// Continue to load the module – can be another branch of the if block
// above if necessary, depending on your desired control flow.
Compare a subset of the error values
You could also use strings.Contains to search for the value realpath failed in the returned error value. This is not a good idea in the event that string changes in future, so if you adopt this pattern, at the very least you should ensure you have rigorous tests around it (and even then it's still not great).
_, err := plugin.Open("./module.so")
if err != nil {
if strings.Contains(err.Error(), "realpath failed") {
// Do your fallback behavior for module not existing
log.Fatalf("module doesn't exist")
} else {
// Some other type of error
log.Fatalf("%+v", err)
}
}
I am working on testing an application of mine, for which I need to create temporary files with specific extensions. My goal is to create files in a temp directory that look similar to this example123.ac.json.
In order to do this I am using ioutil.TempDir and ioutil.TempFile.
Here is a small contrived example of what I am doing.
main.go:
package main
func main() {
}
main_test.go:
package main
import (
"fmt"
"io/ioutil"
"os"
"testing"
)
func TestMain(t *testing.T) {
dir, err := ioutil.TempDir("", "testing")
if err != nil {
t.Fatalf("unable to create temp directory for testing")
}
defer os.RemoveAll(dir)
file, err := ioutil.TempFile(dir, "*.ac.json") // Create a temporary file with '.ac.json' extension
if err != nil {
t.Fatalf("unable to create temporary file for testing")
}
fmt.Printf("created the following file: %v\n", file.Name())
}
When I run the tests locally on my Mac with go test the following is outputted from the fmt.Printf is
$ go test
created the following file: /var/folders/tj/1_mxwn350_d2c5r9b_2zgy7m0000gn/T/testing566832606/900756901.ac.json
PASS
ok github.com/JonathonGore/travisci-bug 0.004s
So it works as expected but when I run it in TravisCI the following is outputted from the Printf statement:
created the following file: /tmp/testing768620677/*.ac.json193187872
For some reason it is using the literal asterisk inside TravisCI but not when running on my own computer.
Here is a link to the TravisCI logs if interested.
For completeness here is my .travis.yml:
language: go
go:
- "1.10"
Anyone have any idea what is going on here? Or am I missing something obvious?
The feature of replacing the first asterisk with the random value was added in Go 1.11. It looks like you are using go 1.10 for your Travis CI runs so the asterisk won't be replaced.
I have a tests which looks like:
package tst
import (
"testing"
"github.com/demas/cowl-go/pkg/postgres"
"log"
"os"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/demas/cowl-go/pkg/quzx-crawler"
"github.com/SlyMarbo/rss"
"time"
_ "github.com/lib/pq"
)
func TestMain(m *testing.M) {
prepare()
retCode := m.Run()
os.Exit(retCode)
}
func prepare() {
connectionString := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
os.Getenv("DBUSER"),
os.Getenv("DBPASS"),
os.Getenv("DBHOST"),
os.Getenv("DBPORT"),
os.Getenv("DBNAME"))
db, err := sqlx.Open("postgres", connectionString)
if err != nil {
log.Fatal(err)
}
db.Exec(`DELETE FROM Settings`)
db.Exec(`DELETE FROM HackerNews`)
// ....
}
Tests works fine if I keep in the root project folder, but if I move them to tst folder I get error message:
D:\development\gopath\src\github.com\demas\cowl-go\tst>go test -v
2017/03/31 16:30:06 sql: unknown driver "postgres" (forgotten import?)
exit status 1
FAIL github.com/demas/cowl-go/tst 0.085s
Why ?
As already mentioned by #JimB in the comments, the error means that you're trying to open a db connection, using sqlx.Open, without first importing a db driver. This can be fixed by, in your case, adding this _ "github.com/lib/pq" import spec.
If, even after adding that import, you're still seeing the same error, then that means that one of your dependencies is also trying to open a db connection without first importing the necessary driver.
Please note that while log.Fatal is a nice and clean way to stop your program it can sometimes be lacking, as you already know. You might want to consider using panic instead, its output is much more chaotic but, on the other hand, you'll get the line number and file name that caused the panic and eventually you'll learn to parse it quickly.