Okay.
So next, I want to show you how we can easily write some end to end tests for our microservices so
that we can launch some tests against the real application and make sure everything is working as expected.
Now, these tests are going to be different from unit tests or integration tests.
These tests are actually going to test the production code running inside of a Docker container with
all of the other dependencies also running.
This kind of test is going to give us the most bang for our buck and make sure that the app is still
functioning correctly.
So let's go ahead and see how we can do this.
I'm going to go ahead and create a new end to end folder in the root of the project, and I'm actually
going to create a new package.json inside of this end to end folder.
It's going to be a separate project all by itself so we can open up a new terminal and CD into the end
to end folder and run npm init for the package name.
I'll just call it sleeper end to end and we can continue through the rest of this setup instructions
to generate a package.json.
So let's cd back into the end to end folder and we can npm install dash D for development dependency.
Make sure we install jest, which is what we're going to use to run the tests.
TypeScript jest TypeScript itself and then the types for jest.
So now that we have a package.json, go ahead and create a tsconfig.json so we can include a very basic
set of instructions for how to compile our TypeScript code.
So we'll include all the typescript inside of a new specs folder.
So we'll create the specs folder and this is where we'll actually include all of our tests themselves.
And then the rest of these options for TS config are just some basic out of the box options.
So let's go back to our package.json and now we'll change the test command here to be jest and we can
go ahead and create our first end to end file.
Right now we'll just call it app dot end to end expects.
We can just run an example test, so I'll say it should pass and we can just return.
Expect true to be truthy.
So we should definitely expect this test to pass so we can now run this from our end to end folder by
running NPM test and we can see just running and it has correctly executed our test.
So this is great.
However, now we want to actually run our application and execute tests against it.
So in order to do this, we're going to turn this end to end app into a Docker application that can
run alongside of the other microservices in our application.
So it can easily launch requests against them and make assertions.
So to do this, we'll just simply create a Docker file in in the end to end folder and this will be
a very basic Docker file.
We will just extend Node Alpine here and then we'll set the working directory at user source app.
Next we'll copy over our entire end to end folder and run npm install g npm to get NPM.
Then we will install our dependencies by running npm install.
And finally we can execute the command for this docker image which will simply be NPM test.
To build this image, we'll run Docker, build tag end to end and go ahead and specify the current path
at end to end, which is our current directory and we'll let the Docker image build.
Okay.
So now that we've built our Docker image, which contains all of our dependencies and test files, we
can run it by running Docker run end to end.
We can see the Docker image has run jest has run our apt end to end spec and has passed.
So now that we have our end to end Docker image, we want to run the rest of our microservices alongside
with it so that we can make real assertions in our end to end test.
So let's go ahead and create a new Docker compose file in our end to end directory where we'll go ahead
and define a new services list here and specify our first service.
Of course we know the end to end container, so we'll go ahead and specify how to build this at our
Docker file, which of course will just be in the current directory for the Docker file.
And importantly, I want to mount our specs folder into the user source app specs directory.
And that's so if we make changes to our tests, they will be reflected in real time as we run the container.
So now we want to add the rest of our microservices.
We can easily do this by going to our root docker compose.
And let's go ahead and copy all of these services, including MongoDB, because of course, we will
need to run a mongo instance for our application to function.
However, now instead of building these services, we're going to use the production image directly
to test against so we can specify an image here.
We can get the image name from our templates reservations deployment file where we have the image name
and paste it in.
Now.
We don't need to override a command.
We can let the image run using the production node command.
We will change the path to the file to go up one directory into app slash reservations and we have no
need for a volume in this service.
So I'm going to go ahead and copy this configuration and paste it for the rest of our services, including
auth.
Payments and notifications.
Of course we'll make a change here to reference the auth image.
The auth m file and open up our port at 3001 instead.
We'll do the same thing for payments, so make sure we reference the correct path to payments and we
have no need to open a port up.
Same for notifications.
So now we've defined all of the other services that will run alongside of our end to end container.
However, now I want to make sure that when we start up the end to end container, the rest of our services
are already running.
So Docker Compose lets us do this easily.
For our end to end definition here we can specify a new depends on list, where we provide the name
of the services that we want to wait to be started before we start up.
So I'll just say that the end to end container will depend on reservations.
Off.
Payments.
Notifications and manga so that everything else starts up before we actually run our tests.
So now we can test this out by running Docker compose up and to end in our end to end folder.
And you can see here that it has gone ahead and created all of the microservices and is actually running
and attaching to the end to end container and it has passed our one test.
Additionally, we can show that our volume is working by changing expecting false to be true and rerunning
the test.
We should see that it now fails because we have volume mounted The updated tests which of course has
incorrect logic and we can see that the container fails here because the test failed.
So I'll go ahead and revert this test and now we can actually start making assertions against our real
running services.
The first test I want to write is the most basic.
It's just called health check API end to end dot spec.
And this is going to be exactly what it sounds like.
It's going to be the health of the applications that we have.
So I'll go ahead and create a new describe block and call it health here.
So let's go ahead and open up a new function where we'll go ahead and describe each service we're going
to test the health of.
So we'll start with the reservations, which is our most important gateway into the system and change
this to a test block.
So all we're going to do in here is write asynchronous function, and all it's going to do is call,
await Fetch, and we'll go ahead and specify the host name, which of course, we know since we're running
inside of the same Docker compose, we are going to reach out with the service name that we defined
here.
So it will be just reservations and that will be on Port 3000.
So we know we have a health check configured for reservations.
So this should respond with an okay 200 status code and we can go ahead and check that here by setting
it equal to response.
And we can use just here to now expect that the response dot okay is a truth value, which only will
be the case if we get A2XX status code.
So let's go ahead and remove our other apt end to end spec and we can go ahead and test this out in
our terminal by running Docker compose up so we can see that our health check has passed and we got
a 200 status code.
We can even go ahead and run Docker container logs on the reservations container to actually see the
request.
So let's do that Docker container logs and paste in the reservations.
You can actually see our peanut logger here logging out the request from our integration tests with
a 200 status code.
So let's go ahead and repeat the same process for the other HTTP health check endpoint.
We have the auth service, so we'll test the auth and expect the response to be, okay, we can save
this and go ahead and retrigger the test by running Docker, compose up end to end.
So you can see that our test failed because our fetch failed here.
And you can see of course because my HTTP port is incorrect, it should be at 3001.
And so now if we go ahead and try to rerun this test, we should see that it passes.
So now both of our HTTP health checks are passing and we know if the service is starting up correctly
and we don't have any runtime errors in the application because we have successfully opened up traffic
in that container.
Okay.
So next we want to do the same health check on our other TCP microservices.
However, they don't have an HTTP port exposed.
It's just a TCP port.
So in order to ping it correctly, we're going to go ahead and install some new dependencies to support
this.
Let's go ahead and install some development dependencies.
So we're going to install a package called TCP ping.
It's going to be able to ping a TCP connection based on the address that we provide, which is exactly
what we want to support this, we're going to have to install some additional jest infrastructure.
So we'll install TS, jest, TS, node and types slash node and the types for TCP ping.
And since we're exposing additional ports in our other services now let's go ahead and specify that
in our Docker compose.
We'll go ahead and map the 3003 port on payments to our local 3003 and we'll do the same thing for notification.
We'll go ahead and map 3004.
So let's go ahead and now test our payments Microservice to start, we're going to accept a callback
function and this time we're going to accept a new done parameter which which we can call to signify
that we are done with the test.
And it was successful.
So let's go ahead and open up a function where we'll now call Ping and you can notice a.
Here.
I've imported ping from tcp ping.
Ping takes an options object where we specify the address of the microservice.
So we know this is payments as we've defined in our docker compose and the port will be at 3003.
Next we receive a callback that gets access to an error object if one is thrown.
So we will check to see if there is an error.
Then we will call the jest function.
Fail to immediately fail the test.
Otherwise we will call done and signify that the test was successful.
Next, in order to have jest properly compile our TypeScript code in this file, we will make use of
the JS package we installed.
We need to create a new file an end to end called.
Jazz dot config dot ts and we'll have a module dot exports set equal to an object here where we specify
the preset is TS jest.
Now we can rerun Docker, compose up end to end and signify dash dash build to tell docker compose to
rebuild our end to end container and install our new dependencies.
You can see our health check to our payment service is successful and our test is passing, which shows
that our service is up and running and responding to traffic.
Let's finish our health check tests up by copying this payments block and creating one more for our
last microservice.
The notifications microservice.
The address will change to notifications and the port will be at 3004.
We can verify this works by running Docker.
Compose up end to end.
You can see that all of our services are responding to our health check successfully.
Okay, so now that we've verified the health of our application, let's test out some of the functions.