diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a90cac3a2c6d69240ea6d2c543fbd1a315d25478..1850801b354fca69941bf4e1a2257b3976aed9bf 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -8,13 +8,6 @@
     sha: v0.5.4
     hooks:
     -   id: shell-lint
-        files: &shellscripts ssl-ca
+        files: &shellscripts ^ssl-ca$
     -   id: shellcheck
         files: *shellscripts
--   repo: local
-    hooks:
-    -   id: test
-        name: make test
-        entry: make test
-        language: system
-        files: Makefile|ssl-ca
diff --git a/.travis.yml b/.travis.yml
index e3a68d0d63f88e8c3fae720b253e0d0209d30c08..0ce32bf57299f9607f396a4f3c3cce1180516ccc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,8 @@ cache:
   - pip
   - directories:
       - $HOME/.pre-commit
+      - $HOME/.cabal
+      - $HOME/.ghc
 
 addons:
   apt:
@@ -16,12 +18,20 @@ addons:
       - openssl
       - curl
       - build-essential
+      - cabal-install
+      - ghc
+
+env:
+  PATH: $PATH:$HOME/.cabal/bin
 
 install:
-  - pip install pre_commit
+  - cabal update && cabal install shellcheck
+  - pip install pre_commit | cat
 
 script:
-  - pre-commit run --all-files
+  - make pre-commit
+  - make test
 
 notifications:
+  on_failure: never
   email: false
diff --git a/Makefile b/Makefile
index 39e170bc8fdf9b32e6ae49b3b2680cb6046c8486..45f83c41493ae18c69aa7166b294ced13589281e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,34 +1,15 @@
-.PHONY: install test clean
+.PHONY: install test clean pre-commit
 
 install:
 	cp ssl-ca /usr/local/bin/ssl-ca
 	chmod 755 /usr/local/bin/ssl-ca
 
-test:
-	./ssl-ca init
-	test "$$(openssl rsa -noout -check -in CA.key)" = "RSA key ok"
-	test "$$(openssl verify -CAfile CA.crt CA.crt)" = "CA.crt: OK"
-	./ssl-ca gen www
-	test "$$(openssl rsa -noout -check -in keys/www)" = "RSA key ok"
-	openssl verify -CAfile CA.crt certs/www
-	test "$$(openssl x509 -in certs/www -issuer -noout)" = "issuer= /CN=ssl-ca"
-	test "$$(openssl x509 -in certs/www -subject -noout)" = "subject= /CN=www.ssl-ca"
-	openssl genrsa -out keys/smtp
-	./ssl-ca sign
-	openssl verify -CAfile CA.crt certs/smtp
-	test "$$(openssl x509 -in certs/smtp -issuer -noout)" = "issuer= /CN=ssl-ca"
-	test "$$(openssl x509 -in certs/smtp -subject -noout)" = "subject= /CN=smtp.ssl-ca"
-	./ssl-ca resign
-	openssl verify -CAfile CA.crt certs/www
-	openssl verify -CAfile CA.crt certs/smtp
-	test "$$(openssl x509 -in certs/www -issuer -noout)" = "issuer= /CN=ssl-ca"
-	test "$$(openssl x509 -in certs/www -subject -noout)" = "subject= /CN=www.ssl-ca"
-	test "$$(openssl x509 -in certs/smtp -issuer -noout)" = "issuer= /CN=ssl-ca"
-	test "$$(openssl x509 -in certs/smtp -subject -noout)" = "subject= /CN=smtp.ssl-ca"
-	openssl s_server -cert certs/www -key keys/www -CAfile CA.crt -quiet -www -no_dhe & echo "$$!" > .server.pid
-	test "$$(curl --fail --cacert CA.crt --resolve www.ssl-ca:4433:127.0.0.1 --write-out '%{ssl_verify_result}' --silent --output /dev/null https://www.ssl-ca:4433/)" = "0"
-	kill "$$(cat .server.pid)"
-	rm .server.pid
+test: clean
+	bats --tap tests/
+
+pre-commit:
+	pre-commit run --all-files
 
 clean:
+	[ ! -f .server.pid ] || kill "$$(cat .server.pid)"
 	git clean -Xdf
diff --git a/README.rst b/README.rst
index 9c6490af9fc7a6e54967e4557951c4f52e92faa7..1040aea7d426187a47d6348f864b1457f6253185 100644
--- a/README.rst
+++ b/README.rst
@@ -4,11 +4,12 @@ SSL-CA
 .. image:: https://travis-ci.org/adarnimrod/ssl-ca.svg?branch=master
     :target: https://travis-ci.org/adarnimrod/ssl-ca
 
