Show original source line when wrapping testing.T.Errorf() - unit-testing

I'm writing some test for a Go module. A lot of it is checking that functions return the correct values. This is a simple example for what I'm currently doing:
package foo
import (
"reflect"
"testing"
)
func Foo() int {
return 3
}
func TestFoo(t *testing.T) {
expected := 4
actual := Foo()
if !reflect.DeepEqual(actual, expected) {
t.Errorf("actual=%v, expected=%v", actual, expected)
}
}
A single test might contain a many such equality checks. Duplicating these 6 lines for each check makes the tests hard to read and error-prone to write (from what I've experienced the past few days). So I thought I'd make an assertEquals() function that wraps the whole logic similar to what other testing frameworks provide (e.g. in JUnit's org.junit.Assert):
func TestFoo(t *testing.T) {
assertEqual(t, 4, Foo())
}
func assertEqual(t *testing.T, actual interface{}, expected interface{}) {
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Assertion failed: %v != %v", actual, expected)
}
}
The problem now is that Errorf() will obviously not show the line where assertEqual() is called but the call to Errorf() inside of assertEqual:
=== RUN TestFoo
foo_test.go:28: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)
Is there a way to fix that, e.g. by showing the whole stack trace instead of only the location of the call to Errorf()?
Or is there a better way to avoid repeating those lines of code to check the return value of a function?

You can use t.Helper():
Helper marks the calling function as a test helper function. When printing file and line information, that function will be skipped. Helper may be called simultaneously from multiple goroutines.
So your helper function becomes:
func assertEqual(t *testing.T, actual interface{}, expected interface{}) {
t.Helper()
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Assertion failed: %v != %v", actual, expected)
}
}
Output:
=== RUN TestFoo
prog.go:13: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)
FAIL
where prog.go:13 in this playground is the line in the main test target that calls assertEqual instead of t.Errorf within it.
This will only change the file line in the testing output. If you actually want the full stack trace, you can use runtime.Caller as mentioned in these two threads.

Related

Unit testing of a function that starts a go routine inside it

I have a codebase that is roughly as follows
type Service struct {
Repo repo // An interface that contains both FunctionOne and FunctionTwo
GoRoutineWaitgroup *sync.WaitGroup
}
func (impl *Service) MyFunction(s string) bool {
a := impl.Repo.FunctionOne()
b := impl.Repo.FunctionTwo()
fmt.Println("Executed Function One and Function two")
go impl.validateMyFunction(a,b)
return true
}
func (impl *Service) validateMyFunction(a string,b string) {
defer helpers.PanicHandler()
impl.GoRoutineWaitgroup.Add(1)
defer impl.GoRoutineWaitgroup.Done()
fmt.Println("a and b are validated")
}
And I wrote unit tests as something similar to this.
func TestMyFunction(t *testing.T) {
ms := &Service{}
test := []struct{
input string
output bool
case string
}{
{"a", true, sample}
}
}
for _, test := range tests {
t.Run(test.case, func(t *testing.T) {
mockRepo := new(mockrepo.Repo) // mockRepo contains mocks of original repo layer methods generated using mockery for testing purposes
mockRepo.On("FunctionOne")
mockRepo.On("FunctionTwo")
ms.Repo = mockRepo
op := ms.MyFunction(test.input)
assert.Equal(t, test.Output, op)
})
}
} // Please keep in mind that this is not my actual code, but just a basic structure.
All tests are successful. But when executing the command go test -v, I saw multiple places in the code where the program panicked and gave invalid memory address or nil pointer dereference. I checked the code in debug mode and realized that the issue is with impl.GoRoutineWaitgroup.Add(1) in method validateMyFunction and when I commented out go validateMyFunction(a,b) and ran tests again, there were no panics in the logs. So How do I solve this issue? How to handle unit tests of functions where we start a goroutine from inside ( as in this case )?
You need initialize value to GoRoutineWaitgroup field.
ms := &Service{GoRoutineWaitgroup: &sync.WaitGroup{}}
Or remove pointer from definition
type Service struct {
Repo repo
GoRoutineWaitgroup sync.WaitGroup
}
Also I do not see waiting for Wait Group in your code. Something like ms.GoRoutineWaitgroup.Wait() and you need to move impl.GoRoutineWaitgroup.Add(1) to MyFunction from validateMyFunction otherwise the code in validateMyFunction will not be called

Golang Unit Testing: error conditions

