go - Unable to mock unexported properties in struct in golang - Stack Overflow

admin2025-04-20  0

I have to write test case for below function

func sendRequest(request *sdk.Request) (*sdk.Response, error) {
    response, err := request.Send()
    if err != nil {
        return nil, fmt.Errorf("cannot send request: %q", err)
    }
    return response, nil
}

I am trying to mock send() but *sdk.Request and *sdk.Response has unexported properties as below

type Request struct {
    transport http.RoundTripper
    method    string
    path      string
    query     url.Values
    header    http.Header
    body      []byte
}
type Response struct {
    status int
    header http.Header
    body   []byte
}

This is my test case

// Helper function to create a mock response
func createMockResponses(status string) *sdk.Response {
    response := &sdk.Response{}
    // Use reflection or public methods to set fields if available
    response.Status() // Assuming SetStatus is a public method
    return response
}

// Test cases
func TestSendRequest_Successs(t *testing.T) {
    // Create a properly initialized sdk.Request object
    request := &sdk.Request{
        
    }

    // Patch the Send method to return a mock response
    defer monkey.UnpatchAll() // Ensure the patch is removed after the test
    monkey.PatchInstanceMethod(
        reflect.TypeOf(request),
        "Send",
        func(*sdk.Request) (*sdk.Response, error) {
            return createMockResponses("OK"), nil
        },
    )

    // Call the function under test
    response, err := sendRequest(request)

    // Assertions
    assert.NoError(t, err)
    assert.Equal(t, "OK", response.Status()) // Assuming GetStatus is a public method
}

But getting error cannot refer to unexported field transport in struct literal of type sdk.Request and inside send function there is method below where it enters into error and gives error

response, err := r.transport.RoundTrip(request)
    if err != nil {
        return
    }

Error


--- FAIL: TestSendRequest_Successs (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x491ad80]

goroutine 25 [running]:
testing.tRunner.func1.2({0x4e8e560, 0x7690490})
    /usr/local/go/src/testing/testing.go:1632 +0x230
testing.tRunner.func1()
    /usr/local/go/src/testing/testing.go:1635 +0x35e
panic({0x4e8e560?, 0x7690490?})
    /usr/local/go/src/runtime/panic.go:785 +0x132
github/openshift-online/ocm-sdk-go.(*Request).SendContext(0xc0001dff00, {0x5a27bd0, 0x7896960})
    /home/amiupadh/go/pkg/mod/github/openshift-online/[email protected]/request.go:122 +0x5c0
github/openshift-online/ocm-sdk-go.(*Request).Send(...)
    /home/amiupadh/go/pkg/mod/github/openshift-online/[email protected]/request.go:95
github/openshift/osdctl/cmd/.sendRequest(0x5a44750?)
    /home/amiupadh/go/src/osdctl/cmd//common.go:42 +0x25
github/openshift/osdctl/cmd/.TestSendRequest_Successs(0xc00079a820)
    /home/amiupadh/go/src/osdctl/cmd//common_test.go:365 +0xc5
testing.tRunner(0xc00079a820, 0x5563370)
    /usr/local/go/src/testing/testing.go:1690 +0xf4
created by testing.(*T).Run in goroutine 1
    /usr/local/go/src/testing/testing.go:1743 +0x390
FAIL    github/openshift/osdctl/cmd/ 0.019s

Reference : .go

I have to write test case for below function

func sendRequest(request *sdk.Request) (*sdk.Response, error) {
    response, err := request.Send()
    if err != nil {
        return nil, fmt.Errorf("cannot send request: %q", err)
    }
    return response, nil
}

I am trying to mock send() but *sdk.Request and *sdk.Response has unexported properties as below

type Request struct {
    transport http.RoundTripper
    method    string
    path      string
    query     url.Values
    header    http.Header
    body      []byte
}
type Response struct {
    status int
    header http.Header
    body   []byte
}

This is my test case

// Helper function to create a mock response
func createMockResponses(status string) *sdk.Response {
    response := &sdk.Response{}
    // Use reflection or public methods to set fields if available
    response.Status() // Assuming SetStatus is a public method
    return response
}

// Test cases
func TestSendRequest_Successs(t *testing.T) {
    // Create a properly initialized sdk.Request object
    request := &sdk.Request{
        
    }

    // Patch the Send method to return a mock response
    defer monkey.UnpatchAll() // Ensure the patch is removed after the test
    monkey.PatchInstanceMethod(
        reflect.TypeOf(request),
        "Send",
        func(*sdk.Request) (*sdk.Response, error) {
            return createMockResponses("OK"), nil
        },
    )

    // Call the function under test
    response, err := sendRequest(request)

    // Assertions
    assert.NoError(t, err)
    assert.Equal(t, "OK", response.Status()) // Assuming GetStatus is a public method
}

