
[ad_1]
Build a Modern REST API with Go – Part 5

This article is the fifth and final in a series covering all aspects of implementing a modern REST API microservices step by step:
- Defining a SQL First Data Model with SQLC
- REST API to Gin. apply with
- configuring with wiper
- Building and Running in a Container
- containerized testing
All codes are available for the series Here,
Now that we’ve built our API microservice, we want to implement tests to make sure it’s working correctly. We have a two-tier service design: the database layer and the HTTP API on top of that. So how do we test it?
We can do this by testing every SQL query we generate, and mocking the actual database, and deciding to implement full unit tests and unit tests for our database layer. We will then test the HTTP API independently by mocking the database layer and testing each API handler. This is a general approach and makes a lot of sense.
Another approach would be to do integration tests. The idea of integration testing is to test your application using only its public API. So in our case, we would do:
- Start the database and microservices, just like the production stack in the previous article,
- We will then implement a REST API test client that will call the microservices API to test all the provided methods.
It is also commonly used in industry to test microservice-based architectures and ensure that the entire application or its subsystems are behaving as expected.
However, both approaches present some challenges. On the one hand, full-on unit testing requires writing a lot of test code, and in our case, testing the database layer is basically testing the postgres SQL driver and sqlc generated code. In the same spirit, testing the HTTP layer independently, mocking the database layer is again a lot of code that needs to be maintained.
On the other hand, integration tests that rely only on the API are great but the checks we can implement are publicly exposed by the API and we would certainly not implement a private API for testing purposes. don’t want to!
best of both worlds?
So, in this article, we’ll take the best of both worlds and implement something in between unit and integration tests:
- To avoid mocking the database, we will introduce a containerized database to support our tests,
- In order to enable granular checks, we will not separate the client and the server, but bypass the network layer and make requests directly to the server.
Please note that this is a very thoughtful approach. However it is very efficient and will definitely save you a lot of time.
To write our tests, we will use stretcher / testimony Libraries for grouping our tests test suite, Using suites allows us to perform the initialization steps before the whole suite and before each test:
setup suite The method is executed once before the execution of the entire suite. We set up the server the same way we do for the main function. The only difference here is that we don’t start the server. Everything else is the same: we use the same configuration mechanism, we instantiate database connections in the same way, and register the same handlers.
setup test The method is executed before each test of the suite. In our case, we truncate the author database to start with a clean slate.
we are using httptest
Standard Go test library for testing our API. This library can be used to test any HTTP application, especially a REST API. It enables “recording” requests and responses.
We are building HTTP requests and parsing HTTP responses the same way we would for a real server, the only difference is that we bypass the network stack by invoking the router directly ServeHTTP
way. Furthermore, we are testing the code the same way it is executed in production.
After calling the server, we validate the response by saying that each field has the expected value.
In the above test, we want to test the update handler but to update a writer, we need to create one first. Since we have not separated client and server, we can easily create required writer in database as shown above.
We are also going to use Docker Compose to run containerized tests as we did in the previous article. To achieve this, we’re going to:
- Create an image with test
- Run this test container image as part of our test stack
test container build
The first step is to create a test container. please note that Tests are not executed during this phase, We are just creating a container which contains the test code to be executed in the next step:
We simply copy the source code inside the container and specify the command to be executed when the container is started. This command justTake the test./…“Which will execute all the tests.
run test stack
Now that we have our test container ready, we need to define our test stack. We’re going to use the same stack as the production stack, except that instead of running the microservices, we’ll be running the tests ourselves:
The build section for tests tells Docker Compose to build the container before starting the stack. We are also configuring our tests to use a containerized database.
We can update our Makefile to add the test target:
The compose command has the following parameters:
--build
Forces creation of test container image--abort-on-container-exit
Forces the stack to stop when a container exits. This allows the stack to stop when our test container has finished running tests. The exit status of the test container is propagated to the compose command, that is, if the test fails, the compose command also fails. This is helpful when running tests in a CI/CD pipeline.--force-recreate
Forces containers to be rebuilt even if their configuration and image have not changed.--remove-orphans
Removes containers for services not defined in the compose file.
This ends this series about modern REST APIs using Go. Congratulations on getting here! I hope you enjoy it as much as I do and find it helpful! have fun!
[ad_2]
Source link
#Containerize #unit #integration #tests #Bertrand #Quenin #August