Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I have a common test suite for multiple packages in go?

Tags:

go

When I'm writing an interface, its often convenient to define my tests in the same package as the interface, and then define multiple packages that implement the interface set, eg.

package/
package/impl/x <-- Implementation X
package/impl/y <-- Implementation Y

Is there an easy way to run the same test suite (in this case, located in package/*_test.go) in the sub packages?

The best solution I've come up with so far is to add a test package:

package/tests/

Which implements the test suite, and a test in each of the implementations to run the tests, but this has two downsides:

1) The tests in package/tests are not in _test.go files, and end up being part of the actual library, documented by godoc, etc.

2) The tests in package/tests are run by a custom test runner, which has to basically duplicate all the functionality of go test to scan for go tests and run them.

Seems like a pretty tacky solution.

Is there is a better way of doing this?

like image 296
Doug Avatar asked Apr 09 '13 09:04

Doug


People also ask

How do I run multiple test cases in Golang?

go test : to run all _test.go files in the package. go test -v : will display the result of all test cases with verbose logging. go test -run TestFunctionName/Inputvalue= : to run the test case for specific input. Here I run the `TestSumNumbersInList` for only the 5th index of input.

Should go tests be in the same package?

Writing tests in Go requires a test file link, and this test file must always end with _test.go . By convention, Go testing files are always located in the same folder, or package, where the code they are testing resides.

How do you run all test suites?

Open the Test suite execution console by clicking Run. If you are running tests in a sequence, the first test case in the sequence is shown in the console. If you run tests in parallel, the automated test cases start to run and the manual test cases or test cases without scripts are shown in the console.

How many test cases are there in a test suite?

For instance, a test suite might contain four test cases, each with a separate test script: Test case 1: Login. Test case 2: Add New Products. Test case 3: Checkout.


3 Answers

I don't really dislike the idea to use a separate testing library. If you have an interface and you have generic tests for each interface, other people that implement that interface might like to use these tests as well.

You could create a package "package/test" that contains a function

// functions needed for each implementation to test it
type Tester struct {
    func New() package.Interface
    func (*package.Interface) Done()
    // whatever you need. Leave nil if function does not apply
}

func TestInterface(t *testing.T, tester Tester)

Notice that the signature of TestInterface does not match to what go test expects. Now, for each package package/impl/x you add one file generic_test.go:

package x

import "testing"
import "package/test"

// run generic tests on this particular implementation
func TestInterface(t *testing.T) {
    test.TestInterface(t,test.Tester{New:New})
}

Where New() is the constructor function of your implementation. The advantage with this scheme is that

  1. Your tests are reusable for whoever implements your interface, even from other packages
  2. It is immediately obvious that you run the generic test suite
  3. The test cases are where the implementation is and not at another, obscure place
  4. The code can be adapted easily if one implementation needs special initialization or similar stuff
  5. It's go test compatible (big plus!)

Of course, in some cases you need a more complicated TestInterface function, but this is the basic idea.

like image 85
fuz Avatar answered Oct 19 '22 21:10

fuz


If you share a piece of code for reuse by different packages then yes, it is a library by definition. Even when used only for testing from *_test.go files. It's no different from importing "testing" of "fmt" in the _test.go file. And having the API documented by godoc is a plus, not minus IMHO.

like image 1
zzzz Avatar answered Oct 19 '22 20:10

zzzz


Maybe something gets mixed up here a bit: If package a defines an interface only than there is no code to test as interfaces in Go are implementation free.

So I assume the methods in your interface in package a have constraints. E.g. in

interface Walker {
    Walk(step int)
    Tired() bool
}

you contract assumes that Tired returns true if more than 500 steps have been Walk'ed (and false otherwise) and your test code checks these dependencies (or assumption, contracts, invariants whatever you name it).

If this is the case I would provide (in package a) an exported function

func TestWalkerContract(w Walker) error {
    w.Walk(100)
    if w.Tired() { return errors.New("Tired after 100 steps") }
    w.Walk(450)
    if !w.Tired() { return errors.New("Not tired after 100+450 steps") }
}

Which documents the contract properly and can be used by packages b and c with types implementing walker to test their implementations in b_test.go and c_test.go. IMHO it is perfectly okay that these function like TestWalkerContract are displayed by godoc.

P.S. More common than Walk and Tired might be an error state which is kept and reported until cleared/reseted.

like image 1
Volker Avatar answered Oct 19 '22 19:10

Volker