Featured image by Egon Elbre
- Counting words or lines from
stdin
.- os.Stdin implements the io.Reader inteface.
- bufio.Scanner can be used to iterate over words (ScanWords), lines (ScanLines), runes [aka Unicode characters] (ScanRunes) or bytes (ScanBytes).
import (
"bufio"
"io"
"os"
)
func count(r io.Reader, countLines bool) int {
scanner := bufio.NewScanner(r)
if !countLines {
scanner.Split(bufio.ScanWords)
}
wc := 0
for scanner.Scan() {
wc++
}
return wc
}
// Count words
count(os.Stdin, false)
// Count lines (\n) [except the trailing one]
count(os.Stdin, true)
- Playing well with others the Unix way. Meaning write error messages to
stderr
(and notstdout
) and return non zero exit codes.
if err := someOperation(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(42)
}
- The arguments passed to a go application can be accessed with
os.Args
(which is declared asvar Args []string
in the os package). Remember item 0 is the path of the application and thus you can useArgs[1:]
to return the slice of only the passed arguments. - Visual Studio Code can easilly navigate to the source code for any standard library by just Cmd+Click on the name.
- You can determine the platform the app is running on by querying things from the
runtime
package. For exampleruntime.GOOS == "windows"
.
Unit-testing
Generally all .go files in the same directory has to belong to the same Go package except for unit-tests that are allowed to belong to a different package. This helps ensure that your package only exports the types and functions/methods as you intended and thus the unit-test can only import what you exported.
Convention is to name unit-test files with the _test.go
suffix and also for the package name.
Example: main.go will be unit-tested with main_test.go and that has a package main_test
as the first line.
- Tests can be run with more verbosity.
$ go test -v
=== RUN TestOne
--- PASS: TestOne (0.00s)
=== RUN TestSomething
--- PASS: TestSomething (0.00s)
=== RUN TestTwo
--- PASS: TestTwo (0.00s)
PASS
ok todo 0.097s
$ go test -v
=== RUN TestOne
--- FAIL: TestOne (0.00s)
...
- Visual Studio Code makes unit-testing really nice and even does code coverage with highlighting the code that was covered.
- Tests can be run with code coverage and a specified timeout. This is what VS Code uses.
$ go test -timeout 30s -coverprofile=/path/to/codecoverage packagename
- To run an individual test. Note that you actually specify a regex of which tests to run. In this case a function matching “TestAdd” in the package “todo”.
$ go test -timeout 30s -run ^TestAdd$ todo
- Example of a unit-test function. Note that unit-tests are declared with a prefix “Test” and the argument is
(t *testing.T)
.
package todo_test
import (
"testing"
"todo"
)
func TestAdd(t *testing.T) {
// Do something you want to test
...
if result != expected {
t.Errorf("Expected %q, got %q\n", expected, result)
}
}
- To see an awesome way of doing an integration test of your CLI app, by which the unit-test compiles the app, runs and test the app. See the code for “interacting/todo/cmd/main_test.go” from this book’s source code.
- You can place resource files to be used by your tests in a special directory called
testdata
. The Go tooling will ignore these files.