diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index f9e90f3d5b27fd4e57fb02c001b18a8aed0e5cd8..7c224f32a8bd0d651058b789dbfa7b8f857349aa 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -7,12 +7,18 @@ jobs:
       - name: Install Go
         uses: actions/setup-go@v2
         with:
-          go-version: '1.16.x'
+          go-version: '1.17.x'
       - name: Checkout code
         uses: actions/checkout@v2
-      - name: Install test dependencies
-        run: sudo apt-get install netcat-openbsd
+      - name: Install dependencies
+        run: sudo apt update && sudo apt install -y python3-pip
+      - name: Install mkdocs
+        run: sudo pip3 install mkdocs mkdocs-material mkdocs-minify-plugin
+      - name: Build docs
+        run: make docs
       - name: Run tests, formatting, vetting and linting
         run: make check
-      - name: Run and upload coverage to codecov.io
-        run: make coverage coverage-upload
\ No newline at end of file
+      - name: Run coverage
+        run: make coverage
+      # - name: Upload coverage to codecov.io
+      #  run: make coverage-upload
diff --git a/Makefile b/Makefile
index b4166d28d313a0b2e06651d04260443a405ef83c..142db10fe9211755b2237a1a64c3a0f843066d90 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,10 @@ help:
 	@echo "  make install-lint                - Install golint"
 
 
+# Documentation
+docs: .PHONY
+	mkdocs build
+
 # Test/check targets
 
 check: test fmt-check vet lint staticcheck
@@ -88,9 +92,6 @@ staticcheck: .PHONY
 
 # Building targets
 
-docs: .PHONY
-	mkdocs build
-
 build-deps: docs
 	which arm-linux-gnueabi-gcc || { echo "ERROR: ARMv6/v7 cross compiler not installed. On Ubuntu, run: apt install gcc-arm-linux-gnueabi"; exit 1; }
 	which aarch64-linux-gnu-gcc || { echo "ERROR: ARM64 cross compiler not installed. On Ubuntu, run: apt install gcc-aarch64-linux-gnu"; exit 1; }
diff --git a/config/config_test.go b/config/config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d7282511a5e6d3bf9ce9deeb5f9dd02ed3e0b73f
--- /dev/null
+++ b/config/config_test.go
@@ -0,0 +1,12 @@
+package config_test
+
+import (
+	"github.com/stretchr/testify/assert"
+	"heckel.io/ntfy/config"
+	"testing"
+)
+
+func TestConfig_New(t *testing.T) {
+	c := config.New(":1234")
+	assert.Equal(t, ":1234", c.ListenHTTP)
+}
diff --git a/server/server_test.go b/server/server_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..eac143741dc0009ad74e49fa98e2b832d7564244
--- /dev/null
+++ b/server/server_test.go
@@ -0,0 +1,36 @@
+package server
+
+import (
+	"encoding/json"
+	"github.com/stretchr/testify/assert"
+	"heckel.io/ntfy/config"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestServer_Publish(t *testing.T) {
+	s := newTestServer(t, newTestConfig())
+
+	rr := httptest.NewRecorder()
+	req, _ := http.NewRequest("PUT", "/mytopic", strings.NewReader("my message"))
+	s.handle(rr, req)
+
+	var m message
+	assert.Nil(t, json.NewDecoder(rr.Body).Decode(&m))
+	assert.NotEmpty(t, m.ID)
+	assert.Equal(t, "my message", m.Message)
+}
+
+func newTestConfig() *config.Config {
+	return config.New(":80")
+}
+
+func newTestServer(t *testing.T, config *config.Config) *Server {
+	server, err := New(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return server
+}