unrolled/render

panic: runtime error: invalid memory address or nil pointer dereference

peterbe opened this issue · 3 comments

I bet I'm doing something wrong being a noob and all but it seems like it should work:

:~/dev/GO/autocompeter (integration-test +%)$ go test -v
=== RUN TestHandleIndexReturnsWithStatusOK
--- FAIL: TestHandleIndexReturnsWithStatusOK (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xc8 pc=0xf2fba]

goroutine 5 [running]:
testing.func·006()
    /opt/boxen/homebrew/Cellar/go/1.4.1/libexec/src/testing/testing.go:441 +0x181
github.com/unrolled/render.(*Render).HTML(0x0, 0x823440, 0xc20800a0c0, 0xc8, 0x420370, 0x5, 0x0, 0x0, 0x0, 0x0, ...)
    /Users/peterbe/dev/GO/src/github.com/unrolled/render/render.go:249 +0x2a
_/Users/peterbe/dev/GO/autocompeter.IndexHandler(0x823440, 0xc20800a0c0, 0xc20801e5b0)
    /Users/peterbe/dev/GO/autocompeter/main.go:80 +0x8a
_/Users/peterbe/dev/GO/autocompeter.TestHandleIndexReturnsWithStatusOK(0xc20806a090)
    /Users/peterbe/dev/GO/autocompeter/main_test.go:13 +0x175
testing.tRunner(0xc20806a090, 0x752690)
    /opt/boxen/homebrew/Cellar/go/1.4.1/libexec/src/testing/testing.go:447 +0xbf
created by testing.RunTests
    /opt/boxen/homebrew/Cellar/go/1.4.1/libexec/src/testing/testing.go:555 +0xa8b

goroutine 1 [chan receive]:
testing.RunTests(0x5197e8, 0x752690, 0x1, 0x1, 0x75d301)
    /opt/boxen/homebrew/Cellar/go/1.4.1/libexec/src/testing/testing.go:556 +0xad6
testing.(*M).Run(0xc20802a0f0, 0x764780)
    /opt/boxen/homebrew/Cellar/go/1.4.1/libexec/src/testing/testing.go:485 +0x6c
main.main()
    _/Users/peterbe/dev/GO/autocompeter/_test/_testmain.go:52 +0x1d5
exit status 2
FAIL    _/Users/peterbe/dev/GO/autocompeter 0.021s

The test is extremely simple so far:

package main

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

func TestHandleIndexReturnsWithStatusOK(t *testing.T) {
    request, _ := http.NewRequest("GET", "/", nil)
    response := httptest.NewRecorder()

    IndexHandler(response, request)

    if response.Code != http.StatusOK {
        t.Fatalf("Non-expected status code%v:\n\tbody: %v", "200", response.Code)
    }
}

And in main.go there's a bunch of other stuff but the things that matter should be:

package main
import "github.com/unrolled/render"

func IndexHandler(w http.ResponseWriter, req *http.Request) {
    // this assumes there's a `templates/index.tmpl` file
    renderer.HTML(w, http.StatusOK, "index", nil)
}

var (
    renderer   *render.Render
    debug      = true
)

func main() {
        ...
    renderer = render.New(render.Options{
        IndentJSON:    debug,
        IsDevelopment: debug,
    })
        ...
        mux := mux.NewRouter()
    mux.HandleFunc("/", IndexHandler).Methods("GET", "HEAD")

    n := negroni.Classic()
    n.UseHandler(mux)
    n.Run(fmt.Sprintf(":%d", port))

}

Perhaps that's now how you're supposed to do testing of http handlers.

Hey Peter, you code looks good, but we need to initial the renderer variable before the handler can be called in your test.

func TestHandleIndexReturnsWithStatusOK(t *testing.T) {
    request, _ := http.NewRequest("GET", "/", nil)
    response := httptest.NewRecorder()

    // Since main isn't called for the test, we need to initialize the renderer var manually.
    renderer = render.New(render.Options{
        IndentJSON:    true,
        IsDevelopment: true,
    })

    IndexHandler(response, request)

    if response.Code != http.StatusOK {
        t.Fatalf("Non-expected status code%v:\n\tbody: %v", "200", response.Code)
    }
}

If you don't have to much customization, you could initial the renderer when you declare it:

var (
    debug    = true
    renderer = render.New(render.Options{
        IndentJSON:    debug,
        IsDevelopment: debug,
    })
)

With that, you wouldn't need to change your test code.

Let me know if this helps!

Yay! That works. Thing is, I want the debug to come in from a flag which I have in my main(). E.g. flag.BoolVar(&debug, "debug", false, "Debug mode")

So, instead I made a default renderer that does not use debug and then I override it in main() later. Now I get working tests and ability to use the debug when running the server on the command line.

var (
    debug      = true
    renderer  = render.New()
)

func main() {
    flag.BoolVar(&debug, "debug", false, "Debug mode")
    renderer = render.New(render.Options{
        IndentJSON:    debug,
        IsDevelopment: debug,
    })
}

👍