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

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()

Related

Is there a way to display success messages in the "testing" package when writing unit test cases in Go?

I am using testing package to test my code. Currently I am only logging errors when errors are not 'nil' like:
if err != nil {
t.Errorf("Failed to initialise Client %s", err)
}
I also want to print success messages when error is nil using the same t *testing.T instance. Is there a way to do this?
You can use Log or Logf methods:
t.Log("It works like Println")
t.Logf("It works like Printf")
Check official documents for more details.

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.

Compare 2 errors in Go for unit test

I got a problem like below: Compare 2 errors when writing unit test
package main
import (
"errors"
"fmt"
"reflect"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func main() {
er1 := errors.New("database name is not exists")
er2 := errors.New("database name is not exists")
result1 := reflect.DeepEqual(er1, er2)
fmt.Println("reflect: ", result1)
result2 := cmp.Equal(er1, er2, cmpopts.EquateErrors())
fmt.Println("compare: ", result2)
result3 := errors.Is(er1, er2)
fmt.Println("errorIs: ", result3)
}
And the output of the above code is:
reflect: true
compare: false
errorIs: false
I want to compare 2 error and reflect.DeepEqual(er1, er2) is the 1st approach I apply and this method produce the output I want, but this approach have a warning from go lint:
avoid using reflect.DeepEqual with errorsdeepequalerrors
After google search, some people tell me some approaches that are:
Use cmp package to compare: cmp.Equal(er1, er2, cmpopts.EquateErrors())
Use errors package to compare: errors.Is(er1, er2)
But both above approaches can not produce same result like the 1st approach (use reflect.DeepEqual).
How I can compare 2 errors without warning from go lint and produce the result like the reflect.DeepEqual
Tks
Depending on how you write your tests, you may depend on reflect.DeepEqual() and ignore the linter warning ;
the drawback is : you start depending on the inner structure of the errors you return.
In the testing code I read, and the testing code we write, we use one of the following patterns :
most of the time, we just compare the error to nil ;
in some cases our functions return predefined error values, and we test for these specific values :
package pkg
var ErrUnboltedGizmo = errors.New("gizmo is unbolted")
// in test functions, depending on the case :
if err == pkg.ErrUnboltedGizmo { ...
// or :
if errors.Is(err, pkg.ErrUnboltedGizmo) { ...
when our production code mandates that a specific error be spotted (a common use case is io.EOF), we write code that dutifully wraps that known error, and we use errors.Is() (both in production code and in testing code),
when the need is, in testing only, to loosely confirm that an error matches something and not something else (e.g : Parse error and not File not found), we simply search for strings in the error message :
if err == nil || !strings.Contains(err.Error(), "database name is not exists") {
t.Errorf("unexpected error : %s", err)
}
What I found useful is to use cmp.Diff with cmpopts.IgnoreFields to ignore the sepecific error fields which cause the issue u detailed and afterward I just run a check on the errors with string.Contains or whatever I find fitting.
So it goes something like this:
if diff := cmp.Diff(expected, got, cmpopts.IgnoreFields(expected, "ErrorField")); diff != "" {
// found diff not including the error
}
Now check only the errors on their own and that's it.
And yeah I know u marked a solution already but maybe it will help someone :)

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).

How to verify if a specific function is called

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.

Resources