diff --git a/cipher_typer/cipher.py b/cipher_typer/cipher.py
index 9db77a1..4563c39 100644
--- a/cipher_typer/cipher.py
+++ b/cipher_typer/cipher.py
@@ -153,29 +153,17 @@ class CaeserSeedCipher:
         return self.translated
 
     def encrypt(self, message: str, key: int = 0) -> str:
+        if key > self.max_key_length:
+            raise IndexError(f"key must be less than {self.max_key_length}")
         self.translated = ""
         self.key = key
         self.message = message
         return self._crypto("encrypt")
 
     def decrypt(self, message: str, key: int = 0) -> str:
+        if key > self.max_key_length:
+            raise IndexError(f"key must be less than {self.max_key_length}")
         self.translated = ""
         self.key = key
         self.message = message
         return self._crypto("decrypt")
-
-
-if __name__ == "__main__":
-    print("Using CaeserCipher:")
-    cipher = CaeserCipher()
-    print(cipher.encrypt("Secret message.", 13))
-    print(cipher.decrypt("'rpErGmzrFFntr`", 13))
-    print(cipher.encrypt("John Doe will be on the [08:00] train @King's Cross", 56))
-    print(cipher.decrypt("9>-=%3>*%{.;;%'*%>=%^-*%KT#DTTL%^[&.=%J .=,u]%2[>]]", 56))
-
-    print("\nUsing CaeserSeedCipher:")
-    c = CaeserSeedCipher()
-    print(c.encrypt("Secret message.", 13))
-    print(c.decrypt("hx5dx}fqx%%HDx-", 13))
-    print(c.encrypt("John Doe will be on the [08:00] train @King's Cross", 56))
-    print(c.decrypt("lG#R27G:2rD||2b:2GR2_#:29uEzuu%2_c?DR2BTDR=,`2xcG``", 56))
diff --git a/dev-requirements.in b/dev-requirements.in
new file mode 100644
index 0000000..f2578c6
--- /dev/null
+++ b/dev-requirements.in
@@ -0,0 +1,2 @@
+-r requirements.txt
+pytest
\ No newline at end of file
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..9688534
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,32 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+#    pip-compile dev-requirements.in
+#
+atomicwrites==1.4.0
+    # via pytest
+attrs==21.2.0
+    # via pytest
+click==7.1.2
+    # via
+    #   -r requirements.txt
+    #   typer
+colorama==0.4.4
+    # via pytest
+iniconfig==1.1.1
+    # via pytest
+packaging==20.9
+    # via pytest
+pluggy==0.13.1
+    # via pytest
+py==1.10.0
+    # via pytest
+pyparsing==2.4.7
+    # via packaging
+pytest==6.2.4
+    # via -r dev-requirements.in
+toml==0.10.2
+    # via pytest
+typer==0.3.2
+    # via -r requirements.txt
diff --git a/setup.cfg b/setup.cfg
index f5f4db1..0b489e1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,10 +4,15 @@ version = 0.1.0
 description = command line utility to encrypt and decrypt text
 author = Andrew White
 author_email = white-aj@outlook.com
-url = ...
+url = https://github.com/aj-white/cipher_typer
+license = MIT
+license_file = LICENSE
 
 [options]
 packages = find:
+install_requires =
+    typer>=0.3.2
+python_requires = >=3.6.1
 
 [option.packages.find]
 exclude =

diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_cipher.py b/tests/test_cipher.py
new file mode 100644
index 0000000..2892e8f
--- /dev/null
+++ b/tests/test_cipher.py
@@ -0,0 +1,134 @@
+import string
+
+import pytest
+from cipher_typer.cipher import CaeserCipher, CaeserSeedCipher, InvalidSelector
+
+
+def test_caeser_all_no_key():
+    c = CaeserCipher("all")
+    message = string.ascii_letters + string.digits
+    enc = c.encrypt(message)
+    dec = c.decrypt(enc)
+    assert dec == message
+
+
+def test_caeser_all():
+    c = CaeserCipher("all")
+    message = string.ascii_letters + string.digits
+    enc = c.encrypt(message, 25)
+    dec = c.decrypt(enc, 25)
+    assert dec == message
+
+
+def test_caeser_lower():
+    c = CaeserCipher("lower")
+    message = string.ascii_lowercase
+    enc = c.encrypt(message, 18)
+    dec = c.decrypt(enc, 18)
+    assert dec == message
+
+
+def test_caeser_upper():
+    c = CaeserCipher("upper")
+    message = string.ascii_uppercase
+    enc = c.encrypt(message, 13)
+    dec = c.decrypt(enc, 13)
+    assert dec == message
+
+
+def test_caeser_both():
+    c = CaeserCipher("both")
+    message = "I am BOTH Upper and lowerCase"
+    enc = c.encrypt(message, 9)
+    dec = c.decrypt(enc, 9)
+    assert dec == message
+
+
+def test_caeser_key_error():
+    c = CaeserCipher("lower")
+    message = string.ascii_lowercase
+    with pytest.raises(IndexError):
+        assert c.encrypt(message, 56)
+        assert c.decrypt(message, 87)
+
+
+def test_caeser_invalid_selector():
+    with pytest.raises(InvalidSelector):
+        assert CaeserCipher("jeff")
+
+
+def test_caeser_mixed_case_message_with_lower():
+    c = CaeserCipher("lower")
+    message = "I am in B8th CaseS"
+    assert c.encrypt(message, 7) == "I ht pu B8ao ChzlS"
+
+
+def test_caser_mixed_case_message_with_upper():
+    c = CaeserCipher("upper")
+    message = "&I am in UPper and LowerCASE WIth 67"
+    assert c.encrypt(message, 18) == "&A am in MHper and DowerUSKW OAth 67"
+
+
+# Test CaserSeedCipher class
+def test_caeser_seed_all_no_key():
+    cs = CaeserSeedCipher()
+    message = string.ascii_letters + string.digits
+    enc = cs.encrypt(message)
+    dec = cs.decrypt(enc)
+    assert dec == message
+
+
+def test_caeser_seed_all():
+    cs = CaeserSeedCipher()
+    message = string.ascii_letters + " " + string.digits + "#"
+    enc = cs.encrypt(message, 60)
+    dec = cs.decrypt(enc, 60)
+    assert dec == message
+
+
+def test_caeser_seed_lower():
+    cs = CaeserSeedCipher("lower")
+    message = string.ascii_lowercase
+    enc = cs.encrypt(message, 3)
+    dec = cs.decrypt(enc, 3)
+    assert dec == message
+
+
+def test_caeser_seed_upper():
+    cs = CaeserSeedCipher("upper")
+    message = string.ascii_uppercase
+    enc = cs.encrypt(message, 11)
+    dec = cs.decrypt(enc, 11)
+    assert dec == message
+
+
+def test_caeser_seed_both():
+    cs = CaeserSeedCipher("both")
+    message = string.ascii_letters
+    enc = cs.encrypt(message, 12)
+    dec = cs.decrypt(enc, 12)
+    assert dec == message
+
+
+def test_caeser_seed_key_error():
+    cs = CaeserSeedCipher()
+    with pytest.raises(IndexError):
+        assert cs.encrypt("Jeff", 100)
+        assert cs.decrypt("Jeff", 230)
+
+
+def test_caeser_seed_invalid_selector():
+    with pytest.raises(InvalidSelector):
+        assert CaeserSeedCipher("poo")
+
+
+def test_caser_seed_mixed_case_lower():
+    cs = CaeserSeedCipher("lower")
+    message = "I am in B8th CaseS"
+    assert cs.encrypt(message, 7) == "I kq lp B8od CktbS"
+
+
+def test_caser_seed_mixed_case_upper():
+    cs = CaeserSeedCipher("upper")
+    message = "&I am in UPper and LowerCASE WIth 67"
+    assert cs.encrypt(message, 18) == "&S am in JMper and EowerVFCK XSth 67"

Missing tests
Please write tests and remove 'examples in cipher.py
tests/test_cipher.py::test_caeser_seed_key_error
tests/test_cipher.py::test_caeser_all_no_key tests/test_cipher.py::test_caeser_all tests/test_cipher.py::test_caeser_lower tests/test_cipher.py::test_caeser_upper tests/test_cipher.py::test_caeser_both tests/test_cipher.py::test_caeser_key_error tests/test_cipher.py::test_caeser_invalid_selector tests/test_cipher.py::test_caeser_mixed_case_message_with_lower tests/test_cipher.py::test_caser_mixed_case_message_with_upper tests/test_cipher.py::test_caeser_seed_all_no_key tests/test_cipher.py::test_caeser_seed_all tests/test_cipher.py::test_caeser_seed_lower tests/test_cipher.py::test_caeser_seed_upper tests/test_cipher.py::test_caeser_seed_both tests/test_cipher.py::test_caeser_seed_invalid_selector tests/test_cipher.py::test_caser_seed_mixed_case_lower tests/test_cipher.py::test_caser_seed_mixed_case_upper