ML
    • Recent
    • Categories
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    Mocking API Endpoints with Go

    IT Discussion
    golang unit test mock api api
    5
    7
    882
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • stacksofplatesS
      stacksofplates
      last edited by stacksofplates

      So I had to mock an API in something I'm writing for testing and I figured I'd post this to show you an easy way to do it in Go.

      First you need to get the JSON structure for the response you'll be mocking. To do this you can just use cURL or whatever tool you want.

      Let's use JSON Placeholder since it's free and doesn't require any authentication. If you go to this URL, you'll see the comments API: https://jsonplaceholder.typicode.com/posts/1/comments

      I use this site to convert JSON to structs. If you paste in the JSON data you get a struct like this:

      type Comment []struct {
          PostID int    `json:"postId"`
          ID     int    `json:"id"`
          Name   string `json:"name"`
          Email  string `json:"email"`
          Body   string `json:"body"`
      }
      

      So we just need to be able to mock this in our unit tests. Here's a small app that reaches out to JSON Placeholder and gets all of the comments that exist for a specific post.

      package main
      
      import (
      	"encoding/json"
      	"fmt"
      	"io/ioutil"
      	"log"
      	"net/http"
      )
      
      type Comment []struct {
      	PostID int    `json:"postId"`
      	ID     int    `json:"id"`
      	Name   string `json:"name"`
      	Email  string `json:"email"`
      	Body   string `json:"body"`
      }
      
      func (c Comment) getComment(u string) {
      
      	req, err := http.Get(u)
      	if err != nil {
      		log.Fatal(err)
      	}
      
      	body, err := ioutil.ReadAll(req.Body)
      	if err != nil {
      		log.Fatal(err)
      	}
      
      	error := json.Unmarshal(body, &c)
      	if error != nil {
      		log.Fatal(error)
      	}
      
      	fmt.Println(c[0].Name)
      
      }
      
      func main() {
      	var c Comment
      
      	c.getComment("https://jsonplaceholder.typicode.com/posts/1/comments")
      }
      

      However, when you're unit testing, you don't want to have to actually reach out to the site. That's more of an integration test. So to be able to test our method, we need to mock that API endpoint. So to do that Go has a test server built in for this reason. You can use it like this:

      package main
      
      import (
      	"io"
      	"net/http"
      	"net/http/httptest"
      	"testing"
      )
      
      type commentTest struct {
      	actual   string
      	expected string
      }
      
      type commentInt struct {
      	actual   int
      	expected int
      }
      
      func TestgetComment(t *testing.T) {
      	data := `
      	[
        		{
          		"postId": 1,
          		"id": 1,
          		"name": "test name",
          		"email": "[email protected]",
          		"body": "this is the body"
        		},
        	]
      	`
      
      	ts := httptest.NewServer(
      		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      			io.WriteString(w, data)
      		}))
      
      	defer ts.Close()
      
      	var c Comment
      
      	c.getComment(ts.URL)
      
      	var tests = []commentTest{
      		{c[0].Name, "test name"},
      		{c[0].Email, "[email protected]"},
      		{c[0].Body, "this is the body"},
      	}
      
      	for _, tt := range tests {
      		if tt.actual != tt.expected {
      			t.Errorf("got %v, want %v", tt.actual, tt.expected)
      		}
      	}
      
      	var testInt = []commentInt {
      		{c[0].PostID, 1},
      		{c[0].ID, 1},
      	}
      
      	for _, ti := range testInt {
      		if ti.actual != ti.expected {
      			t.Errorf("got %v, want %v", ti.actual, ti.expected)
      		}
      	}
      
      }
      

      This particular test happened to be a little long because of the two types of data. There might be an easier way by building one struct for both the int and string types but this way was easy to write really quickly.

      I don't want to get into how testing in Go works. However, the important line is ts := httptest.NewServer(). That's where we define our test server. We tell it to take data and put that into w which is our response. So when we run our c.getComment() method we wrote earlier, we can pass ts.URL which is the URL of our test server and we will get a response of whatever is in data. Then we just compare the results to what we expect them to be and if they aren't the same the test will fail.

      It's good to note that if your JSON data is long, you probably don't want to put it in line. So you could store it in a file alongside your code and pass the data in that way. You would just use something like data, err := ioutil.ReadFile("./data.json").

      1 Reply Last reply Reply Quote 1
      • scottalanmillerS
        scottalanmiller
        last edited by

        Poor APIs, everyone is always making fun of them.

        KellyK 1 Reply Last reply Reply Quote 3
        • jmooreJ
          jmoore
          last edited by

          Good info, thanks for posting.

          1 Reply Last reply Reply Quote 0
          • DanpD
            Danp
            last edited by

            I know that Postman provides the ability to perform this type of API testing. Has anyone tried this feature?

            scottalanmillerS stacksofplatesS 2 Replies Last reply Reply Quote 0
            • scottalanmillerS
              scottalanmiller @Danp
              last edited by

              @Danp said in Mocking API Endpoints with Go:

              I know that Postman

              The Postman Always Mocks Twice

              LOL, oh I'm full of them this week.

              1 Reply Last reply Reply Quote 0
              • KellyK
                Kelly @scottalanmiller
                last edited by

                @scottalanmiller said in Mocking API Endpoints with Go:

                Poor APIs, everyone is always making fun of them.

                Smock.jpg

                1 Reply Last reply Reply Quote 1
                • stacksofplatesS
                  stacksofplates @Danp
                  last edited by

                  @Danp said in Mocking API Endpoints with Go:

                  I know that Postman provides the ability to perform this type of API testing. Has anyone tried this feature?

                  I'm sure it does. The problem there though is if you're running in a pipeline you'd have to somehow get Postman downloaded, set up, and configured automatically to test against it. This is just in the standard library so you can unit test in your pipeline automatically.

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post