Assertions are something that I genuinely feel the standard library in Go is missing. You can most definitely achieve the same results with the likes of
if comparisons and whatever else, but it’s not the cleanest way to write your test files.
This is where the likes of stretchr/testify comes in to save the day. This package has quickly become one of the most popular testing packages, if not the most popular testing package for Go developers around the world.
Its elegant syntax allows you to write incredibly easy assertions that just make sense.
The first thing we’ll have to do in order to get up and running with the testify package is to install it. Now, if you are using Go Modules then this will just be a case of calling
go test ... after importing the package at the top of one of your
However, if you are still stuck on an older version of Go, you can get this package by typing:
After you have done this, we should be good to start incorporating it into our various testing suites.
A Simple Example
Let’s start off by looking at how we would traditionally write tests in Go. This should give us a good idea of what
testify brings to the table in terms of improved readability.
We’ll start by defining a really simple Go program that features one exported function,
If we were to write tests for this using traditional methods, we would typically end up with something like this:
We can then try run this simple test by calling
go test ./... -v, passing in the
-v flag to ensure we can see a more verbose output.
If we wanted to be a bit fancier, we might incorporate table-driven tests in here to ensure a wide variety of cases were tested. For now though, let’s try and modify this basic approach to see how
Awesome, as you can see, we’ve managed to succinctly test for equality using the
assert.Equal function. Straight away this looks like an improvement as we’ve got fewer lines of code to read over and we can clearly see what the test function is trying to achieve.
Negative Test Cases and Nil Tests
So, we’ve looked at happy path testing, but how about negative assertions and Nil checks. Well, thankfully the
testify package has methods that allow us to test for both.
Say we wanted to test a function that returns a status of a given application. For example, if the application was alive and waiting for requests then the status would return
"waiting", if it had crashed, then it would return
"down" as well as a variety of other statuses for when it’s serving a request, or when it’s waiting on a third party, etc.
When we perform our test, we would want our test to pass as long as the status equaled anything but
"down", so we could use
assert.NotEqual() in this particular, hypothetical case.
If we wanted to test to see if
"status" was not nil then we could use either
assert.NotNil(object) depending on how we wish to react to it being
Combining Testify with Table-Driven Tests
testify into our test suites doesn’t necessarily preclude us from using methods such as table-driven testing, in fact, it makes it simpler.
Notice the slight difference between how we called
assert.Equal() in this example compared to the previous example. We’ve initialized assert using
assert.New(t) and we are now able to call
assert.Equal() multiple times, just passing in the input and the expected values as opposed to having to pass
t in as our first parameter every time. This isn’t a big deal, but it certainly helps to make our tests look cleaner.
Another excellent feature of the
testify package is it’s mocking capabilities. Mocking effectively allows us to write replacement objects that mock the behaviors of certain objects in our code that we don’t necessarily want to trigger every time we run our test suite.
This could be, for example, a messaging service or an email service that fires off emails to clients whenever it’s called. If we are actively developing our codebase, we might be running our tests hundreds of times per day, and we might not want to send out hundreds of emails and/or messages a day to clients as they may start to take umbrage.
So, how do we go about mocking using the
A Mocking Example
Let’s take a look at how we can put
mocks to use with a fairly simple example. In this example, we’ve got a system that will attempt to charge a customer for a product or service. When this
ChargeCustomer() method is called, it will subsequently call a Message Service which will send off an SMS text message to the customer to inform them the amount they have been charged.
So, how do we go about testing this to ensure we don’t drive our customers crazy? Well, we mock out our SMSService by creating a new
smsServiceMock and add mock.Mock to its list of fields.
We then stub out our
SendChargeNotification method so that it doesn’t actually send a notification to our clients and return a
Finally, we create our
TestChargeCustomer test function which in turn instantiates a new instance of type
smsServiceMock and specifies what should happen when
SendChargeNotification is called.
So, when we run this calling
go test ./... -v we should see the following output:
go test ./... -v === RUN TestChargeCustomer Mocked charge notification function Value passed in: 100 Charging Customer For the value of 100 --- PASS: TestChargeCustomer (0.00s) main_test.go:33: PASS: SendChargeNotification(int) PASS ok _/Users/elliot/Documents/Projects/tutorials/golang/go-testify-tutorial 0.012s
As you can see, our mocked method was called as opposed to our “production” method and we’ve been able to verify that our
myService.ChargeCustomer() method acts the way we expect it to!
Happy days, we’ve now been able to fully test a more complex project using mocks. It’s worth noting that this technique can be used for all manner of different systems, such as mocking database queries or how you interact with other APIs. Overall, mocking is something that is really powerful and is definitely something you should try to master if you are going to be testing production-grade systems in Go.
Generating Mocks with Mockery
So, in the above example we mocked out all of the various methods ourselves, but in real-life examples, this may represent a hell of a lot of different methods and functions to mock.
Thankfully, this is where the vektra/mockery package comes to our aide.
The mockery binary can take in the name of any
interfaces you may have defined within your Go packages and it’ll automatically output the generated mocks to
mocks/InterfaceName.go. This is seriously handy when you want to save yourself a tonne of time and it’s a tool I would highly recommend checking out!
- Testify helps you to simplify the way you write assertions within your test cases.
- Testify can also be used to mock objects within your testing framework to ensure you aren’t calling production endpoints whenever you test.
Hopefully, this has helped to demystify the art of testing your Go projects using the
stretchr/testify package. In this tutorial, we’ve managed to look at how you can use assertions from the
testify package to do things like assert if things are equal, or not equal or nil.
We’ve also been able to look at how you can mock out various parts of your systems to ensure that, when running your tests, you don’t subsequently start interacting with production systems and doing things you didn’t quite want to.
If you found this useful, or if you have any comments or feedback, then please feel free to let me know in the comments section below.
If you enjoyed this, you may like my other articles on testing in Go: