diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.0.1...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.2.0...main)
+
+## [v1.23.2.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.1.0...freckle-app-v1.23.2.0)
+
+- Add to `Freckle.App.Test`: `expectJust`, `expectRight`, and `expect`
 
 ## [v1.23.1.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.23.0.1...freckle-app-v1.23.1.0)
 
diff --git a/freckle-app.cabal b/freckle-app.cabal
--- a/freckle-app.cabal
+++ b/freckle-app.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.22
 name:               freckle-app
-version:            1.23.1.0
+version:            1.23.2.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
@@ -110,7 +110,7 @@
         doctest >=0.20.1,
         exceptions >=0.10.4,
         faktory >=1.1.2.4,
-        freckle-env >=0.0.1.1,
+        freckle-env >=0.0.1.2,
         freckle-exception >=0.0.0.0,
         freckle-http >=0.1.0.0,
         freckle-otel >=0.0.0.2,
diff --git a/library/Freckle/App/Test.hs b/library/Freckle/App/Test.hs
--- a/library/Freckle/App/Test.hs
+++ b/library/Freckle/App/Test.hs
@@ -11,6 +11,9 @@
   , expectationFailure
   , pending
   , pendingWith
+  , expectJust
+  , expectRight
+  , expect
 
     -- * Re-exports
   , module X
@@ -161,3 +164,32 @@
 
 pendingWith :: MonadIO m => String -> m ()
 pendingWith msg = liftIO $ Hspec.pendingWith msg
+
+-- | Unwraps 'Just', or throws an assertion failure if the 'Maybe' is 'Nothing'
+--
+-- Like @(\``shouldSatisfy`\` 'isJust')@, but returns the 'Just' value.
+expectJust :: (HasCallStack, MonadIO m) => Maybe a -> m a
+expectJust = \case
+  Nothing -> expectationFailure "expected Just, but got Nothing"
+  Just a -> pure a
+
+-- | Unwraps 'Right', or throws an assertion failure if the 'Either' is 'Left'
+--
+-- Like @(\``shouldSatisfy`\` 'isRight')@, but returns the 'Right' value.
+expectRight :: (HasCallStack, MonadIO m, Show a) => Either a b -> m b
+expectRight = \case
+  Left a -> expectationFailure $ "expected Right, but got " <> show a
+  Right b -> pure b
+
+-- | Applies an @a -> Maybe b@ function, then either unwraps a 'Just' result
+--   or throws an assertion failure if the result is 'Nothing'
+--
+-- @'expect' f@ is like @(\``shouldSatisfy`\` f)@, but where @f@ is returns 'Maybe'
+-- rather than 'Bool', and a successful result is returned for use in subsequent
+-- assertions.
+--
+-- This can be useful with @lens@, for example: @b <- expect (preview _Left) a@
+expect :: (HasCallStack, MonadIO m, Show a) => (a -> Maybe b) -> a -> m b
+expect f a = case f a of
+  Nothing -> expectationFailure $ "predicate failed on: " <> show a
+  Just b -> pure b
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.23.1.0
+version: 1.23.2.0
 
 maintainer: Freckle Education
 category: Utils
