diff --git a/aeson-via.cabal b/aeson-via.cabal
--- a/aeson-via.cabal
+++ b/aeson-via.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 60d4229649edb0ef7f1268f79df05c7fa9d34be348ef1a2d860902ccdc143242
+-- hash: 318cc7b2c6d83b710b0f777d76624040eeabdc8b6851a92937edba2804dcffde
 
 name:           aeson-via
-version:        0.1.0
+version:        0.1.1
 synopsis:       Wrappers to derive-via Aeson ToJSON/FromJSON typeclasses
 description:    Please see the README on GitHub at <https://github.com/ejconlon/aeson-via#readme>
 category:       Data
@@ -35,9 +35,28 @@
       src
   ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds
   build-depends:
-      aeson >=1.4
-    , aeson-casing >=0.2
+      aeson >=1.5 && <1.6
+    , aeson-casing >=0.2 && <0.3
     , base >=4.12 && <5
-    , newtype-generics >=0.5
-    , text >=1.2
+    , newtype-generics >=0.5 && <0.6
+    , text >=1.2 && <1.3
+  default-language: Haskell2010
+
+test-suite aeson-via-test
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  other-modules:
+      Paths_aeson_via
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fno-warn-unused-top-binds -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      aeson >=1.5 && <1.6
+    , aeson-casing >=0.2 && <0.3
+    , aeson-via
+    , base >=4.12 && <5
+    , newtype-generics >=0.5 && <0.6
+    , tasty >=1.2 && <1.3
+    , tasty-hunit >=0.10 && <0.11
+    , text >=1.2 && <1.3
   default-language: Haskell2010
diff --git a/src/AesonVia.hs b/src/AesonVia.hs
--- a/src/AesonVia.hs
+++ b/src/AesonVia.hs
@@ -2,6 +2,8 @@
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
 
+-- | Wrappers to control generic 'ToJSON' and 'FromJSON' derivation with deriving-via.
+--   See the test for example definitions and their encoding.
 module AesonVia
   ( AesonRecord (..)
   , AesonNewtype (..)
@@ -11,7 +13,8 @@
   ) where
 
 import Control.Newtype.Generics (Newtype, O, pack, unpack)
-import Data.Aeson
+import Data.Aeson (FromJSON (..), GFromJSON, GToEncoding, GToJSON, Options (..), ToJSON (..), Zero, defaultOptions,
+                   genericParseJSON, genericToEncoding, genericToJSON)
 import Data.Aeson.Casing (aesonPrefix, snakeCase)
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
@@ -39,14 +42,19 @@
 
 -- Has classes
 
+-- | Mostly an internal class directing constructor/field conversion.
 class HasJSONOptions a where
   getJSONOptions :: Proxy a -> Options
 
+-- | Used with 'AesonTag' to define a prefix to be removed from a 'Bounded' 'Enum'.
+-- For example, `data Foo = FooBar | FooBaz` would use the prefix `Foo` to yield converted string
+-- values `bar` and `baz`.
 class HasTagPrefix a where
   getTagPrefix :: Proxy a -> Text
 
 -- Wrappers
 
+-- | Generic deriving ToJSON/FromJSON via this uses 'HasTagPrefix' to turn 'Bounded' 'Enum' datatypes into enumerated strings.
 newtype AesonTag a = AesonTag { unAesonTag :: a }
 
 instance HasTagPrefix a => HasJSONOptions (AesonTag a) where
@@ -59,6 +67,7 @@
 instance (HasJSONOptions (AesonTag a), Generic a, GFromJSON Zero (Rep a)) => FromJSON (AesonTag a) where
   parseJSON = (AesonTag <$>) . genericParseJSON (getJSONOptions (Proxy :: Proxy (AesonTag a)))
 
+-- | Generic deriving ToJSON/FromJSON via this removes the common field name prefix in the encoding.
 newtype AesonRecord a = AesonRecord { unAesonRecord :: a }
 
 instance HasJSONOptions (AesonRecord a) where
@@ -71,6 +80,7 @@
 instance (HasJSONOptions (AesonRecord a), Generic a, GFromJSON Zero (Rep a)) => FromJSON (AesonRecord a) where
   parseJSON = (AesonRecord <$>) . genericParseJSON (getJSONOptions (Proxy :: Proxy (AesonRecord a)))
 
+-- | Generic deriving ToJSON/FromJSON via this yields an encoding equivalent to the wrapped type.
 newtype AesonNewtype n o = AesonNewtype { unAesonNewtype :: n }
 
 instance HasJSONOptions (AesonNewtype n o) where
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main
+  ( main
+  ) where
+
+import AesonVia (AesonNewtype (..), AesonRecord (..), AesonTag (..), HasTagPrefix (..))
+import Control.Newtype.Generics (Newtype)
+import Data.Aeson (FromJSON, ToJSON, encode)
+import qualified Data.Text.Lazy.Encoding as TLE
+import GHC.Generics (Generic)
+import Test.Tasty (TestTree, defaultMain, testGroup)
+import Test.Tasty.HUnit (testCase, (@?=))
+
+newtype Author = Author
+  { _authorWrapped :: String
+  } deriving (Eq, Show, Generic)
+    deriving (ToJSON, FromJSON) via (AesonNewtype Author String)
+
+instance Newtype Author
+
+data Category =
+    CategoryNews
+  | CategoryOpinion
+  | CategoryLies
+  deriving (Eq, Show, Generic)
+  deriving (ToJSON, FromJSON) via (AesonTag Category)
+
+instance HasTagPrefix Category where
+  getTagPrefix _ = "Category"
+
+data Article = Article
+  { _articleTitle :: String
+  , _articleAuthor :: Author
+  , _articleCategory :: Category
+  } deriving (Eq, Show, Generic)
+    deriving (ToJSON, FromJSON) via (AesonRecord Article)
+
+testSomething :: TestTree
+testSomething = testCase "something" $ do
+  let article = Article "Throw the ball!" (Author "Dog") CategoryOpinion
+      expected = "{\"title\":\"Throw the ball!\",\"author\":\"Dog\",\"category\":\"opinion\"}"
+      actual = TLE.decodeUtf8 (encode article)
+  actual @?= expected
+
+main :: IO ()
+main = defaultMain $ testGroup "AesonVia"
+  [ testSomething
+  ]
