Show
Golang is a general-purpose programming language built for the multi-core reality of today’s computers (upwork). In this article:We will build a blog application where a user can:
This API will be built with:
You might be wondering seeing Postgres and Mysql. The API will be built in a way that you can decide to use Mysql or Postgres driver, simply by changing the configuration in the All methods and endpoints will be thoroughly tested. Table tests will also be used to test every possible case for a particular functionality. In Future Articles:This article is just the first part. There will be future articles that will explain how to:
I actually summed everything together in this article here. Where I built a Forum App using Gin Framework. React is used as the Frontend Stack. Let’s Begin! In case you just want to see the code, Get the source code for this article on github Step 1: Basic SetupCreate the directory the project will live in. This can be created anywhere on your computer. Trust me! Let’s call it fullstack mkdir fullstack Next, initiate go modules which makes dependency version information explicit and easier to manage go mod init github.com/{username}/{projectdir} Where go mod init github.com/victorsteven/fullstack We will be using third party packages in this application. If you have never installed them before, you can run the following commands: go get github.com/badoux/checkmail Next, create an mkdir api && mkdir tests Create the touch .env At this point this is the structure we have: fullstack Step 2: Env DetailsLet’s settle the database connection details now. Open the Observe that we have the The default connection is postgres. If you want to use mysql, simply comment the
postgres and uncomment the mysql details in the Note, you can only use one connection at a time. But you can make things fancy (not recommended) by using Postgres for live and mysql for testing. Well, you decide what to do. Step 3: Create ModelsInside the mkdir models Since we are building a blog, we will have two model files for now: “User” and “Post” It is better that the entire file is posted, rather than parts, to avoid any confusion. The In the path touch User.go The In the path touch Post.go Step 4: Create a Custom ResponseBefore we create controllers that will interact with the above defined models, we will create a custom response package. This will be used for the http responses. Inside the mkdir responses Then create the json.go file in the path: touch json.go json.goStep 5: Create JSON Web Tokens (JWT)Remember that users need to be authenticated before they can: Update or Shutdown their accounts, Create, Update, and Delete Posts. Let’s write a package that will help us generate a JWT token that will enable the user to perform the above actions. Inside the mkdir auth Create the token.go file in the path: touch token.go This is the content: token.goObserve from the
token.go file that we assigned the import: Step 6: Create MiddlewaresWe will create two main middlewares:
Inside the mkdir middlewares Then create a middlewares.go file in the path touch middlewares.go middlewares.goStep 7: Create Custom Error HandlingTo format some error messages in a more readable manner, we need to a create a package to help us achieve that. Remember we created a In the path mkdir formaterror Then create a formaterror.go file in the path: formaterror.go When we “wire” the controllers and routes package, the above will make sense to you. Step 8: Create ControllersNow we have reached the meat of the application. Let's create the Inside the mkdir controllers In the path touch base.go This file will have our database connection information, initialise our routes, and start our server: base.goObserve the underscore we used when we imported the mysql and postgres packages. You can simply import the one you need, except if you want to experiment with both. Also observe that we called the Let’s create a home_controller.go file that welcomes us to the API: In the path: touch home_controller.go home_controller.goLet’s Create the users_controller.go file: In the path: touch users_controller.go Let’s create the posts_controller.go file: In the path: touch posts_controller.go Let's create the login_controller.go file: In the path: touch login_controller.go login_controller.goNow, let’s light up the whole place up by creating the routes.go file In
the path: touch routes.go routes.goStep 9: Seeding the Database with Dummy DataIf you wish, you can add dummy data to the database before adding a real one. Let’s create a seed package to achieve that: In the path mkdir seed Then create the seeder.go file: touch seeder.go Step 10: Create an Entry File to the API DirectoryWe need to call the In the path touch server.go This is the content: Step 11: Create app entry file: main.goCreate the main.go file in the root of the fullstack directory touch main.go
This is the file that actually “start the engine”. We will call the Run method that was defined in server.go file This is the content: main.goBefore we run the app, let's confirm your directory structure: fullstack Also ensure that your database(s) are created and you have entered relevant data in the Now without further ado, let’s run the app: go run main.go This will start up the app and run migrations. starting the applicationYes! You did it. Well done. Step 12: Testing the Endpoints in PostmanYou can use Postman or your favorite testing tool, then in the next step, we will write test cases. Lets test endpoints at random: a. GetUsers (/users): Remember we seeded the users table b. GetPosts (/posts): Remember we also seeded the posts table c. Login (/login) Lets Login User 1: We will use that token to update the post in the next test. d. UpdatePost (/posts/{id}): To update a post, we will need the authentication token for a user and in the above, we generated a token when we logged in user 1. The token look like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE1NjY5NDE3MzgsInVzZXJfaWQiOjF9.CK8rJ-3IG3xHdCj3iHKA4ihizepij5HNXz4RWrXe50 I will use this token and put it in Authorization: Bearer Token in Postman: Then I will use JSON in the body to send data to update: If user 2 tries to update user 1 post, the response will be authorized e. DeleteUser (/users/{id}): For the user to shut down his account, he needs to be logged in first, so as to generate the JWT token. Let us login user 2 and delete him. Grab the token, insert it in Authorization: Bearer Token. Then use a “DELETE” method in your Postman. Observe that Status Code is 204, which means No Content. And the response is 1, which means success. f. UpdateUser (/users/{id}): Remember to login the ‘user to update’ and use the Token. Since User 2 is deleted, let’s update User 1: g. CreateUser (/users): We don’t need to be authenticated to create a user (signup). Let’s create a third user: h. CreatePost (/posts): Let the new user create a post. Remember again, he must log in and his token used in Authorization: Bearer Token You can test the remaining endpoints: i. GetUser (/users/{id})— Get one user (No token required) j. GetPost(/posts/{id}) — Get one post (No token required) k. DeletePost(/posts/{id}) — An authenticated User deletes a post he created( Token required) l. Home(/) — Get Home page (No token required) Step 13: Writing Test Cases For EndpointsWe have proven that then endpoints are working. But that is not enough, we still need really test each of them to further proof. Remember we defined a test database in our I am aware that we can use Interfaces and mock database calls, but I decided to be replicate the exact same process that happens in real life for testing. At the start of this project, we created the tests directory. Now, we will create two packages inside it: a. Models Tests We are going to test the methods in the models package defined in step 3 In the path mkdir modeltests cd into the package and create a file model_test.go cd modeltests && touch model_test.go Inside the model_test.go file:
You can also see that we wrote other helper functions that we will use in the actual test, such as refreshing the test database and seeding it, this will be done for every test. So no test depends on another to pass. i. user_model_test.go Now, let’s create tests for the methods in the User model. Create the file user_model_test.go in the path same as the model_test.go: touch user_model_test.go This is the content: user_model_test.goWe can run individual tests in user_model_test.go. To run TestFindAllUsers, simply do: go test --run TestFindAllUsers Get a more detailed output by attaching the go test -v --run TestFindAllUsers ii. post_model_test.go Let’s also create tests for the methods in the
Post model. Create the file post_model_test.go in the path same as the model_test.go: This is the content: Let's run one test: go test -v --run TestUpdateAPost Output: Let’s make a test FAIL so that you can see what it looks like for a failing test. For the
assert.Equal(t, updatedPost.ID, postUpdate.ID)toassert.Equal(t, updatedPost.ID, 543) //where 543 is invalid Now, when we run this test, it will fail: You can see that line: post_model_test.go:101 1 does not equal 543 Running all tests in the modeltests package: To run the test suite in the Then run: go test -v Yay! All tests in the b. Controllers Tests In the tests paths: mkdir controllertests Next, will we create the controller_test.go file where we will define the In the path: touch controller_test.go i. login_controller_test.go We will use TABLE TEST here. The idea behind the table test is that all possible cases defined in a struct, which uses a loop to run each test case. This saves us a lot of time as we tend to test all cases for a particular functionality with just one test function. In the same directory path as controller_test.go, we create a file touch login_controller_test.go With the content: Running the TestLogin: go test -v --run TestLogin TestLoginii. user_controller_test.go This test file extensively uses TABLE TEST to test all possible cases, rather creating separate test functions for each case, hence saving us a lot of time. In the same path as controller_test.go, create the user_controller_test.go file touch user_controller_test.go file Let’s run one test function inside that file to demonstrate. Running TestCreateUser go test -v --run TestCreateUser TestCreateUserObserve this: This is one of the cases in our table test: { Since this test hits the database and identified that the iii. post_controller_test.go TABLE TEST is also extensively used in this file. In the same directory as controller_test.go, create the post_controller_test.go file touch post_controller_test.go With the content: In this test file, we can run TestDeletePost function go test -v --run TestDeletePost
TestDeletePostRunning all tests in the controllertests package We can also run the test suite for the In the path go test -v All Tests passed. Feels Good! c. Running the Entire Tests in the app. At this point, your folder structure should look like this: Now, to run the test suite, you should be in the Then run: go test -v ./... This will run all the tests in the Not one test failing. How Sweet. Wrapping up.Frankly, I did a lot of research to put this article together. Because of writing test cases, I had to change the entire app structure. This was really a pain in the butt. I think is all worth it now, testing every single function/method ships a better software. Really, I feel good. What Next?
I actually summed everything together in this article here. Where I built a Forum App using Gin Framework. React is used as the Frontend Stack. You can follow me on medium to get updates. You can also follow me on twitter Get the source code for this article on githubHappy Coding! |