But getting error cannot refer to unexported field transport in struct literal of type sdk.Request and inside send function there is method below where it enters into error and gives error

response, err := r.transport.RoundTrip(request)
    if err != nil {
        return
    }

Error


--- FAIL: TestSendRequest_Successs (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x491ad80]

goroutine 25 [running]:
testing.tRunner.func1.2({0x4e8e560, 0x7690490})
    /usr/local/go/src/testing/testing.go:1632 +0x230
testing.tRunner.func1()
    /usr/local/go/src/testing/testing.go:1635 +0x35e
panic({0x4e8e560?, 0x7690490?})
    /usr/local/go/src/runtime/panic.go:785 +0x132
github/openshift-online/ocm-sdk-go.(*Request).SendContext(0xc0001dff00, {0x5a27bd0, 0x7896960})
    /home/amiupadh/go/pkg/mod/github/openshift-online/[email protected]/request.go:122 +0x5c0
github/openshift-online/ocm-sdk-go.(*Request).Send(...)
    /home/amiupadh/go/pkg/mod/github/openshift-online/[email protected]/request.go:95
github/openshift/osdctl/cmd/.sendRequest(0x5a44750?)
    /home/amiupadh/go/src/osdctl/cmd//common.go:42 +0x25
github/openshift/osdctl/cmd/.TestSendRequest_Successs(0xc00079a820)
    /home/amiupadh/go/src/osdctl/cmd//common_test.go:365 +0xc5
testing.tRunner(0xc00079a820, 0x5563370)
    /usr/local/go/src/testing/testing.go:1690 +0xf4
created by testing.(*T).Run in goroutine 1
    /usr/local/go/src/testing/testing.go:1743 +0x390
FAIL    github/openshift/osdctl/cmd/ 0.019s

Reference : https://github/openshift/osdctl/blob/master/cmd//common.go

Share Improve this question asked Mar 3 at 4:07 AupaAupa 3851 gold badge3 silver badges15 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 4

as we have non exported members for Request and Response structs, we better use methods provided by library to write tests related to them instead of replacing their pointer receiver methods using monkey library, by the way I think your send method is a wrapper to Request.Send() and thus writing test for such wrapper is not necessary and actual send method should write related test I suggest to think more about the aim of your test by asking yourself: are we going to test our send method with all possible inputs(actually nil or not nil are sufficient tests here) or we're going to test Send method of sdk? , any way here is working test:

package 

import (
    "encoding/json"
    "net/http"
    "testing"

    "github/onsi/ginkgo/v2/dsl/core" // nolint
    "github/onsi/gomega"             // nolint
    "github/onsi/gomega/ghttp"
    sdk "github/openshift-online/ocm-sdk-go"
    "github/openshift-online/ocm-sdk-go/logging"
    . "github/openshift-online/ocm-sdk-go/testing" // nolint
)

var OkResp = Resp{"OK"}

type Resp struct {
    Result string `json:"result"`
}

// Test cases
func TestSendRequest(t *testing.T) {
    var apiServer *ghttp.Server
    var connection *sdk.Connection
    var logger logging.Logger

    var okRespBytes []byte

    core.BeforeEach(func() {
        var err error

        okRespBytes, err = json.Marshal(OkResp)
        gomega.Expect(err).ToNot(gomega.HaveOccurred())

        // Create the API server:
        apiServer = MakeTCPServer()

        // Create the connection:
        connection, err = sdk.NewConnectionBuilder().
            Logger(logger).
            URL(apiServer.URL()).
            RetryLimit(0).
            Build()
        gomega.Expect(err).ToNot(gomega.HaveOccurred())
    })

    core.Describe("Get", func() {
        core.It("Sends path", func() {
            // Configure the server:
            apiServer.AppendHandlers(
                ghttp.CombineHandlers(
                    ghttp.VerifyRequest(http.MethodGet, "/mypath"),
                    RespondWithJSON(http.StatusOK, string(okRespBytes)),
                ),
            )

            // Build the request:
            request := connection.Get().
                Path("/mypath")

            // Send the request:
            resp, err := sendRequest(request)
            gomega.Expect(err).ToNot(gomega.HaveOccurred())
            gomega.Expect(resp).Should(gomega.MatchJSON(OkResp))
        })
    })
}

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745110155a285533.html

最新回复(0)