- Well, the last two tests we created,
that basic unit test and the table test,
they were fantastic.
Right, we're testing the GET call.
But here's the problem with those two tests.
They require a connection to the outside world.
We're hitting a live sever.
We normally aren't that lucky in our CI environments
when we have access to the outside world.
Now, I always want to be very careful about mocking,
I don't want to mock databases,
I want to use Docker for that.
Certain systems where it's critical that we know
that we're talking to live systems or binary protocols.
Everything is working.
I'd rather use Docker for that,
again, the databases, don't mock databases.
But a lot of things can be mocked,
and these tests that we did run, could be mocked.
Right, like, I don't particularly care,
I know that web servers work.
So I don't care about the http protocols, here.
What I care about is that if I send this request
and I get back the response, that I can process it.
And so what I want to show you is how you can mock
a GET call, already built into Go's standard library
and the language.
And what's nice is,
Go gives us this package called httptest.
We're going to use this to mock these GET calls
and we'll use them again later,
to be able to mock some of our internal stuff.
So, look at what we do here in this code.
Since we're going to be making a GET call
to the goinggo dot web server for that RSS feed,
which is nothing more than an XML document,
what I've done here is I've created a raw string,
right there.
It's called feed, it's raw.
You see the use of those tick marks here
and what those tick marks are doing
is respecting even the carriage return line feeds
and the tabs, or anything I put into that space.
So, this is a RSS document that represents
at least a portion of what we expect to get back,
when I hit the goinggo.net web server.
Great.
Now I've created some structs here,
and with the XML tag, so we can pull this data
when we mock the call and get this raw string back.
We can unmarshel it into our real data structures here.
And do some actual testing.
But the whole key to the mock, is this function right here,
called mockServer.
I want you to notice mockServer is using the pointer
semantics for the server type, inside httptest.
The server type allows us, during unit testing,
to kind of stand up a web service
on some random port
that we'll be able to get back
and write code that we know executes on the call.
Bottom line is this,
when we call NewServer right here on the return,
then NewServer's going to stand up a web service
on a port, that it will tell us.
And when we hit the endpoint that it gives us,
any endpoint that it gives us, it's going execute
this function that I'm defining right here on line 63.
I like when I define literal functions to assign
them to a variable, and then pass them into what I'm doing.
I just find that the code is more readable.
A lot of people like to, kinda, define that function
in the calls.
I'm looking for readability.
So, look at this, a basic handle or function at Go
uses this signature.
The ResponseWriter, which is an interface,
and the request which is a concrete type.
That is a concrete type, this is an interface type.
And what's going to happen is,
is when we hit our mock server,
we're going to write 200 into the response
we're going to set our application to XML.
Just like the goinggo web server would do.
And then, we're going to use this Fprint line function
to take our raw string and to send it, or stream it,
through the ResponseWriter.
Brilliant.
So, no matter what we hit, on this mock server,
this function gets called.
Which means we can mock any http GET call that we want
and from our code perspective, or you know,
it did go out, it didn't really leave the machine
but it did actually go out and do what it had to do.
Okay, so, watch what we do now with our mock server.
We're going to check that our status is okay.
We call the mockServer function
We get back our server pointer.
Now we have stood up a actual web service
and any time we hit it,
it's going to execute this function here.
We have to defer the close
'cause we've always got to be clean.
So, given the need to test downloading content
like we have, when we check this URL,
look at the field, .URL off of our server pointer.
So whatever this endpoint is,
that's what we're going to hit.
We're not going to hit the actual goinggo.net server,
we're going to hit our mock server.
This is going to be local host on some random port.
And if you extend that local host, some random port,
with any sort of URL you want,
that's okay, it's still going to hit this function.
And you can look at the request,
and put some, maybe, conditional logic in here,
for the different actual endpoints
beyond the port that you might want to test.
Great, we expect this status code.
So now we call GET like we always have,
using this local host URL.
And then the rest of the code goes ahead
and checks the status unmarshels into our document.
We just validate some things,
like we've got the right number of items.
So, remember that this test before
took about 800 milliseconds on the network that I'm on.
Let's see how fast it is to run this test
now that we're mocking it and not leaving my machine.
So, I'm already in the folder that we need to be in there.
There's the example3 underscore test.
And I'm going to run test, with the minus v.
What I want you to see is how fast this test ran.
This is 14 milliseconds, not 800 milliseconds,
14 milliseconds.
We didn't leave my machine.
And you can see, this is the endpoint that we hit,
that was given to us by our mock server,
this 49897 is that port.
So, we hit that, when we hit that URL
at that, you know, local host and port,
we ended up executing this code right in here.
And we mocked out the call,
and we got back what we expected,
which was a valid document on the 200.
And look at this, our test that would have ran
for 900 milliseconds that we've had to worry about
having access to from a server,
we now mocked, and I feel very confident that this GET call
works, because our mock is pretty much real as you can get
hitting a server.
And now, it's even better from my perspective is,
it's not just about the happy case.
I can start doing some really nasty things
around what happens if this fails, and that fails,
and things I wouldn't really be able to cause
my external server to do.
I can mock a bunch of failure here,
and validate that we're handling everything
that could possibly happen.