- So there's another kind of test in Go
that a lot of people don't know about
and they're called Example Tests.
In fact, Examples serve two purposes,
they help with providing examples
in the Go documentation that your code is producing
and for a test, so what I'm gonna show you here is
I've added another file, another underscore
test file to this project called handlers_example_test,
and in here I've written a function called Example.
Now, what's really interesting here and on this example
is that there is no parameter being passed into it.
We just start with the word example,
and then again what we're gonna have here
is something that we want to test,
and because this is an example,
what's gonna happen is is we've got to bind our example
to an actual exported function in this package.
So notice that I have a function here called SendJSON,
this is now ExampleSendJSON.
You know if I give this some other name,
notice I've got some squiggly lines here,
the Go linters identified that this example
doesn't refer to some known exported thing
inside the package, you see that golint
is really trying to make sure that we do this,
and this is because if it doesn't bind
to something that's exported, it's not gonna show up
in the GoDocs, so I'm gonna show you
the GoDocs in a second.
Now, if you're talking about a method,
something like a method off of a type,
then there's a naming convention
where you're gonna be using a type name
and an underscore for the method,
so if this was a method off of some type,
we'd be using the type name, and then we'd be
using underscore to denote the method,
golint will help you find in your way there.
Okay, so notice it says ExampleSendJSON.
What I'd like to do before we run this code
and look at it is, look at something
that's a little bit more, maybe real world,
and I've moved over to the KIT pack, the KIT project
that I have under ardanlabs
and specifically looking at the log package.
Now notice that in log I did do log_examples_test,
this is my naming convention for examples
when I want to split them.
All you really need to do is have in it a
test_test file.
And you can see that this example
is showing the user how to use the log API.
Now, what I want to do is a couple of things here.
Let's go ahead and run GoDoc.
Now, to run GoDoc, I just type godoc
and I tell it what part I wanted to listen on for http.
By the way, this is great because what GoDoc does
is able to read your Gopath and all of the
source code on your machine and give you documentation.
So look at what I've done here, I've said godoc-http:3000
and if I come into my browser now and do localhost 3000,
this might look familiar to you.
In fact, if I go to
if I go to golang.org
right here, which is where we get documentation
when we're online, looks pretty similar doesn't it?
Yeah, because golang.org is really built around GoDoc,
just with some modifications.
Okay, and what's nice is I can click on Packages.
So a lot of times I'm too cheap to buy internet
on the airplanes, and I'm on airplanes all the time right,
so when I need documentation, guess what I do?
GoDoc on the command line and I don't need
to worry about golang.org not being available to me.
Now, what's also cool here is I've got a list
of all of the packages, not just
in the standard library, but also inside my Gopath,
look at that, in my Gopath.
Now the thing that stinks
is they didn't give us a search feature.
So, if I want to find that log package under Kit,
I'm gonna have to start searching,
so there's ardanlabs right there,
and I've got a lot of stuff under ardanlabs, right
that's all the Go training stuff here.
This is the only thing that stinks about our GoDoc here
is trying to find what you want
'cause I've gotta use the browser search feature,
but here's Kit and here is log.
Now, you might find this interesting
that there's some really nice looking documentation here.
Look at that, Package log,
where did all this documentation come from,
where did all this overview come from?
Look, the code you are writing is also documentation
and anything that's exported, is coming out in the index,
and any comments you put above anything that's exported,
is going to be comments in the documentation.
Look at this, and look what you see here.
Example
and what do you see?
All the code I wrote inside the example function.
So, here's some rules that I'm going to put in
and what golint does is, well,
every comment you write is code,
not only is every comment code, it's documentation,
and so comments must be proper sentences,
grammar, punctuation, everything,
and in a code review I am looking for that,
because every comment you write,
especially those above anything that's exported,
show up here.
Remember, your code is also documentation.
Write clear, concise sentences, grammar, punctuation,
the whole nine yards.
and you can see how the example code
that I wrote inside this function
showed up in the Go documentation.
And if we look at part of the API you see,
this is why I write clear, concise, complete
grammar type sentences.
Now, I have a lot of overview in this documentation.
When you're gonna have a lot of overview
the best thing to do is head to doc.go file
and what you're gonna is put all that overview
documentation there above the package log.
Any comments you put above package log becomes overview.
And when you're gonna have a lot of it, like 40 lines of it,
don't put it into a normal source code files,
just do a doc.go, it all has to be about package log.
And how you space things and indent things
will show up as code blocks
or sometimes even section headers.
Okay, brilliant, so look at that, that is some really cool.
So these example also become part of your documentation
and golint will also try to help you make sure
that your comments are there and they're gonna be useful
from a documentation standpoint.
But I started this with the idea
that an example is also a test and I want to show you that.
Notice how this example has at the bottom this output
directive using a comment.
What this means is that anything, any comment
you now put at below, and you're gonna have
multiple lines of comment here,
is now what we expect to see from standard out.
So anything in this example writes to standard out,
we can now validate, think about this.
You have example code in your documentation,
if somebody comes to you and says,
I don't know how to use it, you say
dude, I've added examples to the GoDoc, just read it,
but you've also validated that your examples work
with every build of your source code.
So if anybody copies and pastes that,
you don't have to worry about them coming back and saying,
hey, it's not working.
No, no, no, no, I know it's working
because that example produced this standard out,
this output out of standard out and I tested it.
So look at this, we're saying that ExampleSendJSON
should always produce this JSON document.
Now, let's go ahead and test that, right.
Let's go and test that.
So let's come in here, let's call our godoc for a second,
we're still in handlers, I've got my example test,
and what I want to do is say go test,
but this time I'm gonna tell it to just run
this ExampleSendJSON,
and when I do that, it's just gonna say pass, okay,
because the output's the same.
So let me do the following.
Let's pretend that this was the expected output.
Just bill ardanlabs., we know it isn't,
let's say that's what it was expected,
now watch what happens when I run just Example.
It says, hey Bill, you know you got this,
this is what you got, but you wanted this.
Hey, this test, this test fails.
Oh, how brilliant is that?
So I've got examples that show up in Go documentation
with the idea that I can also validate
that these examples work by testing
what this example code outputs.
So we can just fix this output now, there it is.
Run it again, brilliant.
My examples are now passing.
So examples are really powerful.
Now, you can't have an example as a test,
examples don't have to be tests, by the way.
When we were looking at my log,
there was no output directive here,
I was purely using it for documentation,
but if you decide you want it to be a test,
just remember that the output has to be consistent.
So, if you've got output that changes based on
what time it is, and where you are and things like that,
you can't do this, we're not gonna change code
to make sure that this test can do that,
that's bad, right.
Code is for the users, we've got to really focus
on usability first, I don't want to add things
to an API just to make it testable,
but we can create APIs that are testable
by focusing on the inputs and the outputs.
But, here we are, here's an example
that will show both in the Go documentation
and we can run it as a test.