diff --git a/models/action_test.go b/models/action_test.go
index 2bcd22cb22c..3f29e1556c2 100644
--- a/models/action_test.go
+++ b/models/action_test.go
@@ -297,7 +297,7 @@ func TestCommitRepoAction(t *testing.T) {
 	}
 
 	for _, s := range samples {
-		prepareTestEnv(t)
+		PrepareTestEnv(t)
 
 		user := AssertExistsAndLoadBean(t, &User{ID: s.userID}).(*User)
 		repo := AssertExistsAndLoadBean(t, &Repository{ID: s.repositoryID, OwnerID: user.ID}).(*Repository)
diff --git a/models/main_test.go b/models/main_test.go
index 451b5e4b216..11c175ef88c 100644
--- a/models/main_test.go
+++ b/models/main_test.go
@@ -1,14 +1,8 @@
 package models
 
 import (
-	"fmt"
-	"os"
-	"path/filepath"
 	"testing"
 
-	"code.gitea.io/gitea/modules/setting"
-
-	_ "github.com/mattn/go-sqlite3" // for the test engine
 	"github.com/stretchr/testify/assert"
 )
 
@@ -19,17 +13,5 @@ func TestFixturesAreConsistent(t *testing.T) {
 }
 
 func TestMain(m *testing.M) {
-	if err := CreateTestEngine("fixtures/"); err != nil {
-		fmt.Printf("Error creating test engine: %v\n", err)
-		os.Exit(1)
-	}
-
-	setting.AppURL = "https://try.gitea.io/"
-	setting.RunUser = "runuser"
-	setting.SSH.Port = 3000
-	setting.SSH.Domain = "try.gitea.io"
-	setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
-	setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
-
-	os.Exit(m.Run())
+	MainTest(m, "..")
 }
diff --git a/models/unit_tests.go b/models/unit_tests.go
index 7f8e86fb8fa..cf7c3e4f928 100644
--- a/models/unit_tests.go
+++ b/models/unit_tests.go
@@ -5,7 +5,9 @@
 package models
 
 import (
+	"fmt"
 	"os"
+	"path/filepath"
 	"testing"
 
 	"code.gitea.io/gitea/modules/setting"
@@ -13,6 +15,7 @@ import (
 	"github.com/Unknwon/com"
 	"github.com/go-xorm/core"
 	"github.com/go-xorm/xorm"
+	_ "github.com/mattn/go-sqlite3" // for the test engine
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/testfixtures.v2"
 )
@@ -20,9 +23,30 @@ import (
 // NonexistentID an ID that will never exist
 const NonexistentID = 9223372036854775807
 
-// CreateTestEngine create in-memory sqlite database for unit tests
-// Any package that calls this must import github.com/mattn/go-sqlite3
-func CreateTestEngine(fixturesDir string) error {
+// giteaRoot a path to the gitea root
+var giteaRoot string
+
+// MainTest a reusable TestMain(..) function for unit tests that need to use a
+// test database. Creates the test database, and sets necessary settings.
+func MainTest(m *testing.M, pathToGiteaRoot string) {
+	giteaRoot = pathToGiteaRoot
+	fixturesDir := filepath.Join(pathToGiteaRoot, "models", "fixtures")
+	if err := createTestEngine(fixturesDir); err != nil {
+		fmt.Fprintf(os.Stderr, "Error creating test engine: %v\n", err)
+		os.Exit(1)
+	}
+
+	setting.AppURL = "https://try.gitea.io/"
+	setting.RunUser = "runuser"
+	setting.SSH.Port = 3000
+	setting.SSH.Domain = "try.gitea.io"
+	setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
+	setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
+
+	os.Exit(m.Run())
+}
+
+func createTestEngine(fixturesDir string) error {
 	var err error
 	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
 	if err != nil {
@@ -45,10 +69,13 @@ func PrepareTestDatabase() error {
 	return LoadFixtures()
 }
 
-func prepareTestEnv(t testing.TB) {
+// PrepareTestEnv prepares the environment for unit tests. Can only be called
+// by tests that use the above MainTest(..) function.
+func PrepareTestEnv(t testing.TB) {
 	assert.NoError(t, PrepareTestDatabase())
 	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
-	assert.NoError(t, com.CopyDir("../integrations/gitea-repositories-meta", setting.RepoRootPath))
+	metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
+	assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
 }
 
 type testCond struct {
diff --git a/models/wiki_test.go b/models/wiki_test.go
index e80c6cbb8db..c816a175588 100644
--- a/models/wiki_test.go
+++ b/models/wiki_test.go
@@ -74,6 +74,14 @@ func TestWikiFilenameToName(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, test.Expected, name)
 	}
+	for _, badFilename := range []string{
+		"nofileextension",
+		"wrongfileextension.txt",
+		"badescaping%%.md",
+	} {
+		_, err := WikiFilenameToName(badFilename)
+		assert.Error(t, err)
+	}
 }
 
 func TestWikiNameToFilenameToName(t *testing.T) {
@@ -115,7 +123,7 @@ func TestRepository_WikiPath(t *testing.T) {
 }
 
 func TestRepository_HasWiki(t *testing.T) {
-	prepareTestEnv(t)
+	PrepareTestEnv(t)
 	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 	assert.True(t, repo1.HasWiki())
 	repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
@@ -123,7 +131,7 @@ func TestRepository_HasWiki(t *testing.T) {
 }
 
 func TestRepository_InitWiki(t *testing.T) {
-	prepareTestEnv(t)
+	PrepareTestEnv(t)
 	// repo1 already has a wiki
 	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 	assert.NoError(t, repo1.InitWiki())
@@ -135,7 +143,7 @@ func TestRepository_InitWiki(t *testing.T) {
 }
 
 func TestRepository_LocalWikiPath(t *testing.T) {
-	prepareTestEnv(t)
+	PrepareTestEnv(t)
 	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 	expected := filepath.Join(setting.AppDataPath, "tmp/local-wiki/1")
 	assert.Equal(t, expected, repo.LocalWikiPath())
@@ -150,11 +158,23 @@ func TestRepository_AddWikiPage(t *testing.T) {
 		"Another page",
 		"Here's a <tag> and a/slash",
 	} {
-		prepareTestEnv(t)
+		PrepareTestEnv(t)
 		assert.NoError(t, repo.AddWikiPage(doer, wikiName, wikiContent, commitMsg))
 		expectedPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(wikiName))
 		assert.True(t, com.IsExist(expectedPath))
 	}
+
+	// test for already-existing wiki name
+	PrepareTestEnv(t)
+	err := repo.AddWikiPage(doer, "Home", wikiContent, commitMsg)
+	assert.Error(t, err)
+	assert.True(t, IsErrWikiAlreadyExist(err))
+
+	// test for reserved wiki name
+	PrepareTestEnv(t)
+	err = repo.AddWikiPage(doer, "_edit", wikiContent, commitMsg)
+	assert.Error(t, err)
+	assert.True(t, IsErrWikiReservedName(err))
 }
 
 func TestRepository_EditWikiPage(t *testing.T) {
@@ -163,20 +183,23 @@ func TestRepository_EditWikiPage(t *testing.T) {
 	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 	doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
 	for _, newWikiName := range []string{
+		"Home", // same name as before
 		"New home",
 		"New/name/with/slashes",
 	} {
-		prepareTestEnv(t)
+		PrepareTestEnv(t)
 		assert.NoError(t, repo.EditWikiPage(doer, "Home", newWikiName, newWikiContent, commitMsg))
 		newPath := path.Join(repo.LocalWikiPath(), WikiNameToFilename(newWikiName))
 		assert.True(t, com.IsExist(newPath))
-		oldPath := path.Join(repo.LocalWikiPath(), "Home.md")
-		assert.False(t, com.IsExist(oldPath))
+		if newWikiName != "Home" {
+			oldPath := path.Join(repo.LocalWikiPath(), "Home.md")
+			assert.False(t, com.IsExist(oldPath))
+		}
 	}
 }
 
 func TestRepository_DeleteWikiPage(t *testing.T) {
-	prepareTestEnv(t)
+	PrepareTestEnv(t)
 	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 	doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
 	assert.NoError(t, repo.DeleteWikiPage(doer, "Home"))
diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go
index 6bb7ffe9876..da15b64395d 100644
--- a/modules/test/context_tests.go
+++ b/modules/test/context_tests.go
@@ -9,33 +9,46 @@ import (
 	"net/url"
 	"testing"
 
+	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
 
+	"github.com/go-macaron/session"
+	_ "github.com/mattn/go-sqlite3" // for the test engine
 	"github.com/stretchr/testify/assert"
-	macaron "gopkg.in/macaron.v1"
+	"gopkg.in/macaron.v1"
 )
 
 // MockContext mock context for unit tests
-func MockContext(t *testing.T) *context.Context {
-	var macaronContext *macaron.Context
-	mac := macaron.New()
-	mac.Get("*/", func(ctx *macaron.Context) {
-		macaronContext = ctx
-	})
-	req, err := http.NewRequest("GET", "star", nil)
-	assert.NoError(t, err)
-	req.Form = url.Values{}
-	mac.ServeHTTP(&mockResponseWriter{}, req)
-	assert.NotNil(t, macaronContext)
-	assert.EqualValues(t, req, macaronContext.Req.Request)
+func MockContext(t *testing.T, path string) *context.Context {
+	var macaronContext macaron.Context
+	macaronContext.ReplaceAllParams(macaron.Params{})
 	macaronContext.Locale = &mockLocale{}
+	requestURL, err := url.Parse(path)
+	assert.NoError(t, err)
+	macaronContext.Req = macaron.Request{Request: &http.Request{
+		URL:  requestURL,
+		Form: url.Values{},
+	}}
 	macaronContext.Resp = &mockResponseWriter{}
 	macaronContext.Render = &mockRender{ResponseWriter: macaronContext.Resp}
+	macaronContext.Data = map[string]interface{}{}
 	return &context.Context{
-		Context: macaronContext,
+		Context: &macaronContext,
+		Flash:   &session.Flash{},
 	}
 }
 
+// LoadRepo load a repo into a test context.
+func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
+	ctx.Repo = &context.Repository{}
+	ctx.Repo.Repository = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
+}
+
+// LoadUser load a user into a test context.
+func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
+	ctx.User = models.AssertExistsAndLoadBean(t, &models.User{ID: userID}).(*models.User)
+}
+
 type mockLocale struct{}
 
 func (l mockLocale) Language() string {
@@ -43,7 +56,7 @@ func (l mockLocale) Language() string {
 }
 
 func (l mockLocale) Tr(s string, _ ...interface{}) string {
-	return "test translation"
+	return s
 }
 
 type mockResponseWriter struct {
@@ -91,7 +104,8 @@ func (tr *mockRender) SetResponseWriter(rw http.ResponseWriter) {
 	tr.ResponseWriter = rw
 }
 
-func (tr *mockRender) JSON(int, interface{}) {
+func (tr *mockRender) JSON(status int, _ interface{}) {
+	tr.Status(status)
 }
 
 func (tr *mockRender) JSONString(interface{}) (string, error) {
diff --git a/routers/repo/main_test.go b/routers/repo/main_test.go
new file mode 100644
index 00000000000..04bbeeb2117
--- /dev/null
+++ b/routers/repo/main_test.go
@@ -0,0 +1,16 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+)
+
+func TestMain(m *testing.M) {
+	models.MainTest(m, filepath.Join("..", ".."))
+}
diff --git a/routers/repo/wiki_test.go b/routers/repo/wiki_test.go
new file mode 100644
index 00000000000..9b1e369fa2e
--- /dev/null
+++ b/routers/repo/wiki_test.go
@@ -0,0 +1,171 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"io/ioutil"
+	"net/http"
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/auth"
+	"code.gitea.io/gitea/modules/test"
+
+	"github.com/Unknwon/com"
+	"github.com/stretchr/testify/assert"
+)
+
+const content = "Wiki contents for unit tests"
+const message = "Wiki commit message for unit tests"
+
+func wikiPath(repo *models.Repository, wikiName string) string {
+	return filepath.Join(repo.LocalWikiPath(), models.WikiNameToFilename(wikiName))
+}
+
+func wikiContent(t *testing.T, repo *models.Repository, wikiName string) string {
+	bytes, err := ioutil.ReadFile(wikiPath(repo, wikiName))
+	assert.NoError(t, err)
+	return string(bytes)
+}
+
+func assertWikiExists(t *testing.T, repo *models.Repository, wikiName string) {
+	assert.True(t, com.IsExist(wikiPath(repo, wikiName)))
+}
+
+func assertWikiNotExists(t *testing.T, repo *models.Repository, wikiName string) {
+	assert.False(t, com.IsExist(wikiPath(repo, wikiName)))
+}
+
+func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
+	pageMetas, ok := metas.([]PageMeta)
+	if !assert.True(t, ok) {
+		return
+	}
+	if !assert.EqualValues(t, len(expectedNames), len(pageMetas)) {
+		return
+	}
+	for i, pageMeta := range pageMetas {
+		assert.EqualValues(t, expectedNames[i], pageMeta.Name)
+	}
+}
+
+func TestWiki(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
+	ctx.SetParams(":page", "Home")
+	test.LoadRepo(t, ctx, 1)
+	Wiki(ctx)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assert.EqualValues(t, "Home", ctx.Data["Title"])
+	assertPagesMetas(t, []string{"Home"}, ctx.Data["Pages"])
+}
+
+func TestWikiPages(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
+	test.LoadRepo(t, ctx, 1)
+	WikiPages(ctx)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assertPagesMetas(t, []string{"Home"}, ctx.Data["Pages"])
+}
+
+func TestNewWiki(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+	test.LoadUser(t, ctx, 2)
+	test.LoadRepo(t, ctx, 1)
+	NewWiki(ctx)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assert.EqualValues(t, ctx.Tr("repo.wiki.new_page"), ctx.Data["Title"])
+}
+
+func TestNewWikiPost(t *testing.T) {
+	for _, title := range []string{
+		"New page",
+		"&&&&",
+	} {
+		models.PrepareTestEnv(t)
+
+		ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+		test.LoadUser(t, ctx, 2)
+		test.LoadRepo(t, ctx, 1)
+		NewWikiPost(ctx, auth.NewWikiForm{
+			Title:   title,
+			Content: content,
+			Message: message,
+		})
+		assert.EqualValues(t, http.StatusFound, ctx.Resp.Status())
+		assertWikiExists(t, ctx.Repo.Repository, title)
+		assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content)
+	}
+}
+
+func TestNewWikiPost_ReservedName(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+	test.LoadUser(t, ctx, 2)
+	test.LoadRepo(t, ctx, 1)
+	NewWikiPost(ctx, auth.NewWikiForm{
+		Title:   "_edit",
+		Content: content,
+		Message: message,
+	})
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg)
+	assertWikiNotExists(t, ctx.Repo.Repository, "_edit")
+}
+
+func TestEditWiki(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/_edit/Home")
+	ctx.SetParams(":page", "Home")
+	test.LoadUser(t, ctx, 2)
+	test.LoadRepo(t, ctx, 1)
+	EditWiki(ctx)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assert.EqualValues(t, "Home", ctx.Data["Title"])
+	assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"])
+}
+
+func TestEditWikiPost(t *testing.T) {
+	for _, title := range []string{
+		"Home",
+		"New/<page>",
+	} {
+		models.PrepareTestEnv(t)
+		ctx := test.MockContext(t, "user2/repo1/wiki/_new/Home")
+		ctx.SetParams(":page", "Home")
+		test.LoadUser(t, ctx, 2)
+		test.LoadRepo(t, ctx, 1)
+		EditWikiPost(ctx, auth.NewWikiForm{
+			Title:   title,
+			Content: content,
+			Message: message,
+		})
+		assert.EqualValues(t, http.StatusFound, ctx.Resp.Status())
+		assertWikiExists(t, ctx.Repo.Repository, title)
+		assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content)
+		if title != "Home" {
+			assertWikiNotExists(t, ctx.Repo.Repository, "Home")
+		}
+	}
+}
+
+func TestDeleteWikiPagePost(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1/wiki/Home/delete")
+	test.LoadUser(t, ctx, 2)
+	test.LoadRepo(t, ctx, 1)
+	DeleteWikiPagePost(ctx)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	assertWikiNotExists(t, ctx.Repo.Repository, "Home")
+}
diff --git a/routers/user/home_test.go b/routers/user/home_test.go
index beca9361745..a9b146b7627 100644
--- a/routers/user/home_test.go
+++ b/routers/user/home_test.go
@@ -19,8 +19,8 @@ func TestIssues(t *testing.T) {
 	setting.UI.IssuePagingNum = 1
 	assert.NoError(t, models.LoadFixtures())
 
-	ctx := test.MockContext(t)
-	ctx.User = models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	ctx := test.MockContext(t, "issues")
+	test.LoadUser(t, ctx, 2)
 	ctx.SetParams(":type", "issues")
 	ctx.Req.Form.Set("state", "closed")
 	Issues(ctx)
diff --git a/routers/user/main_test.go b/routers/user/main_test.go
index 83c0c65474c..ed0724dc773 100644
--- a/routers/user/main_test.go
+++ b/routers/user/main_test.go
@@ -5,29 +5,12 @@
 package user
 
 import (
-	"fmt"
-	"os"
 	"path/filepath"
 	"testing"
 
 	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/modules/setting"
-
-	_ "github.com/mattn/go-sqlite3" // for the test engine
 )
 
 func TestMain(m *testing.M) {
-	if err := models.CreateTestEngine("../../models/fixtures/"); err != nil {
-		fmt.Printf("Error creating test engine: %v\n", err)
-		os.Exit(1)
-	}
-
-	setting.AppURL = "https://try.gitea.io/"
-	setting.RunUser = "runuser"
-	setting.SSH.Port = 3000
-	setting.SSH.Domain = "try.gitea.io"
-	setting.RepoRootPath = filepath.Join(os.TempDir(), "repos")
-	setting.AppDataPath = filepath.Join(os.TempDir(), "appdata")
-
-	os.Exit(m.Run())
+	models.MainTest(m, filepath.Join("..", ".."))
 }