-This utility automates generating an SSL certificate authority, keys and signed
-certificates. The dependencies are: OpenSSL, cURL git and make (for testing and
-installation, although you can just copy the file). The use case in mind is
-testing and internal environments, therefore some security measures (like
-revocation) are not available in the current implementation.
+This utility automates generating an SSL certificate authority, keys and
+signed certificates. The only dependency is OpenSSL (and base utils). Make 
+and Git are needed for installation (although one can just download and copy
+:code:`ssl-ca`). The use case in mind is testing and internal environments,
+therefore some security measures (like revocation) are not available in the
+current implementation.
 
 Installation
 ------------
@@ -71,9 +72,17 @@ other cert on the internet.
 Development
 -----------
 
-For testing run :code:`make test`. For cleaning temporary files run :code:`git
-clean -fdx`. You can use `pre-commit <http://pre-commit.com/>`_ to have the test
-(which is quite quick) run on every commit to ensure quality code.
+Requirements are:
+
+- Python (2.7 or 3.5 or later).
+- Make.
+- Git.
+- Bats.
+
+Tests are written using `Bats <https://github.com/sstephenson/bats>`_ and some
+linters are used with `pre-commit <http://pre-commit.com/>`_. The :code:`clean`,
+:code:`test` and :code:`pre-commit` Make targets are provided. Installing the
+pre-commit Git hooks is recommended.
 
 License
 -------
diff --git a/tests/ssl-ca.bats b/tests/ssl-ca.bats
new file mode 100644
index 0000000000000000000000000000000000000000..bbd537e2286530555cd690fd7377216781e25049
--- /dev/null
+++ b/tests/ssl-ca.bats
@@ -0,0 +1,71 @@
+#!/usr/bin/env bats
+
+export PATH="$BATS_TEST_DIRNAME/../:$PATH"
+
+setup () {
+    make clean
+    ./ssl-ca init
+}
+
+teardown () {
+    make clean
+}
+
+verify () {
+    openssl verify -CAfile CA.crt "$1"
+}
+
+match () {
+    cmp <(openssl pkey -pubout -outform PEM -in "$1") <(openssl x509 -pubkey -noout -in "$2")
+}
+
+@test "init" {
+    [ "$(openssl rsa -noout -check -in CA.key)" = "RSA key ok" ]
+    [ "$(openssl verify -CAfile CA.crt CA.crt)" = "CA.crt: OK" ]
+    ./ssl-ca init
+}
+
+@test "generate cert" {
+    ./ssl-ca gen www
+    verify certs/www
+    match keys/www certs/www
+    [ "$(openssl rsa -noout -check -in keys/www)" = "RSA key ok" ]
+    [ "$(openssl x509 -in certs/www -issuer -noout)" = "issuer=CN = ssl-ca" ]
+    [ "$(openssl x509 -in certs/www -subject -noout)" = "subject=CN = www.ssl-ca" ]
+
+}
+
+@test "sign cert" {
+    openssl genrsa -out keys/smtp
+    ./ssl-ca sign
+    verify certs/smtp
+    match keys/smtp certs/smtp
+    [ "$(openssl x509 -in certs/smtp -issuer -noout)" = "issuer=CN = ssl-ca" ]
+    [ "$(openssl x509 -in certs/smtp -subject -noout)" = "subject=CN = smtp.ssl-ca" ]
+
+}
+
+@test "resign" {
+    ./ssl-ca gen www
+    openssl genrsa -out keys/smtp
+    ./ssl-ca sign
+    verify certs/www
+    match keys/www certs/www
+    verify certs/smtp
+    match keys/smtp certs/smtp
+    [ "$(openssl x509 -in certs/www -issuer -noout)" = "issuer=CN = ssl-ca" ]
+    [ "$(openssl x509 -in certs/www -subject -noout)" = "subject=CN = www.ssl-ca" ]
+    [ "$(openssl x509 -in certs/smtp -issuer -noout)" = "issuer=CN = ssl-ca" ]
+    [ "$(openssl x509 -in certs/smtp -subject -noout)" = "subject=CN = smtp.ssl-ca" ]
+}
+
+@test "webserver" {
+    ./ssl-ca gen www
+    openssl s_server -cert certs/www -key keys/www -CAfile CA.crt -quiet -www -no_dhe &
+    echo "$!" > .server.pid
+    run curl --fail --cacert CA.crt --resolve www.ssl-ca:4433:127.0.0.1 --write-out '%{ssl_verify_result}' --silent --output /dev/null https://www.ssl-ca:4433/
+    [ "$output" = "0" ]
+    [ "$status" -eq 0 ]
+    kill "$(cat .server.pid)"
+    rm .server.pid
+}