Skip to content

Commit

Permalink
added testing part
Browse files Browse the repository at this point in the history
  • Loading branch information
reinbach committed Apr 4, 2015
1 parent 677f731 commit 6948216
Show file tree
Hide file tree
Showing 21 changed files with 588 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -19,6 +19,7 @@ There is an overview of the these sections at [http://reinbach.com](http://reinb
### Parts

* [Templating](https://github.com/reinbach/golang-webapp-guide/tree/master/template)
* [Testing](https://github.com/reinbach/golang-webapp-guide/tree/master/test)

## License

Expand Down
12 changes: 12 additions & 0 deletions test/Makefile
@@ -0,0 +1,12 @@
run:
@go run server/main.go

test:
@for d in */ ; do \
go test -coverprofile=coverage.$${d:0:(-1)}.out ./$${d} ; \
done
@echo "mode: set" > coverage.out && cat coverage.*.out | sed '/mode: set/d' >> coverage.out
@rm coverage.*.out

cov:
@go tool cover -html coverage.out
33 changes: 33 additions & 0 deletions test/README.md
@@ -0,0 +1,33 @@
# Golang WebApp Guide

## Test

How we start to add testing to our web site. Golang provides plenty of tools in the standard library to handle this including coverage. We simple just create a `<filename>_test.go` file the represents the file we want to test. Then we can call `go test` on that file and our tests will run.

To make things a little simpler and to make automating easier, we move the `server.go` file into the `server` directory.

We create a Makefile to help with the running of various commands for us


### Run Tests

We can run individual tests with;

go test ./server/
go test ./template/

Or we can make use of the Makefile commands

make test # run the tests and create coverage files
make cov # to open up browser and view the coverage reports


### Run Site

We can now start the application with the old way;

go run ./server/main.go

Or with with the use of the Makefile

make run
27 changes: 27 additions & 0 deletions test/coverage.out
@@ -0,0 +1,27 @@
mode: set
github.com/reinbach/golang-webapp-guide/test/server/main.go:15.60,19.2 3 1
github.com/reinbach/golang-webapp-guide/test/server/main.go:21.61,25.2 3 0
github.com/reinbach/golang-webapp-guide/test/server/main.go:27.64,30.2 1 1
github.com/reinbach/golang-webapp-guide/test/server/main.go:32.13,39.2 5 0
github.com/reinbach/golang-webapp-guide/test/template/context.go:7.28,9.2 1 0
github.com/reinbach/golang-webapp-guide/test/template/context.go:11.54,12.21 1 1
github.com/reinbach/golang-webapp-guide/test/template/context.go:15.2,15.23 1 1
github.com/reinbach/golang-webapp-guide/test/template/context.go:12.21,14.3 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:16.36,18.16 2 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:22.2,22.54 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:25.2,25.22 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:28.2,28.10 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:18.16,20.3 1 0
github.com/reinbach/golang-webapp-guide/test/template/render.go:22.54,24.3 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:25.22,27.3 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:31.60,34.27 3 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:42.2,42.21 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:34.27,36.17 2 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:36.17,40.4 3 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:45.50,47.26 2 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:50.2,50.14 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:47.26,49.3 1 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:53.92,58.16 4 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:61.2,62.16 2 1
github.com/reinbach/golang-webapp-guide/test/template/render.go:58.16,60.3 1 0
github.com/reinbach/golang-webapp-guide/test/template/render.go:62.16,64.3 1 0
39 changes: 39 additions & 0 deletions test/server/main.go
@@ -0,0 +1,39 @@
package main

import (
"net/http"

"github.com/reinbach/golang-webapp-guide/test/template"
"github.com/zenazn/goji"
"github.com/zenazn/goji/web"
)

var (
templates = []string{"base.html"}
)

func Home(c web.C, w http.ResponseWriter, r *http.Request) {
ctx := template.NewContext()
ctx.Add("HomePage", true)
template.Render(c, w, r, append(templates, "home.html"), ctx)
}

func About(c web.C, w http.ResponseWriter, r *http.Request) {
ctx := template.NewContext()
ctx.Add("AboutPage", true)
template.Render(c, w, r, append(templates, "about.html"), ctx)
}

func NotFound(c web.C, w http.ResponseWriter, r *http.Request) {
template.Render(c, w, r, append(templates, "404.html"),
template.NewContext())
}

func main() {
http.HandleFunc(template.STATIC_URL, template.StaticHandler)
goji.Get("/", Home)
goji.Get("/about", About)
goji.NotFound(NotFound)

goji.Serve()
}
29 changes: 29 additions & 0 deletions test/server/main_test.go
@@ -0,0 +1,29 @@
package main

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

"github.com/zenazn/goji/web"
)

func TestHome(t *testing.T) {
r, _ := http.NewRequest("GET", "", nil)
w := httptest.NewRecorder()
c := web.C{}
Home(c, w, r)
if w.Code != http.StatusOK {
t.Errorf("200 expected, got %v instead", w.Code)
}
}

func TestNotFound(t *testing.T) {
r, _ := http.NewRequest("GET", "/404", nil)
w := httptest.NewRecorder()
c := web.C{}
NotFound(c, w, r)
if w.Code != http.StatusOK {
t.Errorf("200 expected, got %v instead", w.Code)
}
}
8 changes: 8 additions & 0 deletions test/template/constants.go
@@ -0,0 +1,8 @@
package template

const (
STATIC_URL string = "/static/"
STATIC_ROOT string = "static/"
TEMPLATE_DIR string = "html/"
PARENT_PACKAGE string = "test"
)
16 changes: 16 additions & 0 deletions test/template/context.go
@@ -0,0 +1,16 @@
package template

type Context struct {
Values map[string]interface{}
}

func NewContext() *Context {
return &Context{}
}

func (c *Context) Add(key string, value interface{}) {
if c.Values == nil {
c.Values = make(map[string]interface{}, 1)
}
c.Values[key] = value
}
14 changes: 14 additions & 0 deletions test/template/context_test.go
@@ -0,0 +1,14 @@
package template

import (
"testing"
)

func TestAdd(t *testing.T) {
e := map[string]string{"new": "value"}
c := Context{}
c.Add("new", "value")
if c.Values["new"] != e["new"] {
t.Errorf("Expected %v, got %v", e, c.Values)
}
}
4 changes: 4 additions & 0 deletions test/template/html/404.html
@@ -0,0 +1,4 @@
{{ define "content" }}
<h1>404 Error</h1>
<p class="lead">Hopefully it was intentional that we ended up here!</p>
{{ end }}
4 changes: 4 additions & 0 deletions test/template/html/about.html
@@ -0,0 +1,4 @@
{{ define "content" }}
<h1>About</h1>
<p class="lead">We're on the about page!</p>
{{ end }}
46 changes: 46 additions & 0 deletions test/template/html/base.html
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Golang Web Guide - Template</title>

<link href="{{ .Static }}css/bootstrap.min.css" rel="stylesheet">
<link href="{{ .Static }}css/main.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Golang Web Guide</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li {{ if .HomePage }}class="active"{{ end }}><a href="/">Home</a></li>
<li {{ if .AboutPage }}class="active"{{ end }}><a href="/about">About</a></li>
</ul>
</div>
</div>
</nav>

<div class="container">

<div class="content">
{{ template "content" . }}
</div>

</div>
</body>
</html>
4 changes: 4 additions & 0 deletions test/template/html/home.html
@@ -0,0 +1,4 @@
{{ define "content" }}
<h1>Golang Web Guide - Test</h1>
<p class="lead">In this section we start adding tests.</p>
{{ end }}
65 changes: 65 additions & 0 deletions test/template/render.go
@@ -0,0 +1,65 @@
package template

import (
"html/template"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"time"

"github.com/zenazn/goji/web"
)

func GetAbsDir(a ...string) string {
p, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
// this sucks need better way to get abs path to base package
if p[len(p)-len(PARENT_PACKAGE):] != PARENT_PACKAGE {
p = path.Dir(p)
}
for _, v := range a {
p = path.Join(p, v)
}
return p
}

func StaticHandler(w http.ResponseWriter, r *http.Request) {
static_file := r.URL.Path[len(STATIC_URL):]
static_dir := GetAbsDir("template", STATIC_ROOT)
if len(static_file) != 0 {
f, err := http.Dir(static_dir).Open(static_file)
if err == nil {
content := io.ReadSeeker(f)
http.ServeContent(w, r, static_file, time.Now(), content)
return
}
}
http.NotFound(w, r)
}

func UpdateTemplateList(tmpls []string) []string {
d := GetAbsDir("template", TEMPLATE_DIR)
for i, v := range tmpls {
tmpls[i] = filepath.Join(d, v)
}
return tmpls
}

func Render(c web.C, w http.ResponseWriter, r *http.Request, tmpls []string, ctx *Context) {
ctx.Add("Static", STATIC_URL)

tmpl_list := UpdateTemplateList(tmpls)
t, err := template.ParseFiles(tmpl_list...)
if err != nil {
log.Print("template parsing error: ", err)
}
err = t.Execute(w, ctx.Values)
if err != nil {
log.Print("template executing error: ", err)
}
}
44 changes: 44 additions & 0 deletions test/template/render_test.go
@@ -0,0 +1,44 @@
package template

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

"github.com/zenazn/goji/web"
)

func TestUpdateTemplateList(t *testing.T) {
l := UpdateTemplateList([]string{"test.html"})
if len(l) != 1 {
t.Errorf("Expected list of 1 templates, got %v", len(l))
}
}

func TestStaticHandlerValid(t *testing.T) {
p := path.Join(STATIC_URL, "css/main.css")
r, _ := http.NewRequest("GET", p, nil)
w := httptest.NewRecorder()
StaticHandler(w, r)
if w.Code != http.StatusOK {
t.Errorf("200 expected, got %v instead", w.Code)
}
}

func TestStaticHandlerInValid(t *testing.T) {
p := path.Join(STATIC_URL, "css/something.css")
r, _ := http.NewRequest("GET", p, nil)
w := httptest.NewRecorder()
StaticHandler(w, r)
if w.Code != http.StatusNotFound {
t.Errorf("404 expected, got %v instead", w.Code)
}
}

func TestRender(t *testing.T) {
c := web.C{}
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
Render(c, w, r, []string{"home.html"}, &Context{})
}
5 changes: 5 additions & 0 deletions test/template/static/css/bootstrap.min.css

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions test/template/static/css/main.css
@@ -0,0 +1,8 @@
body {
padding-top: 50px;
}

.content {
padding: 40px 15px;
text-align: center;
}
Binary file not shown.

0 comments on commit 6948216

Please sign in to comment.