Home > Blockchain >  How to assert that the request happened when mocking an API concurrently?
How to assert that the request happened when mocking an API concurrently?

Time:11-25

Followup question to: How do I guarantee that the request happened correctly when mocking an API?

main.go

package main

import (
    "net/http"
)

func SomeFeature(host, a string) {
    if a == "foo" {
        resp, err := http.Get(host   "/foo")
    }
    if a == "bar" {
        resp, err := http.Get(host   "/baz"))
    }

    // baz is missing, the test should error!
}

main_test.go

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestSomeFeature(t *testing.T) {

    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(200)
    }))

    testCases := []struct {
        name     string
        variable string
    }{
        {
            name:     "test 1",
            variable: "foo",
        },
        {
            name:     "test 2",
            variable: "bar",
        },
        {
            name:     "test 3",
            variable: "baz",
        },
    }
    for _, tc := range testCases {
        tc := tc
        t.Run(tc.name, func(t *testing.T) {
            t.Parallel()
            SomeFeature(server.URL, tc.variable)

            // assert that the http call happened somehow?
        })
    }
}
  • GO Playground: https://go.dev/play/p/EFanSSzgnbk
  • How to do I assert that each test case send a request to the mocked server?
  • How can I assert that a request wasn't sent?

All while keeping the tests parallel/concurrent?

CodePudding user response:

You could create a new server for each test case.


Or you can use channels, specifically a map of channels where the key is the test case's identifier, e.g.

getChans := map[string]chan struct{}{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    key := strings.Split(r.URL.Path, "/")[1] // extract channel key from path
    go func() { getChans[key] <- struct{}{} }()

    w.WriteHeader(200)
}))

Add a channel key field to the test case. This will be added to the host's URL and the handler will then extract the key, as demonstrated above, to get the correct channel. Also add a field to indicate whether http.Get should be called or not:

testCases := []struct {
    name      string
    chkey     string
    variable  string
    shouldGet bool
}{
    {
        name:      "test 1",
        chkey:     "key1"
        variable:  "foo",
        shouldGet: true,
    },
    // ...
}

Before running the test case add the test-case-specific channel to the map:

getChans[tc.chkey] = make(chan struct{})

Then use the channel key field in the test case as part of the host's URL path:

err := SomeFeature(server.URL "/" tc.chkey, tc.variable)
if err != nil {
    t.Error("SomeFeature should not error")
}

And to check whether or not http.Get was called use select with some acceptable timeout:

select {
case <-getChans[tc.chkey]:
    if !tc.shouldGet {
        t.Error(tc.name   " get called")
    }
case <-time.Tick(3 * time.Second):
    if tc.shouldGet {
        t.Error(tc.name   " get not called")
    }
}

https://go.dev/play/p/7By3ArkbI_o

  • Related