How do I test error conditions, & other unexpected code flow, in Golang?
Suppose I have code as follows:
import crypto
func A(args) error {
x, err := crypto.B()
if err != nil {
return err
}
return nil
}
B is some function. I believe that the only way for me to test this failure scenario is to change the value of B for my test (mock it so it returns an error). Things I have tried:
1) monkeypatch the function just before the test and unpatch afterwards.
This was a horrible idea. Caused all sorts of weird issues as the
tests were running.
2) Pass B as an argument to A. This is all well and good but it also
means that I have to change the definition for A and then update
every use of it every time the implementation changes. Furthermore,
A may be using many imported functions and putting them all in the
type definition for A just seems ugly and not idiomatic.
3) Change the value of B for my test, then change it back.
import crypto.B
cryptoB = crypto.B
func A(args) error {
x, err := cryptoB()
if err != nil {
return err
}
...
}
func Test_A(t *testing.T) {
oldB := cryptoB
cryptoB = mockB
// run test
cryptoB = oldB
...
}
I've been using method #3 as it allows me fine grained unit testing control while not creating too much overhead. That said, it's ugly and causes confusion e.g. "Why are we renaming all of the imported functions?".
What should I be doing? Please help me so my codes can be more better :)
Like you, I've never seen a solution to this problem that's totally satisfied me.
In regards to your example 3, remember that you can defer the reset of the cryptoB. This, combined with good naming of the mock functions, would make it clear what you are trying to accomplish. There are obviously still code-style issues with this approach, with having all of your references listed line by line, twice, at the start of your file.
func TestSomething(t *testing.T) {
cryptoB = mockedFunc
defer func() {
cryptoB = crypto.B
}
// Testing goes on here
}
Option 4
The other approach (which I would favor) would be to turn the functions you export into methods of a CryptoA struct. The struct would store whatever dependencies and state it requires. Something like this:
type CryptoA struct {
cryptoB func() error
}
func (a *CryptoA) CryptoA() error {
return a.cryptoB()
}
func NewCryptoA() *CryptoA {
return &CryptoA{
cryptoB: func() error {
return nil
},
}
}
and mocking would be very similar:
func TestSomething(t *testing.T) {
a := NewCryptoA()
a.cryptoB = mockedFunc
// Testing goes on here
}
With this approach you lose some by your API having an extra step for invocation, a := NewCryptoA(), and you still have to name all of your dependencies, but you make gains by having the state of your API specific to each client.
Maybe there is a flaw in your API, and you leak data somewhere unexpected, or there is some state modifications that you don't expect. If you create a new CryptoA for each caller, then maybe the amount of data you leak, or the number of clients with a corrupted state, is limited, and therefore the impact less severe/abusable. I'm obviously spitballing at how this applies to your codebase, but hopefully you can get the idea of how this is a win.
Also, if you want to give the ability for users to specify their own hash algorithm, you can swap it out internally, and since it's private you maintain confidence that the function is up to the standards of your API. Again, I'm obviously spitballing.
I'll be skimming the answers to see if there's an idiomatic way to do this that I'm unaware of.
My preferred approach is usually to make A a method, and store the dependencies in the receiver object. Example:
import crypto;
type CryptoIface interface {
B() (string, error)
}
type standardCrypto struct {}
var _ CryptoIface = &standardCrypto{}
func (c *standardCrypto) B() (string, error) {
return crypto.B()
}
func main() {
crypto = &standardCrypto{}
err = A(crypto, ...)
// stuff
}
func A(crypto CryptoIface, args ...string) error {
result, err := crypto.B()
if err != nil {
return err
}
// do something with result
return nil
}
Then for your tests, you can easily create a mock version of the CryptoIface:
type mockCrypto struct {
Bfunc func(args ...string) error
}
func (c *mockCrypto) B(args ...string) error {
return c.Bfunc(args...)
}
func TestA(t *testing.T) {
c := &mockCrypto{
Bfunc: func(_ ...string) error {
return errors.New("test error")
}
}
err := A(c)
if err.String() != "test error" {
t.Errorf("Unexpected error: %s", err)
}
}
This approach is usually most useful for large projects or APIs where it makes sense to include a number of methods on a single object. Use your discretion. For smaller cases, this will be overkill.

assert_called_once() or assert_called_xyz().... equivalent?

It is important to me to be able to assert how many times a fake / mocked method is called in my tests and I'm wondering what is the best way to do this without using something like testify. In my case, the call to the mocked method is the result of some recursive call.
Lets say I have table driven tests with various animals, I want to assert that Hello is actually called for some tests but not for others. In some cases, it should be called more than once for given test (iterating over a slice).
Is it appropriate to just add a counter and make an assertion on that in my table driven test? It seems to me like maybe there is a better way to do this.
If I do add a counter to the hello method... where is it appropriate to deal with and check this. In the fake method itself or in the test etc?
type fakeFarmService struct {
abc.someFarmServiceInterface
}
func (f *fakeFarmService) Hello(ctx context.Context, in *abc.FarmRequest) (*abc.FarmResponse, error) {
if in.GetAnimal() == Monkey {
return &abc.HelloResponse{}, nil
}
return nil, errors.New("an error")
}
I've used the approach of counter on the struct and then asserting it inside the package level unit test multiple times in the past. Still, it's probably only until the level of package, when you would like to test such an internal assertions. I believe it's an accepted way of doing this in Go. Just be careful about properly synchronizing the access to the counter, if you decide to use a global variable or run the tests concurrently.
package main
import (
"fmt"
"sync"
"testing"
)
type fakeable interface {
Hello()
}
type fakeFarmService struct {
mu sync.Mutex
counter int
}
func (f *fakeFarmService) Hello() {
f.mu.Lock()
f.counter++
f.mu.Unlock()
}
func helloCaller(callee fakeable) {
callee.Hello()
}
func TestCallingTheHello(t *testing.T) {
fakeSvc := &fakeFarmService{}
helloCaller(fakeSvc)
helloCaller(fakeSvc)
// we expect that Hello method of fakeable was called 2 times
fakeSvc.mu.Lock()
defer fakeSvc.mu.Unlock()
if c := fakeSvc.counter; c != 2 {
t.Errorf("unexpected call count, want 2, got %d", c)
}
}
func main() {
TestCallingTheHello(&testing.T{})
}
https://play.golang.org/p/RXKuLKIZwc (test error won't work inside the playground)
Some good material on advanced testing in Go
Testing Techniques by Andrew Gerrand
NewStore TechTalk - Advanced Testing with Go by Mitchell Hashimoto

