diff --git a/elocrypt.cabal b/elocrypt.cabal
--- a/elocrypt.cabal
+++ b/elocrypt.cabal
@@ -1,5 +1,5 @@
 name:                elocrypt
-version:             0.3.1
+version:             0.3.2
 synopsis:            Generate easy-to-remember, hard-to-guess passwords
 homepage:            https://www.github.com/sgillespie/elocrypt
 license:             OtherLicense
@@ -9,7 +9,10 @@
 copyright:           Copyright: (c) 2015 Sean Gillespie
 category:            Cryptography
 build-type:          Simple
-extra-source-files:  README.md
+extra-source-files:  README.md,
+                     test/*.hs,
+                     test/Test/*.hs,
+                     test/Test/Elocrypt/*.hs
 cabal-version:       >=1.10
 
 description:
@@ -23,7 +26,7 @@
 source-repository this
   type: git
   location: https://github.com/sgillespie/elocrypt.git
-  tag: v0.3.0
+  tag: v0.3.2
                   
 library
   exposed-modules:     Data.Elocrypt,
diff --git a/src/cli/Main.hs b/src/cli/Main.hs
--- a/src/cli/Main.hs
+++ b/src/cli/Main.hs
@@ -10,7 +10,7 @@
 
 import Data.Elocrypt
 
-version = "elocrypt 0.3.1"
+version = "elocrypt 0.3.2"
 
 main :: IO ()
 main = do
diff --git a/test/Test/Elocrypt/Instances.hs b/test/Test/Elocrypt/Instances.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Elocrypt/Instances.hs
@@ -0,0 +1,20 @@
+module Test.Elocrypt.Instances where
+
+import Control.Monad
+import System.Random
+import Test.QuickCheck
+
+instance Arbitrary StdGen where
+  arbitrary = mkStdGen `liftM` arbitrary
+
+newtype AlphaChar = Alpha Char
+                  deriving (Eq, Ord, Show)
+
+instance Arbitrary AlphaChar where
+  arbitrary = Alpha `liftM` elements ['a'..'z']
+
+newtype GreaterThan2 a = GT2 { greaterThan2 :: a }
+                       deriving (Eq, Ord, Show)
+
+instance (Num a, Ord a, Arbitrary a) => Arbitrary (GreaterThan2 a) where
+  arbitrary = GT2 `fmap` (arbitrary `suchThat` (>2))
diff --git a/test/Test/Elocrypt/TrigraphTest.hs b/test/Test/Elocrypt/TrigraphTest.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Elocrypt/TrigraphTest.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Test.Elocrypt.TrigraphTest where
+
+import Control.Monad
+import Test.QuickCheck
+import Test.Tasty.QuickCheck (testProperty)
+import Test.Tasty.TH
+
+import Test.Elocrypt.Instances
+import Data.Elocrypt.Trigraph
+
+tests = $(testGroupGenerator)
+
+prop_findFrequencyShouldSucceedWith2Chars :: AlphaChar -> AlphaChar -> Bool
+prop_findFrequencyShouldSucceedWith2Chars (Alpha c1) (Alpha c2)
+  = fromJust (findFrequency [c1, c2])
+  where fromJust (Just _) = True
+        fromJust Nothing  = False
+
+prop_findFrequencyShouldFailWithNot2Chars :: String -> Property
+prop_findFrequencyShouldFailWithNot2Chars str
+  = length str /= 2 ==> findFrequency str == Nothing
diff --git a/test/Test/ElocryptTest.hs b/test/Test/ElocryptTest.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/ElocryptTest.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Test.ElocryptTest where
+
+import Control.Monad
+import Control.Monad.Random hiding (next)
+import Data.Maybe
+import System.Random hiding (next)
+import Test.QuickCheck
+import Test.Tasty.QuickCheck (testProperty)
+import Test.Tasty.TH
+
+import Data.Elocrypt
+import Data.Elocrypt.Trigraph
+import Test.Elocrypt.Instances
+
+tests = $(testGroupGenerator)
+
+prop_genPasswordShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool
+prop_genPasswordShouldBeUnique (GT2 len) gen
+  = p /= fst (genPassword len gen')
+  where (p, gen') = genPassword len gen
+
+prop_genPasswordsShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool
+prop_genPasswordsShouldBeUnique (GT2 len) gen
+  = p /= ps
+  where (p:ps:_) = fst (genPasswords len gen)
+
+prop_newPasswordShouldBeLength :: GreaterThan2 Int -> StdGen -> Bool
+prop_newPasswordShouldBeLength (GT2 len) gen = length (newPassword len gen) == len
+
+prop_newPasswordShouldConsistOfAlphabet :: GreaterThan2 Int -> StdGen -> Bool
+prop_newPasswordShouldConsistOfAlphabet (GT2 len) gen
+  = all ((flip elem) alphabet) (newPassword len gen)
+
+prop_newPasswordsShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool
+prop_newPasswordsShouldBeUnique (GT2 len) gen
+  = p /= ps
+  where (p:ps:_) = newPasswords len gen
+
+prop_first2ShouldHaveLength2 :: StdGen -> Bool
+prop_first2ShouldHaveLength2 g = length (evalRand first2 g) == 2
+
+prop_nextShouldSkip0Weights :: AlphaChar -> AlphaChar -> StdGen -> Property
+prop_nextShouldSkip0Weights (Alpha c1) (Alpha c2) gen = isCandidate next' [c1, c2]
+  where next' = evalRand (next . reverse $ [c1, c2]) gen
+
+prop_lastNShouldSkip0Weights :: AlphaChar -> AlphaChar -> Int -> StdGen -> Property
+prop_lastNShouldSkip0Weights (Alpha c1) (Alpha c2) len gen
+  = len > 0 ==> lastNShouldSkip0Weights' lastN'
+  where f2 = [c1, c2]
+        lastN' = evalRand (lastN (reverse [c1, c2]) len) gen
+
+lastNShouldSkip0Weights' :: String -> Property
+lastNShouldSkip0Weights' (p:ps:pss:psss) = isCandidate p [pss, ps] .&&.
+                                           lastNShouldSkip0Weights' (ps:pss:psss)
+lastNShouldSkip0Weights' _ = property True
+
+-- Utility functions
+isCandidate :: Char -> String -> Property
+isCandidate c (s:ss:sss) = hasCandidates f2 ==>
+                           isJust $ elem c `liftM` findNextCandidates f2
+  where f2 = [s, ss]
+
+isCandidate _ _ = undefined -- This really shouldn't ever happen
+
+hasCandidates :: String -> Bool
+hasCandidates s = (not . null) `liftM` findNextCandidates s == Just True
+
+findNextCandidates :: String -> Maybe [Char]
+findNextCandidates (c1:c2:_) = map fst `liftM` frequency
+  where f2 = [c1, c2]
+        frequency = (filter ((/=0) . snd) . zip ['a'..'z']) `liftM` findFrequency f2
+
+findNextCandidates _ = Nothing  -- This really shouldn't ever happen
