diff --git a/hspec-core.cabal b/hspec-core.cabal
--- a/hspec-core.cabal
+++ b/hspec-core.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:             hspec-core
-version:          2.7.5
+version:          2.7.6
 license:          MIT
 license-file:     LICENSE
 copyright:        (c) 2011-2021 Simon Hengel,
diff --git a/src/Test/Hspec/Core/Hooks.hs b/src/Test/Hspec/Core/Hooks.hs
--- a/src/Test/Hspec/Core/Hooks.hs
+++ b/src/Test/Hspec/Core/Hooks.hs
@@ -5,6 +5,7 @@
 , beforeWith
 , beforeAll
 , beforeAll_
+, beforeAllWith
 , after
 , after_
 , afterAll
@@ -44,6 +45,12 @@
 beforeAll_ action spec = do
   mvar <- runIO (newMVar Empty)
   before_ (memoize mvar action) spec
+
+-- | Run a custom action with an argument before the first spec item.
+beforeAllWith :: (b -> IO a) -> SpecWith a -> SpecWith b
+beforeAllWith action spec = do
+  mvar <- runIO (newMVar Empty)
+  beforeWith (memoize mvar . action) spec
 
 data Memoized a =
     Empty
diff --git a/src/Test/Hspec/Core/Runner.hs b/src/Test/Hspec/Core/Runner.hs
--- a/src/Test/Hspec/Core/Runner.hs
+++ b/src/Test/Hspec/Core/Runner.hs
@@ -57,9 +57,6 @@
 
 import           Test.Hspec.Core.Runner.Eval
 
--- | Filter specs by given predicate.
---
--- The predicate takes a list of "describe" labels and a "requirement".
 filterSpecs :: Config -> [EvalTree] -> [EvalTree]
 filterSpecs c = go []
   where
@@ -81,19 +78,16 @@
       Node group specs -> goSpecs (groups ++ [group]) specs (Node group)
       NodeWithCleanup action specs -> goSpecs groups specs (NodeWithCleanup action)
 
-applyDryRun :: Config -> [SpecTree ()] -> [SpecTree ()]
+applyDryRun :: Config -> [EvalTree] -> [EvalTree]
 applyDryRun c
-  | configDryRun c = map (removeCleanup . fmap markSuccess)
+  | configDryRun c = bimapForest removeCleanup markSuccess
   | otherwise = id
   where
-    markSuccess :: Item () -> Item ()
-    markSuccess item = item {itemExample = safeEvaluateExample (Result "" Success)}
+    removeCleanup :: IO () -> IO ()
+    removeCleanup _ = return ()
 
-    removeCleanup :: SpecTree () -> SpecTree ()
-    removeCleanup spec = case spec of
-      Node x xs -> Node x (map removeCleanup xs)
-      NodeWithCleanup _ xs -> NodeWithCleanup (\() -> return ()) (map removeCleanup xs)
-      leaf@(Leaf _) -> leaf
+    markSuccess :: EvalItem -> EvalItem
+    markSuccess item = item {evalItemAction = \ _ -> return $ Result "" Success}
 
 -- | Run a given spec and write a report to `stdout`.
 -- Exit with `exitFailure` if at least one spec item fails.
@@ -227,7 +221,7 @@
         | configRandomize config = randomizeForest seed
         | otherwise = id
 
-    filteredSpec <- randomize . filterSpecs config . mapMaybe (toEvalTree params) . applyDryRun config <$> runSpecM focusedSpec
+    filteredSpec <- randomize . filterSpecs config . applyDryRun config . toEvalForest params <$> runSpecM focusedSpec
 
     (total, failures) <- withHiddenCursor useColor h $ do
       let
@@ -249,15 +243,14 @@
     dumpFailureReport config seed qcArgs failures
     return (Summary total (length failures))
 
