How to verify if a specific function is called - go

I'm trying my hand at writing TDD in Go. I am however stuck at the following.
The test to write:
func TestFeatureStart(t *testing.T) {}
Implementation to test:
func (f *Feature) Start() error {
cmd := exec.Command(f.Cmd)
cmd.Start()
}
How would one test this simple bit? I figured I only wanted to verify that the exec library is spoken to correctly. That's the way I would do it in Java using Mockito. Can anyone help me write this test? From what I've read the usage of interfaces is suggested.
The Feature-struct only contains a string Cmd.

You can fake the whole deal with interfaces, but you could also use fakeable functions. In the code:
var cmdStart = (*exec.Cmd).Start
func (f *Feature) Start() error {
cmd := exec.Command(f.Cmd)
return cmdStart(cmd)
}
In the tests:
called := false
cmdStart = func(*exec.Cmd) error { called = true; return nil }
f.Start()
if !called {
t.Errorf("command didn't start")
}
See also: Andrew Gerrand's Testing Techniques talk.

Related

gomock missing call(s)

I am trying to mock the below method using gomock
func (w *writer) Publish(vacancies []model.Vacancy) error {
...
if _, err = w.conn.WriteMessages(msg); err != nil {
return fmt.Errorf("failed to write message: %w", err)
}
Interface:
type Producer interface {
Publish(vacancies []model.Vacancy) error
Close() error
}
SuiteTest:
func (p *ProducerTestSuite) SetupTest() {
p.mockCtrl = gomock.NewController(p.T())
p.producer = NewMockProducer(p.mockCtrl)
writer, err := producer.NewWriter(context.Background(), scheduler.KafkaConf{Addr: "localhost:9092", Topic: "test"})
p.Require().NoError(err)
p.writer = writer
}
...
func (p *ProducerTestSuite) TestProducer_Publish() {
p.producer.EXPECT().Publish([]model.Vacancy{}).Return(nil)
p.Require().NoError(p.writer.Publish([]model.Vacancy{}))
}
mockgen:
//go:generate mockgen -package producer_test -destination mock_test.go -source ../kafka.go
When I try run test, I got this message:
=== RUN TestSuite/TestProducer_Publish
controller.go:137: missing call(s) to *producer_test.MockProducer.Publish(is equal to [] ([]storage.Vacancy)) /Users/...
controller.go:137: aborting test due to missing call(s)
Where I wrong?
It appears as if you are not calling the same thing that you are expecting on. Your expect is watching p.producer.Publish(), but your test code calls p.writer.Publish(). I cannot see any code here that would lead writer to call anything in producer.
The following code would behave as you expect:
func (p *ProducerTestSuite) TestProducer_Publish() {
p.producer.EXPECT().Publish([]model.Vacancy{}).Return(nil)
p.Require().NoError(p.producer.Publish([]model.Vacancy{}))
}
However, this test does not seem to actually exercise the unit that the test name indicates it should. Perhaps you are misunderstanding mocking ?
the Expect() means these must be a call to this method with the specified parameter, otherwise it will be failed, the missing call means your set a Expect() but didn't call it.
This answer is late, but it might helpful.
To require the function to be called once:
mockService.EXPECT().DoSomething().Return(nil, nil)
To allow the function to be called zero or more times:
mockService.EXPECT().DoSomething().Return(nil, nil).AnyTimes()
I also resolved a similar issue by adding a:
EXPECT().<interface-method>().Return(...).AnyTimes()
It seems that if one sets an EXPECT() gomock assumes that it must be called at least once. Adding AnyTimes() allows it to be called 0 times.

Why doesn't t.Fail() accept string arguments?

I am trying to improve my Golang tests. And I was reading this: https://ieftimov.com/post/testing-in-go-failing-tests/
I was using t.Fatal("message") a lot, when instead I should have been using a combination of:
t.Fail()
t.Logf()
so why on Earth is there not a single call then can fail the test and log the reason why? Is there a way for me to add such a method to a test.Testing instance? I just want to do:
t.FailWithReason("the reason the test failed")
does this exist and if not can I add it somehow?
Take a look at the documentation and source code for the testing package.
The documentation has an example of typical use:
func TestAbs(t *testing.T) {
got := Abs(-1)
if got != 1 {
t.Errorf("Abs(-1) = %d; want 1", got)
}
}
The documentation for t.Errorf is:
// Errorf is equivalent to Logf followed by Fail.
which is very similar to what you say you want:
t.Fail()
t.Logf()

How to discard printouts when running tests

Using Go 1.11 I have a function that prints a label above a user input. This is fine and works but when I have started to write the test for this function it prints out this label when running the tests.
I have also use log.Print but then in the test file adding the following
func init() {
log.SetOutput(ioutil.Discard)
}
This stops the log.Print from being displayed when running the tests. So how do I get it to do the same for any fmt.Println?
UPDATE
I thought I would post the func that I am testing and how I set up,
func checkLoop(loopCount int) int {
var input int
var ok bool
for !ok {
fmt.Printf("Input %d :: ", loopCount)
ok, input = takeInput()
}
return input
}
So takeInput() only takes the users input, using fmt.Scan and checks it to see if its within a set range I want. So it returns a bool and if its false it will re-use the label to check what input number it is.
Either you're testing on a wrong level or you're testing a wrong thing, let me explain.
If it's for some reason valuable for you to test that
your function prints something and also accepts user input,
then go ahead and do end-to-end testing for it—that is, also test
that it prints what it's expected to print, and once it does that, submit it some canned input and then verify it processes it the way you expect it to.
I don't know your code but supposedly you should stop
using fmt.P* family of functions—which imply using os.Std{in|out|err} and write the functions forming the core of your code accepting io.Reader and io.Writer interfaces and
call them from the top-level code passing them os.Stdin and os.Stderr etc.
Then, when testing, it will be easy to pass these functions
stub (or mock) values which also satisfy the said interfaces.
(The fmt package supports formatted printing using the fmt.FP* family of functions, where that F is a historical artefact meaning "file" and allows you to pass it any value implementing io.Writer.)
Another possibility is that what you explained looks like
a code smell: a function concerned with processing some input
data has no business printing any labels anywhere.
So it well may be that you instead should split your function
at least in two: the "outer" one prompts the user and reads the data, and the second accepts the data and processes it.
You then test the second one.
For example,
discard_test.go:
package main
import (
"fmt"
"os"
"syscall"
"testing"
)
func printLabel(label string) {
fmt.Println(label)
}
func TestDiscardLabel(t *testing.T) {
defer func(stdout *os.File) {
os.Stdout = stdout
}(os.Stdout)
os.Stdout = os.NewFile(uintptr(syscall.Stdin), os.DevNull)
printLabel("Discard:")
}
func TestPrintLabel(t *testing.T) {
printLabel("Print:")
}
Output:
$ go test discard_test.go -v
--- PASS: TestDiscardLabel (0.00s)
=== RUN TestPrintLabel
Print:
--- PASS: TestPrintLabel (0.00s)
PASS
$
fmt.Printf use the os.Stdout so you can simply:
os.Stdout = nil
fmt.Println("Hello Gopher!")
or more elegant:
os.Stdout,_ = os.Open(os.DevNull)
fmt.Println("Hello Gopher!")
Hope this help

Is there a way to find not handled errors in go code?

Let's assume the following code:
func main() {
doStuff()
}
Sound good, until your program runs without any error but does nothing because you just forgot that doStuff() actually looks like:
func doStuff() error {
// ...
return errors.New("woops!")
}
What we should do ist:
func main() {
err := doStuff()
if err != nil {
panic(err)
}
}
Or at least (to make it obvious):
func main() {
_ = doStuff()
}
There are so many go tools out there like. Is there a simple way to check my code for not handled error return values?
As #eugecm suggested, github.com/kisielk/errcheck will do this, as will github.com/GoASTScanner/gas . Also, github.com/alecthomas/gometalinter makes it easy to download a collection of linters and run them in parallel, and includes both of the above.
So,
go get github.com/alecthomas/gometalinter
gometalinter --install
gometalinter
would download gometalinter, which will then install a number of error and style checkers and run them in parallel, reporting the results.
The relevant results from your example would be like:
main.go:13::warning: Errors unhandled.,LOW,HIGH (gas)
main.go:13::warning: error return value not checked (doStuff()) (errcheck)
I recommend gometalinter because it gives results like the above, where running errcheck on a bare command just says main.go:13:10 doStuff() (because errcheck is a program that only checks for unchecked error return values, so no additional information is really necessary).

Compare Go stdout to file contents with testing package [duplicate]

I have a simple function I want to test:
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
fmt.Print(message)
}
}
But how can I test what the function actually sends to standard output? Test::Output does what I want in Perl. I know I could write all my own boilerplate to do the same in Go (as described here):
orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
t.Error("Failure!")
}
But that's a lot of extra work for every single test. I'm hoping there's a more standard way, or perhaps an abstraction library to handle this.
One thing to also remember, there's nothing stopping you from writing functions to avoid the boilerplate.
For example I have a command line app that uses log and I wrote this function:
func captureOutput(f func()) string {
var buf bytes.Buffer
log.SetOutput(&buf)
f()
log.SetOutput(os.Stderr)
return buf.String()
}
Then used it like this:
output := captureOutput(func() {
client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)
Using this assert library: http://godoc.org/github.com/stretchr/testify/assert.
You can do one of three things. The first is to use Examples.
The package also runs and verifies example code. Example functions may include a concluding line comment that begins with "Output:" and is compared with the standard output of the function when the tests are run. (The comparison ignores leading and trailing space.) These are examples of an example:
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The second (and more appropriate, IMO) is to use fake functions for your IO. In your code you do:
var myPrint = fmt.Print
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
myPrint(message) // N.B.
}
}
And in your tests:
func init() {
myPrint = fakePrint // fakePrint records everything it's supposed to print.
}
func Test...
The third is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but bytes.Buffer in tests.
You could consider adding a return statement to your function to return the string that is actually printed out.
func (t *Thing) print(min_verbosity int, message string) string {
if t.verbosity >= minv {
fmt.Print(message)
return message
}
return ""
}
Now, your test could just check the returned string against an expected string (rather than the print out). Maybe a bit more in-line with Test Driven Development (TDD).
And, in your production code, nothing would need to change, since you don't have to assign the return value of a function if you don't need it.

Resources