3

I'm using appengine and golang to develop simple RESTful APIs. The code works fine when I start service using goapp serve, and I started writing the unit test functions to test the API endpoints, and I'm struck here with the panic error appengine: NewContext passed an unknown http.Request. I'm getting this error when i run goapp test.

It looks like for some reason, i'm not able to pass the request that I created and pass it to appengine.NewContext()

Below is the snippet of the code..

body := strings.NewReader("")

request, err := http.NewRequest("GET", "endpoint url", body) //inst.NewRequest("GET", goalUrl, body) //
if err != nil {
    t.Error(err)
}
t.Log(request)

c := appengine.NewContext(request) // ERROR: appengine: NewContext passed an unknown http.Request

I have created a simple reproducible code. Can you help me with this? or Does anyone have their golang API project on appengine, and have unit test functions to test the endpoints, i'll like to take a look at their code...

Here is the gitlab issue that I posted, which has all the required details of the issue along with examples and detailed error message. Thank for all your help.

srini
  • 1,110
  • 3
  • 11
  • 20

2 Answers2

2

Apologies for the delay in response. I was working on the solution myself

Here is how I solved the go appengine unit tests issue: the complete solution code can be found on this github repo branch.

In the *_test.go file, I have used the following.

  • httptest.NewServer(..) to create a new instance of the test server. also used to capture the base url of the service which is used in preparing the request object.
  • aetest.NewContext() to create a new context for testing purpose
  • http.NewRequest(..) to create the new request
  • gorilla's context.Set(..) to assign key ("Context"), value (context created in above step) to the request created above
  • httptest.NewRecorder() a new recorder to save the results
  • http.Handler.ServeHTTP(..) passing the recorded and request. used to make the API request

For each API handler code, instead of directly creating the new context using appengine.NewContext, I have code described in the below sudocode

using the gorilla's context.GetOk(..), check 
    IF the received request object has the key "Context"
    THEN using value of that key as the context
    ELSE derive context using appengine.NewContext(r)

I have wrapped the reusable functionality into a separate library called aeunittest and used it in my code.

With this setup, i'm, able to run the goapp test to fire the unit tests. see the solved code for complete details of the solution.

Here is the blogpost that helped me find this solution. Thanks a lot Mark. TESTING GO HTTP HANDLERS IN GOOGLE APP ENGINE WITH MUX AND HIGHER ORDER FUNCTIONS.

srini
  • 1,110
  • 3
  • 11
  • 20
1

NewContext derives a context from in flight HTTP requests, i.e. ones that have been registered with it.

Since you are creating a new request, unknown to the internal appengine package, it's panicking.

That package also provides a RegisterTestRequest function, but it's not mentioned in the main documentation so YMMV. I have no experience with this.

nothingmuch
  • 1,456
  • 9
  • 10
  • thank you, but I see a note in the document saying "It should only be used by aetest package". I believe it might not be a risk. Is there any other solution? – srini Oct 31 '16 at 17:00
  • Why do you need to call `NewContext` to begin with? Perhaps a better approach would be to completely separate the appengine specific parts of the logic, and make the application logic testable independent of appengine specific state? – nothingmuch Nov 08 '16 at 03:01
  • the Context is required for the appengine datastore operations like put, get, so it is required in my case. – srini Nov 09 '16 at 23:00
  • What I mean is that you could abstract those APIs into interfaces and provide a mock context and a mock datstore to boot (or just a datastore, if you can hide the context), compatible with but independent of the appengine package – nothingmuch Nov 09 '16 at 23:03
  • i'm not sure how to do that. just now, I have posted a solution that I found working for me. I believe it is different than what you are talking about. However, I'm interested to learn your solution too. – srini Nov 09 '16 at 23:52