-toEvalTree :: Params -> SpecTree () -> Maybe EvalTree
-toEvalTree params = go
+toEvalForest :: Params -> [SpecTree ()] -> [EvalTree]
+toEvalForest params = bimapForest withUnit toEvalItem . filterForest itemIsFocused
   where
-    go :: Tree (() -> c) (Item ()) -> Maybe (Tree c EvalItem)
-    go t = case t of
-      Node s xs -> Just $ Node s (mapMaybe go xs)
-      NodeWithCleanup c xs -> Just $ NodeWithCleanup (c ()) (mapMaybe go xs)
-      Leaf (Item requirement loc isParallelizable isFocused e) ->
-        guard isFocused >> return (Leaf (EvalItem requirement loc (fromMaybe False isParallelizable) (e params $ ($ ()))))
+    toEvalItem :: Item () -> EvalItem
+    toEvalItem (Item requirement loc isParallelizable _isFocused e) = EvalItem requirement loc (fromMaybe False isParallelizable) (e params withUnit)
+
+    withUnit :: ActionWith () -> IO ()
+    withUnit action = action ()
 
 dumpFailureReport :: Config -> Integer -> QC.Args -> [Path] -> IO ()
 dumpFailureReport config seed qcArgs xs = do
diff --git a/src/Test/Hspec/Core/Spec.hs b/src/Test/Hspec/Core/Spec.hs
--- a/src/Test/Hspec/Core/Spec.hs
+++ b/src/Test/Hspec/Core/Spec.hs
@@ -108,7 +108,7 @@
   let
     ys
       | any (any itemIsFocused) xs = xs
-      | otherwise = map (bimapTree id (\ item -> item {itemIsFocused = True})) xs
+      | otherwise = bimapForest id (\ item -> item {itemIsFocused = True}) xs
   fromSpecList ys
 
 -- | @fit@ is an alias for @fmap focus . it@
diff --git a/src/Test/Hspec/Core/Tree.hs b/src/Test/Hspec/Core/Tree.hs
--- a/src/Test/Hspec/Core/Tree.hs
+++ b/src/Test/Hspec/Core/Tree.hs
@@ -13,6 +13,9 @@
 , specGroup
 , specItem
 , bimapTree
+, bimapForest
+, filterTree
+, filterForest
 , location
 ) where
 
@@ -35,6 +38,9 @@
 -- over the type of cleanup actions and the type of the actual spec items.
 type SpecTree a = Tree (ActionWith a) (Item a)
 
+bimapForest :: (a -> b) -> (c -> d) -> [Tree a c] -> [Tree b d]
+bimapForest g f = map (bimapTree g f)
+
 bimapTree :: (a -> b) -> (c -> d) -> Tree a c -> Tree b d
 bimapTree g f = go
   where
@@ -42,6 +48,15 @@
       Node d xs -> Node d (map go xs)
       NodeWithCleanup cleanup xs -> NodeWithCleanup (g cleanup) (map go xs)
       Leaf item -> Leaf (f item)
+
+filterForest :: (a -> Bool) -> [Tree c a] -> [Tree c a]
+filterForest = mapMaybe . filterTree
+
+filterTree :: (a -> Bool) -> Tree c a -> Maybe (Tree c a)
+filterTree p t = case t of
+  Node s xs -> Just $ Node s (filterForest p xs)
+  NodeWithCleanup c xs -> Just $ NodeWithCleanup c (filterForest p xs)
+  leaf@(Leaf a) -> guard (p a) >> return leaf
 
 -- |
 -- @Item@ is used to represent spec items internally.  A spec item consists of:
diff --git a/test/Helper.hs b/test/Helper.hs
--- a/test/Helper.hs
+++ b/test/Helper.hs
@@ -68,7 +68,7 @@
   (==) = exceptionEq
 
 throwException :: IO ()
-throwException = E.throwIO (E.ErrorCall "foobar")
+throwException = E.throwIO DivideByZero
 
 ignoreExitCode :: IO () -> IO ()
 ignoreExitCode action = action `E.catch` \e -> let _ = e :: ExitCode in return ()
