91

I was interested in creating unit test for "unexported (private) functions" in go. However, its basically really hard to create unit tests form them in the test package because I have to make them "public". Which in the end, defeats the whole point of them being private. The point is that these helper function help modularize and now that they are modular, it would be nice to be able to create unit tests for them without making them available to everyone except the testing package, nice they are not functions that should be accessed or used by anyone else except the testing suite or the actual package itself.

Any suggestions? Is it possible to only export to its own package and 1 additional package or something of that sort in go?

4 Answers 4

85

create a test file within the package

library_test.go

package mypkg

func TestPrivateStruct(t *testing.T){
  pf := private{ "Private Field" }
  ....
}

library.go

package mypkg

type private struct {
  privateField string
}

go test mypkg -v will run your Tests with your private struct

7
  • But this would mean that all of my unit tests are not under the test package, right? So running go test on the test directory would no longer give a complete test of my library, right? Commented Jul 8, 2014 at 2:23
  • 9
    @CharlieParker -- Yes, but that's the standard in Go. You mix _test files and non-_test files in a package, and test some/path/packagename, not some/path/packagename/test or some/path/tests/packagename.
    – twotwotwo
    Commented Jul 8, 2014 at 3:25
  • 3
    @twotwowo fair enough. However, its annoying to have to go to every dir to run each go test. Is it possible to run all of the tests through some kind of command or a Make file or something of that sort? Commented Jul 8, 2014 at 3:27
  • 30
    Ah, good question. The go tools support a ... wildcard, so you can go test github.com/yourname/yourrepo/... to test every package in there. (Or things like go test github.com/yourname/... or go test crypto/... if you're so moved.)
    – twotwotwo
    Commented Jul 8, 2014 at 3:31
  • 2
    @CharlieParker go test ./... run all the tests. Remember to use --tags if you need. Commented Nov 29, 2016 at 12:32
24

if you want to use pkg_test name as a package name for test and still test unexported field a simple trick could be create export_test.go file in your package with package name pkg then export your unexported field in there for example

file code.go

package pkg 

func getFunc(){}

file export_test.go

package pkg 
var GetFunc = getFunc

file code_test.go

package pkg_test
func TestGetFunc(t *testing.T) {
    testFunc:=pkg.GetFunc

    //check your test scenario here

}

note that all these files are in the same folder

the solution is inspired from here

7
  • 2
    This is the correct way of doing it. Otherwise you either have to export things just for tests or have black box tests sharing the same package as the code to be tested.
    – Luke
    Commented May 26, 2022 at 13:20
  • you are not exporting a method, just a function Commented Jun 2, 2022 at 10:23
  • The part about "note that all these files are in the same folder" cannot be stressed enough. I spent quite a bit of time fiddling with this before noticing the line. Thank you for your post, it was very helpful! Commented Sep 9, 2022 at 11:51
  • 1
    @JoulinRouge : I am late here but to test method you can use above approach with var ExposedPrivateMethod = (*SomeStruct).privateMethod in you test package, and you can use it in _test package like ExposedPrivateMethod(*SomeStruct, 1, 2) for testing that method. Commented May 27, 2023 at 15:13
  • 1
    @mrpandey , no. The GetFunc function will only be available to tests in this directory, i.e. to test packages pkg and pkg_test because the GetFunc function is declared in a file ending in _test.go.
    – Vadim
    Commented Nov 16, 2023 at 10:36
9

First, you can have both types of tests in the same location as your package by using the package name for internal tests (eg mypkg) and using the same package name with "_test" appended for "external" tests (eg mypkg_test). Both types of tests must be in files whose name ends in "_test.go".

BUT, the whole point of unit tests is to test the "external interface" (ie public functions) to your package. That is unit tests should always be "white box" tests (see White Box Testing). That way you can refactor your code and your tests won't break.

Of course, sometimes you want to check internal consistency, which is not possible through the "external interface". For that I have found assertions invaluable. Another possibility would be to add public "diagnostic" function(s) with names that indicate that they are not for normal use.

2
  • 3
    with names that indicate that they are not for normal use. --- at which point you're just accessing internals from your unit tests, but through a layer of indirection which is accessible from callers to boot. If you do this, it's a crystal clear sign 'external-only' testing doesn't cut it for your purpose. Might as well call a spade a spade and go internal.
    – hraban
    Commented Aug 9, 2017 at 7:19
  • 47
    unit tests are not only for public functions. Often a public interface could result in a myriad of different expectations resulting from combinations of private functions. it's a lot easier and safer to have a unit test for the smaller single purpose functions. You still want to test public ones as well obviously. Commented Aug 22, 2017 at 9:08
3

As of Go 1.5 you can use internal packages for shared functions within a package without having to expose them to outside callers.

Simply create a package named internal inside you package, move your shared functions into files inside the internal package and expose them as public functions. Then you can create test files for these functions as you would normally, using the internal_test for the package.

For example:

project/
└── domain/
    ├── logic.go
    ├── logic_test.go
    ├── wisdom.go
    ├── wisdom_test.go
    └── internal/
        ├── shared.go
        └── shared_test.go  

Only code within the domain package would be able to see and access the functions exposed inside the internal package. For a more in depth description, see the proposal or look at the go source code.

3
  • 1
    It's a good suggestion to use internal packages, but sometimes the function doesn't need to be shared, just tested.
    – Jeremy
    Commented May 27, 2021 at 18:54
  • So whats the appropriate way @Jeremy? Commented May 5 at 13:46
  • I think this is still the best suggestion @Shikhar. I haven't kept up with changes since 1.5 though.
    – Jeremy
    Commented May 25 at 2:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.