diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014, Pat Brisbin
+Copyright (c) 2017, Pat Brisbin
 
 All rights reserved.
 
diff --git a/load-env.cabal b/load-env.cabal
--- a/load-env.cabal
+++ b/load-env.cabal
@@ -1,42 +1,56 @@
-name:                   load-env
-version:                0.1.1
-author:                 Pat Brisbin <pbrisbin@gmail.com>
-maintainer:             Pat Brisbin <pbrisbin@gmail.com>
-license:                BSD3
-license-file:           LICENSE
-synopsis:               Load environment variables from a file.
-category:               Configuration
-description:            Parse a .env file and load any declared variables into
-                        the current process's environment. This allows for a
-                        .env file to specify development-friendly defaults for
-                        configuration values normally set in the deployment
-                        environment.
-cabal-version:          >= 1.10
-build-type:             Simple
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 9c909afbd1a08e860e60320c5f83c51783e382789593a82de5798a86d1464c45
 
+name:           load-env
+version:        0.1.2
+synopsis:       Load environment variables from a file.
+description:    Parse a .env file and load any declared variables into the current process's environment. This allows for a .env file to specify development-friendly defaults for configuration values normally set in the deployment environment.
+category:       Configuration
+homepage:       https://github.com/pbrisbin/load-env#readme
+bug-reports:    https://github.com/pbrisbin/load-env/issues
+author:         Pat Brisbin <pbrisbin@gmail.com>
+maintainer:     Pat Brisbin <pbrisbin@gmail.com>
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+source-repository head
+  type: git
+  location: https://github.com/pbrisbin/load-env
+
 library
-  default-language:     Haskell2010
-  hs-source-dirs:       src
-  ghc-options:          -Wall
-  exposed-modules:      LoadEnv
-                      , LoadEnv.Parse
-  build-depends:        base >= 4.7.0 && < 5
-                      , directory
-                      , parsec
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >=4.8.0 && <5
+    , directory
+    , parsec
+  exposed-modules:
+      LoadEnv
+      LoadEnv.Parse
+  other-modules:
+      Paths_load_env
+  default-language: Haskell2010
 
 test-suite spec
-  type:                 exitcode-stdio-1.0
-  default-language:     Haskell2010
-  hs-source-dirs:       test
-  ghc-options:          -Wall
-  main-is:              Spec.hs
-  build-depends:        base
-                      , load-env
-                      , directory
-                      , hspec
-                      , HUnit
-                      , parsec
-
-source-repository head
-  type:                 git
-  location:             https://github.com/pbrisbin/load-env
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  ghc-options: -Wall
+  build-depends:
+      base >=4.8.0 && <5
+    , directory
+    , hspec
+    , load-env
+    , parsec
+  other-modules:
+      LoadEnv.ParseSpec
+      LoadEnvSpec
+      Paths_load_env
+  default-language: Haskell2010
diff --git a/src/LoadEnv.hs b/src/LoadEnv.hs
--- a/src/LoadEnv.hs
+++ b/src/LoadEnv.hs
@@ -5,11 +5,10 @@
 
 
 import Control.Monad (when)
+import LoadEnv.Parse
 import System.Directory (doesFileExist)
 import System.Environment (setEnv)
 import Text.Parsec.String (parseFromFile)
-
-import LoadEnv.Parse
 
 -- |
 --
diff --git a/src/LoadEnv/Parse.hs b/src/LoadEnv/Parse.hs
--- a/src/LoadEnv/Parse.hs
+++ b/src/LoadEnv/Parse.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 module LoadEnv.Parse
     ( Environment
     , Variable
@@ -6,13 +5,8 @@
     , parseVariable
     ) where
 
-#if __GLASGOW_HASKELL__ < 710
-import Control.Applicative ((<$>))
-#endif
-
 import Control.Monad (void)
 import Data.Maybe (catMaybes)
-
 import Text.Parsec
 import Text.Parsec.String
 
@@ -26,25 +20,22 @@
 parseLine = possibly parseVariable
 
 possibly :: Parser a -> Parser (Maybe a)
-possibly p = try (fmap Just p) <|> ignored
+possibly p = try (Just <$> p) <|> ignored
 
   where
-    ignored = do
-        void $ manyTill anyToken newline
-        return Nothing
+    ignored = Nothing <$ manyTill anyToken newline
 
 parseVariable :: Parser Variable
 parseVariable = do
     optional $ between spaces spaces $ string "export"
 
     i <- identifier
-    void $ char '='
+    v <- char '=' *> value
 
-    v <- value
     void $ many $ oneOf " \t"
-    void $ newline
+    void newline
 
-    return (i, v)
+    pure (i, v)
 
 -- Environment variable names used by the utilities in the Shell and Utilities
 -- volume of IEEE Std 1003.1-2001 consist solely of uppercase letters, digits,
@@ -58,13 +49,13 @@
     x <- upper <|> underscore
     ys <- many $ upper <|> digit <|> underscore
 
-    return (x:ys)
+    pure (x:ys)
 
   where
     underscore = char '_'
 
 value :: Parser String
-value = quotedValue <|> unquotedValue <|> return ""
+value = quotedValue <|> unquotedValue <|> pure ""
 
 quotedValue :: Parser String
 quotedValue = do
@@ -76,4 +67,4 @@
 unquotedValue = many1 $ try (escaped ' ') <|> noneOf "\"' \n"
 
 escaped :: Char -> Parser Char
