Introduction
Hey, DEV friends! π
Today I'm going to share with you another chapter from my canceled book Easy Development with GoFiber (I talked about it in the first article of this series). Actually, this chapter was supposed to be in the practical part of the book (the article series), but I purposely pulled it out as the third chapter.
I think application testing is just as important in understanding how the web framework works under the hood.
Plan for the Chapter 3
In this third article (or chapter), we will review the topic of the Fiber application testing for beginners on real example.
And weβre going to cover the following π
π Table of contents
Reasons for testing
Testing written code is a very useful and needed skill, so we will start this article with a review of the Fiber application testing process.
We will use both the features built into the language and the framework for testing and some third-party tools. For example, the very popular Go testing package Testify.
βοΈ Note: Don't worry if you've never written unit-tests in your Go applications, because it's a very easy process to understand!
What does the test function include?
The test function is very similar to the usual Go function, but has only the purpose of checking the incoming conditions on strictly defined test cases. To avoid confusion between them, always add the _test
suffix to the name of the test file, and begin the name of the test function itself with the word Test.
For example, if we need to cover the SomeFuncToExample
function, which is in the example.go
file, we need to create a new file named example_text.go
, in which we need to place a test function named TestSomeFuncToExample(t *testing.T)
.
βοΈ Note: Also, note that the test file must be inside the same Go package as the function to be tested.
Typically, each test function contains:
- A structure that describes the incoming conditions.
- A collection of test cases, according to the structure.
- A test instance of an application with some simple state.
- A loop with test logic inside, to do an enumeration of test cases.
Creating a test function
Now, let's look at an example of such a test function on a very simple example: we're going to check one of the application paths for what HTTP methods it gives back.
If it gives a status of HTTP 200 OK, then this test case will be successful (PASS). In the case, if it will give a status of HTTP 404 Not Found, then it will fail. But we will be ready for this error.
Such a test case is needed to test our target function not only for a positive scenario, but also for a negative (FAIL) scenario, which may also occur as a result of using the application in the future.
Tip for VS Code users
If you write code in VS Code with the Go tools plugin (like me π), it has a built-in generator for unit-tests that generates a starting template for testing a particular function:
Click them and a template like this will be created:
Test code and detailed description
OK! Let's write our first unit-test. As always, please follow the comments in the code, it will help you understand what is going on in each of the lines.
// ./go/testing.go
package routes
import (
"net/http/httptest"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert" // add Testify package
)
func TestHelloRoute(t *testing.T) {
// Define a structure for specifying input and output data
// of a single test case
tests := []struct {
description string // description of the test case
route string // route path to test
expectedCode int // expected HTTP status code
}{
// First test case
{
description: "get HTTP status 200",
route: "/hello",
expectedCode: 200,
},
// Second test case
{
description: "get HTTP status 404, when route is not exists",
route: "/not-found",
expectedCode: 404,
},
}
// Define Fiber app.
app := fiber.New()
// Create route with GET method for test
app.Get("/hello", func(c *fiber.Ctx) error {
// Return simple string as response
return c.SendString("Hello, World!")
})
// Iterate through test single test cases
for _, test := range tests {
// Create a new http request with the route from the test case
req := httptest.NewRequest("GET", test.route, nil)
// Perform the request plain with the app,
// the second argument is a request latency
// (set to -1 for no latency)
resp, _ := app.Test(req, 1)
// Verify, if the status code is as expected
assert.Equalf(t, test.expectedCode, resp.StatusCode, test.description)
}
}
Alright, now let's go into more detail only about the logic of the test function, which is in the for loop. The other parts of the test function should already be familiar to you from other sections of this article.
So, at the very beginning of the loop, using the standard Go package called httptest, we create a new HTTP request with the GET method to be sent to the routing address from the test case.
βοΈ Note: It's the following line:
resp, _ := app.Test(req, 1)
.
This passes the newly created HTTP request to the Test function built into the Fiber web framework, which we looked at earlier in this article. This is required so that the application instance can process the request.
The next step is to compare two HTTP statuses: the one obtained from the request and the one we specified in the test case. Here we first encounter the most frequently used function assert.Equalf
from the Testify package, the purpose of which is simply to compare two values. If the values match completely, the test will be considered passed.
Running this test with the command go test -v ./...
, we will see that test was successful:
=== RUN TestHelloRoute
--- PASS: TestHelloRoute (0.00s)
Congratulations! π We just wrote a unit-test for a function, and it succeeded. Yes, that was just one use case, but trust me, the same thing is waiting in real applications.
We will see it for ourselves in the next parts of this series.
Summary
We wrote our first test case for the application, using the Test
method and the third-party package called Testify. I truly hope that now it will be much easier for you to write tests for your Fiber (and not only) web applications!
In the next article, we will be working with internal and external Fiber middleware, third-party packages and boilerplate.
Photos and videos by
- Clint McKoy https://unsplash.com/photos/App9lCSl2uA
- Go Team at Google https://code.visualstudio.com/docs/languages/go
P.S.
If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! π»
βοΈ You can support me on Boosty, both on a permanent and on a one-time basis. All proceeds from this way will go to support my OSS projects and will energize me to create new products and articles for the community.
And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!
My main projects that need your help (and stars) π
- π₯ gowebly: A next-generation CLI tool that makes it easy to create amazing web applications with Go on the backend, using htmx, hyperscript or Alpine.js and the most popular CSS frameworks on the frontend.
- β¨ create-go-app: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.
Top comments (4)
Hi Vic! Thanks for investing time in writing this serie of blogposts once again. I think there is one small typo:
Here it says:
For example, if we need to cover the SomeFuncToExample function, which is in the example.go file, we need to create a new file named **example_text.go**, in which we need to place a test function named TestSomeFuncToExample(t *testing.T).
Should it be ** example_test.go**?
I used config
DisableHeaderNormalizing: true
as my fiber init in test. But it seem not work properly with app.Test(). Any idea?Hi,
How could I increase Test coverage for my Handlers when using Fiber Test?
what's test.description for when it doesn't print?