How to mock/unittest nested function

I have a function which is getting called inside other function.
send_api.go
function *send_api*(client *http.Client,url string) map[string]string,error {
//send api request and parse the response and return the dict
return dictmap
for eg:{apple fruit}
}
Then this function is getting called in main() function
func *main()*{
getmap :=send_api(client *http.Client,"test.com")
}
good.go
func *get_dict_key*(key string) string,error {
value,ok := get_map[key]
if !ok {
return fmt.Errorf("value is nil")
}
return value ,nil
}
function *good*(client *http.client, key string) {
//get a dictionary value
dcmap,err := get_dict_key("apple")
if err != nil {
panic(err)
}
value := dcmap[key]
//use the value to do other processing
}
unit_test
func Test_good(t *testing.T) {
Convey("AND quadra and conusl dcs are mapped",t, func() {
mockResponses := send mock GET request to the URL and receive a response{"apple":"fruit"}
}
server, client := tools.TestClientServer(&mockResponses)
defer server.Close()
getMap := send_api(client.HTTPClient, "http://test")
//At this point getMAP has value {'apple' 'fruit'}
**q1.How to pass getMap value inside this get_dict_fkey function during testing?**
value := get_dict_key("apple")
good(client,"apple") #error:(value is nil)
Q1. **q1.How to pass getMap value inside this get_dict_function during testing?*
Any pointer would be helpful?
Assuming you are facing difficulty to mock http.Client, I would like to suggest following options.
1. Refactor the code
You need to refactor the code in such a way that you can pass the mockable dependencies to function that you would like to test.
For example,
Refactor func send_api(client *http.Client,url string) map[string]string,error so that it does api request and getting/parsing data, but call another function from it, which does the further processing (that actually you would like to test and not the http.Client part).
But, with this approach, you may not be able to test end to end flow. But you can test those functions separately.
2. Mock http.Client
Again, you may need to refactor your code. Some related article can be found here
Update: Recommending to watch justforfunc #16: unit testing HTTP servers

Using testing.T as anonymous struct field: "too many arguments in call to this.T.common.Fail"

I am trying to solve the Karate Chop kata in Go as an exercise and stuck with this compiler error from my test case:
too many arguments in call to this.T.common.Fail
I wrapped testing.T into a struct with additional methods, as an anonymous struct field:
package main
import (
"fmt"
"testing"
)
type assertions struct {
*testing.T
}
func (this assertions) assert_equal(expected int, actual int) {
if (expected != actual) {
this.Fail(fmt.Sprintf("Failed asserting that %v is %v", actual, expected));
}
}
func TestChop(t *testing.T) {
test := assertions{t}
test.assert_equal(-1, Chop(3, []int{}))
test.assert_equal(-1, Chop(3, []int{1}))
...
}
I expect this.Fail to call Fail() on the anonymous testing.T struct field, which takes a string parameter. Why isn't this the case and where does this.T.common.Fail come from? I cannot find any reference to common in the testing package documentation.
Source file src/testing/testing.go
// Fail marks the function as having failed but continues execution.
func (c *common) Fail() {
c.mu.Lock()
defer c.mu.Unlock()
c.failed = true
}
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
mu sync.RWMutex // guards output and failed
output []byte // Output generated by test or benchmark.
failed bool // Test or benchmark has failed.
skipped bool // Test of benchmark has been skipped.
finished bool
start time.Time // Time test or benchmark started
duration time.Duration
self interface{} // To be sent on signal channel when done.
signal chan interface{} // Output for serial tests.
}
// T is a type passed to Test functions to manage test state and support formatted test logs.
// Logs are accumulated during execution and dumped to standard error when done.
type T struct {
common
name string // Name of test.
startParallel chan bool // Parallel tests will wait on this.
}
func (*T) Fail
func (c *T) Fail()
Fail marks the function as having failed but continues execution.
There are no arguments to T.common.Fail().
Try Errorf:
func (*T) Errorf
func (c *T) Errorf(format string, args ...interface{})
Errorf is equivalent to Logf followed by Fail.
For example,
this.Errorf("Failed asserting that %v is %v", actual, expected)