diff --git a/test/Test/Hspec/Core/HooksSpec.hs b/test/Test/Hspec/Core/HooksSpec.hs
--- a/test/Test/Hspec/Core/HooksSpec.hs
+++ b/test/Test/Hspec/Core/HooksSpec.hs
@@ -155,6 +155,66 @@
           , "bar"
           ]
 
+  describe "beforeAllWith" $ do
+    it "transforms the spec argument" $ do
+      (rec, retrieve) <- mkAppend
+      let action :: Int -> IO String
+          action = return . show
+      runSilent $ H.beforeAll (return 23) $ H.beforeAllWith action $ do
+        H.it "foo" $ \value -> rec value
+      retrieve `shouldReturn` ["23"]
+
+    it "can be used multiple times" $ do
+      let action1 :: Int -> IO Int
+          action1 = return . succ
+
+          action2 :: Int -> IO String
+          action2 = return . show
+
+          action3 :: String -> IO String
+          action3 = return . ("foo " ++)
+
+      (rec, retrieve) <- mkAppend
+
+      runSilent $ H.beforeAll (return 23) $
+        H.beforeAllWith action1 $ H.beforeAllWith action2 $ H.beforeAllWith action3 $ do
+          H.it "foo" $ \value -> rec value
+
+      retrieve `shouldReturn` ["foo 24"]
+
+    it "runs an action before the first spec item" $ do
+      (rec, retrieve) <- mkAppend
+      runSilent $ H.beforeAll (return (23 :: Int)) $
+        H.beforeAllWith (\value -> rec "beforeAllWith" >> return (show value)) $ do
+          H.it "foo" $ \value -> do
+            rec $ "foo " ++ value
+          H.it "bar" $ \value -> do
+            rec $ "bar " ++ value
+      retrieve `shouldReturn` [
+          "beforeAllWith"
+        , "foo 23"
+        , "bar 23"
+        ]
+
+    context "when specified action throws an exception" $ do
+      it "sets subsequent spec items to pending" $ do
+        result <- silence . H.hspecResult $
+          H.beforeAll (return (23 :: Int)) $
+            H.beforeAllWith (\_ -> throwIO (ErrorCall "foo")) $ do
+              H.it "foo" $ \n -> do
+                n `shouldBe` (23 :: Int)
+              H.it "bar" $ \n -> do
+                n `shouldBe` 23
+        result `shouldBe` H.Summary {H.summaryExamples = 2, H.summaryFailures = 1}
+
+    context "when used with an empty list of examples" $ do
+      it "does not run specified action" $ do
+        (rec, retrieve) <- mkAppend
+        runSilent $ H.beforeAll (return (23 :: Int)) $
+          H.beforeAllWith (\_ -> rec "beforeAllWith" >> return "value") $ do
+            return ()
+        retrieve `shouldReturn` []
+
   describe "after" $ do
     it "runs an action after every spec item" $ do
       (rec, retrieve) <- mkAppend
diff --git a/test/Test/Hspec/Core/UtilSpec.hs b/test/Test/Hspec/Core/UtilSpec.hs
--- a/test/Test/Hspec/Core/UtilSpec.hs
+++ b/test/Test/Hspec/Core/UtilSpec.hs
@@ -49,7 +49,7 @@
 
     it "returns Left on exception" $ do
       Left e <- safeTry throwException
-      show e `shouldBe` "foobar"
+      E.fromException e `shouldBe` Just E.DivideByZero
 
     it "evaluates result to weak head normal form" $ do
       Left e <- safeTry (return $ E.throw $ E.ErrorCall "foo")
diff --git a/version.yaml b/version.yaml
--- a/version.yaml
+++ b/version.yaml
@@ -1,1 +1,1 @@
-&version 2.7.5
+&version 2.7.6
