DEV Community

Cover image for πŸ“– Go Fiber by Examples: Testing the application
Vic ShΓ³stak
Vic ShΓ³stak

Posted on • Edited on

πŸ“– Go Fiber by Examples: Testing the application

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!

↑ Table of contents

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.

↑ Table of contents

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.

↑ Table of contents

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:

VS Code unit-test

Click them and a template like this will be created:

VS Code Go tools demo

↑ Table of contents

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)
  }
}


Enter fullscreen mode Exit fullscreen mode

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)


Enter fullscreen mode Exit fullscreen mode

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.

↑ Table of contents

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.

↑ Table of contents

Photos and videos by

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.

support me on Boosty

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.

Other my small projects: yatr, gosl, json2csv, csv2api.

Top comments (4)

Collapse
 
jhoval profile image
Mauro Franchini

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**?

Collapse
 
bimbimprasetyoafif profile image
Bimo Prasetyo Afif

I used config DisableHeaderNormalizing: true as my fiber init in test. But it seem not work properly with app.Test(). Any idea?

Collapse
 
shayegh profile image
Mojtaba Shayegh

Hi,
How could I increase Test coverage for my Handlers when using Fiber Test?

Collapse
 
daveteu profile image
Dave

what's test.description for when it doesn't print?