diff --git a/heist-aeson.cabal b/heist-aeson.cabal
--- a/heist-aeson.cabal
+++ b/heist-aeson.cabal
@@ -7,7 +7,7 @@
 -- The package version. See the Haskell package versioning policy
 -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
 -- standards guiding when and how versions should be incremented.
-Version:             0.1
+Version:             0.2
 
 -- A short (one-line) description of the package.
 Synopsis:            Use JSON directly from Heist templates.
@@ -37,10 +37,13 @@
 
 -- Extra files to be distributed with the package, such as examples or
 -- a README.
--- Extra-source-files:  
+Extra-source-files:  tests/runTests.hs
+                     tests/*.tpl
+                     tests/*.json
+                     tests/*.expected
 
 -- Constraint on the version of Cabal needed to build this package.
-Cabal-version:       >=1.2
+Cabal-version:       >=1.6
 
 
 Library
diff --git a/tests/access.expected b/tests/access.expected
new file mode 100644
--- /dev/null
+++ b/tests/access.expected
@@ -0,0 +1,6 @@
+value
+Third level
+<attr attribute='Third level'></attr>
+
+  Third level
+
diff --git a/tests/access.json b/tests/access.json
new file mode 100644
--- /dev/null
+++ b/tests/access.json
@@ -0,0 +1,2 @@
+{"key": "value"
+,"level1": {"level2": {"level3": "Third level"}}}
diff --git a/tests/access.tpl b/tests/access.tpl
new file mode 100644
--- /dev/null
+++ b/tests/access.tpl
@@ -0,0 +1,6 @@
+<val:key/>
+<val:level1.level2.level3/>
+<attr attribute="$(val:level1.level2.level3)"/>
+<json:section key="level1.level2">
+  <val:level3/>
+</json:section>
diff --git a/tests/loop.expected b/tests/loop.expected
new file mode 100644
--- /dev/null
+++ b/tests/loop.expected
@@ -0,0 +1,13 @@
+<html>
+
+  <ul>
+    
+      <li>One</li>
+    
+      <li>Two</li>
+    
+      <li>Three</li>
+    
+  </ul>
+
+</html>
diff --git a/tests/loop.json b/tests/loop.json
new file mode 100644
--- /dev/null
+++ b/tests/loop.json
@@ -0,0 +1,1 @@
+{"loop": ["One", "Two", "Three"]}
diff --git a/tests/loop.tpl b/tests/loop.tpl
new file mode 100644
--- /dev/null
+++ b/tests/loop.tpl
@@ -0,0 +1,9 @@
+<html>
+<json:section key="loop">
+  <ul>
+    <json:repeated key="@">
+      <li><val:self/></li>
+    </json:repeated>
+  </ul>
+</json:section>
+</html>
diff --git a/tests/runTests.hs b/tests/runTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/runTests.hs
@@ -0,0 +1,68 @@
+module Main ( main ) where
+
+import Text.Templating.Heist.Aeson
+import Text.Templating.Heist
+import System.Directory
+import System.FilePath
+import System.Exit
+import System.IO
+import Data.Aeson
+import Data.Attoparsec
+import Blaze.ByteString.Builder
+import Control.Monad
+import qualified Data.ByteString.Char8 as Strict
+
+main :: IO ()
+main = runTests
+
+runTests :: IO ()
+runTests
+    = do tests <- findTests
+         ets <- loadTemplates "." $ addHeistAeson $ emptyTemplateState "."
+         let ts = either error id ets
+         results <- mapM (executeTest ts) tests
+         unless (and results) $ exitWith (ExitFailure 1)
+
+data Test = Test { testName :: String
+                 , testJson :: FilePath
+                 , testTemplate :: FilePath
+                 , testExpected :: FilePath }
+
+-- Find all tests (.tpl, .json, .expected triplets) in the current directory.
+findTests :: IO [Test]
+findTests = do files <- getDirectoryContents "."
+               return [ Test { testName = takeBaseName file
+                             , testJson = json
+                             , testTemplate = file
+                             , testExpected = expected }
+                        | file <- files
+                      , takeExtension file == ".tpl"
+                      , json <- filter (== replaceExtension file "json") files
+                      , expected <- filter (== replaceExtension file "expected") files ]
+
+
+executeTest :: JsonState IO -> Test -> IO Bool
+executeTest state test = do
+    putStr (testName test ++ ": ")
+    hFlush stdout
+    jsonTxt <- Strict.readFile (testJson test)
+    case eitherResult (parse json jsonTxt) of
+      Left errMsg     -> do putStrLn "FAILED"
+                            hPutStrLn stderr $ "Failed to parse json: " ++ errMsg
+                            return False
+      Right jsonValue -> do
+        result  <- renderJsonTemplate state (Strict.pack $ testName test) jsonValue
+        case result of
+          Nothing -> do putStrLn "FAILED"
+                        hPutStrLn stderr $ "Failed to parse template"
+                        return False
+          Just (t, mime) -> do
+            expected <- Strict.readFile (testExpected test)
+            if expected == toByteString t
+               then do putStrLn "OK"
+                       return True
+               else do putStrLn "FAILED"
+                       let outFile = addExtension (testName test) "out"
+                       hPutStrLn stderr $ "Generated data doesn't match expected result. Data written to " ++ outFile
+                       Strict.writeFile outFile (toByteString t)
+                       return False
diff --git a/tests/scope.expected b/tests/scope.expected
new file mode 100644
--- /dev/null
+++ b/tests/scope.expected
@@ -0,0 +1,6 @@
+value
+
+  value
+  <val:outer.inner></val:outer.inner>
+
+<val:inner></val:inner>
diff --git a/tests/scope.json b/tests/scope.json
new file mode 100644
--- /dev/null
+++ b/tests/scope.json
@@ -0,0 +1,1 @@
+{"outer": {"inner": "value"}}
diff --git a/tests/scope.tpl b/tests/scope.tpl
new file mode 100644
--- /dev/null
+++ b/tests/scope.tpl
@@ -0,0 +1,6 @@
+<val:outer.inner/>
+<json:section key="outer">
+  <val:inner/>
+  <val:outer.inner/>
+</json:section>
+<val:inner/>
diff --git a/tests/section.expected b/tests/section.expected
new file mode 100644
--- /dev/null
+++ b/tests/section.expected
@@ -0,0 +1,9 @@
+<div>
+  
+    No songs available.
+  
+  
+    Username: Lemmih
+  
+  
+</div>
diff --git a/tests/section.json b/tests/section.json
new file mode 100644
--- /dev/null
+++ b/tests/section.json
@@ -0,0 +1,1 @@
+{"user": { "name": "Lemmih" }}
diff --git a/tests/section.tpl b/tests/section.tpl
new file mode 100644
--- /dev/null
+++ b/tests/section.tpl
@@ -0,0 +1,11 @@
+<div>
+  <json:no-section key="songs">
+    No songs available.
+  </json:no-section>
+  <json:section key="user">
+    Username: <val:name/>
+  </json:section>
+  <json:no-section key="user">
+    No user info available.
+  </json:no-section>
+</div>