-escaped c = string ("\\" ++ [c]) >> return c
+escaped c = c <$ string ("\\" ++ [c])
diff --git a/test/LoadEnv/ParseSpec.hs b/test/LoadEnv/ParseSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/LoadEnv/ParseSpec.hs
@@ -0,0 +1,113 @@
+module LoadEnv.ParseSpec
+    ( spec
+    ) where
+
+import LoadEnv.Parse
+import Test.Hspec
+import Text.Parsec (parse)
+
+spec :: Spec
+spec = do
+    describe "parseEnvironment" $ do
+        it "parses variable declarations among comments and blank lines" $ do
+            let env = unlines
+                    [ "# An environment file"
+                    , "FOO=bar"
+                    , "BAZ=\"bat\""
+                    , "BAT=\"multi-"
+                    , "pass"
+                    , "\""
+                    , ""
+                    , "# vim ft:sh:"
+                    ]
+
+            parse parseEnvironment "" env `shouldBe` Right
+                [ ("FOO", "bar")
+                , ("BAZ", "bat")
+                , ("BAT", "multi-\npass\n")
+                ]
+
+        it "parses an empty file into an empty list of variables" $ do
+            parse parseEnvironment "" "" `shouldBe` Right []
+
+
+    describe "parseVariable" $ do
+        it "reads unquoted variables" $
+            parse parseVariable "" "FOO=bar\n" `shouldBe` Right ("FOO", "bar")
+
+        it "reads quoted variables" $ do
+            parse parseVariable "" "FOO=\"bar\"\n"
+                `shouldBe` Right ("FOO", "bar")
+            parse parseVariable "" "FOO='bar'\n"
+                `shouldBe` Right ("FOO", "bar")
+
+        it "allows newlines in quoted variables" $ do
+            parse parseVariable "" "FOO=\"foo\nbar\"\n"
+                `shouldBe` Right ("FOO", "foo\nbar")
+
+        it "handles empty values" $
+            parse parseVariable "" "FOO=\n" `shouldBe` Right ("FOO", "")
+
+        it "handles empty quoted values" $ do
+            parse parseVariable "" "FOO=\"\"\n" `shouldBe` Right ("FOO", "")
+            parse parseVariable "" "FOO=''\n" `shouldBe` Right ("FOO", "")
+
+        it "handles underscored variables" $
+            parse parseVariable "" "FOO_BAR=baz\n"
+                `shouldBe` Right ("FOO_BAR", "baz")
+
+        it "treats leading spaces as invalid" $
+            parse parseVariable "" "  FOO=bar\n"
+                `shouldContainError` "unexpected \"F\""
+
+        it "treats spaces around equals as invalid" $
+            parse parseVariable "" "FOO = bar\n"
+                `shouldContainError` "unexpected \" \""
+
+        it "treats unquoted spaces as invalid" $
+            parse parseVariable "" "FOO=bar baz\n"
+                `shouldContainError` "unexpected \"b\""
+
+        it "treats unbalanced quotes as invalid" $ do
+            parse parseVariable "" "FOO=\"bar\n"
+                `shouldContainError` "unexpected end of input"
+            parse parseVariable "" "FOO='bar\n"
+                `shouldContainError` "unexpected end of input"
+            parse parseVariable "" "FOO=bar\"\n"
+                `shouldContainError` "unexpected \"\\\"\""
+            parse parseVariable "" "FOO=bar'\n"
+                `shouldContainError` "unexpected \"\'\""
+
+        it "handles escaped quotes" $ do
+            parse parseVariable "" "FOO=\"bar\\\"baz\"\n"
+                `shouldBe` Right ("FOO", "bar\"baz")
+            parse parseVariable "" "FOO='bar\\'baz'\n"
+                `shouldBe` Right ("FOO", "bar'baz")
+
+        it "handles escaped spaces" $
+            parse parseVariable "" "FOO=bar\\ baz\n"
+                `shouldBe` Right ("FOO", "bar baz")
+
+        it "discards any lines using `export'" $
+            parse parseVariable "" "export FOO=bar\n"
+                `shouldBe` Right ("FOO", "bar")
+
+        it "requires valid environment variable identifies" $ do
+            parse parseVariable "" "S3_KEY=abc123\n"
+                `shouldBe` Right ("S3_KEY", "abc123")
+            parse parseVariable "" "_S3_KEY=abc123\n"
+                `shouldBe` Right ("_S3_KEY", "abc123")
+
+            parse parseVariable "" "S3~KEY=abc123\n"
+                `shouldContainError` "unexpected \"~\""
+            parse parseVariable "" "S3-KEY=abc123\n"
+                `shouldContainError` "unexpected \"-\""
+            parse parseVariable "" "S3_key=abc123\n"
+                `shouldContainError` "unexpected \"k\""
+            parse parseVariable "" "3_KEY=abc123\n"
+                `shouldContainError` "unexpected \"3\""
+
+shouldContainError :: Show a => Either a b -> String -> Expectation
+v `shouldContainError` msg = either
+    (\e -> show e `shouldContain` msg)
+    (\_ -> expectationFailure "Expected no parse") v
diff --git a/test/LoadEnvSpec.hs b/test/LoadEnvSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/LoadEnvSpec.hs
@@ -0,0 +1,38 @@
+module LoadEnvSpec
+    ( spec
+    ) where
+
+import Control.Monad (when)
+import LoadEnv
+import System.Directory (doesFileExist, removeFile)
+import System.Environment (lookupEnv)
+import Test.Hspec
+
+spec :: Spec
+spec = after_ cleanup $
+    describe "loadEnv" $ do
+        it "loads environment variables from ./.env if present" $ do
+            writeFile envFile $ unlines
+                [ "FOO=\"bar\""
+                , "BAZ=\"bat\""
+                ]
+
+            loadEnvFrom envFile
+
+            mbar <- lookupEnv "FOO"
+            mbat <- lookupEnv "BAZ"
+            mbar `shouldBe` Just "bar"
+            mbat `shouldBe` Just "bat"
+
+        it "does not fail if the file is not present" $ do
+            loadEnvFrom "i-do-not-exist"
+
+            return ()
+
+cleanup :: IO ()
+cleanup = do
+    e <- doesFileExist envFile
+    when e $ removeFile envFile
+
+envFile :: FilePath
+envFile = "/tmp/load-env-test-file"
