diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -56,3 +56,7 @@
 
 * Updated copyright dates.
 * Tweaked documentation.
+
+## 0.3.1.0  -- 2025-06-19
+
+* Added a `Group` constructor and a corresponding `group` function for creating a group of tests.
diff --git a/dwergaz.cabal b/dwergaz.cabal
--- a/dwergaz.cabal
+++ b/dwergaz.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.4
 name:                dwergaz
-version:             0.3.0.2
+version:             0.3.1.0
 synopsis:            A minimal testing library
 description:         dwergaz is a minimal testing library.
 license:             ISC
@@ -12,7 +12,7 @@
 category:            Testing
 build-type:          Simple
 extra-doc-files:     ChangeLog.md
-tested-with:         GHC ==9.4.8 || ==9.6.6 || ==9.8.4 || ==9.10.1 || ==9.12.1
+tested-with:         GHC ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.2 || ==9.12.2
 
 source-repository head
   type:     git
diff --git a/src/Test/Dwergaz.hs b/src/Test/Dwergaz.hs
--- a/src/Test/Dwergaz.hs
+++ b/src/Test/Dwergaz.hs
@@ -12,18 +12,20 @@
 --
 -- See the <https://github.com/henrytill/dwergaz/blob/master/test/Main.hs tests> for a usage example.
 module Test.Dwergaz
-  ( Test (..),
-    assertFailure,
-    assertBool,
-    assertEqual,
-    Result,
-    resultToString,
-    resultIsPassed,
-    runTest,
+  ( Test (..)
+  , assertFailure
+  , assertBool
+  , assertEqual
+  , group
+  , Result
+  , resultToString
+  , resultIsPassed
+  , runTest
   )
 where
 
 import Text.PrettyPrint
+import Prelude hiding ((<>))
 
 data Test
   = forall a b.
@@ -42,6 +44,11 @@
       String
       -- | Condition
       Bool
+  | Group
+      -- | Group name
+      String
+      -- | Tests
+      [Test]
 
 assertFailure ::
   -- | Test description
@@ -68,26 +75,41 @@
   Test
 assertEqual desc = Expect desc (==)
 
+group ::
+  -- | Group name
+  String ->
+  -- | Tests
+  [Test] ->
+  Test
+group = Group
+
 data Result
   = forall a b. (Show a, Show b) => FailedExpect String a b
   | Failed String
   | Passed String
+  | Multiple String [Result]
 
 prettyResult :: Result -> Doc
 prettyResult (FailedExpect n e a) =
   vcat
-    [ text "FAILED:" <+> text n,
-      nest 2 (text "EXPECTED:") <+> text (show e),
-      nest 2 (text "ACTUAL:") <+> text (show a)
+    [ text "FAILED:" <+> text n
+    , nest 2 (text "EXPECTED:") <+> text (show e)
+    , nest 2 (text "ACTUAL:") <+> text (show a)
     ]
 prettyResult (Failed n) = text "FAILED:" <+> text n
 prettyResult (Passed n) = text "PASSED:" <+> text n
+prettyResult (Multiple n rs) =
+  vcat
+    [ text n <> colon
+    , nest 2 . vcat . map prettyResult $ rs
+    ]
 
 resultToString :: Result -> String
 resultToString = render . prettyResult
 
 resultIsPassed :: Result -> Bool
 resultIsPassed (Passed _) = True
+resultIsPassed (Multiple _ rs) = all resultIsPassed rs
 resultIsPassed _ = False
 
 runTest :: Test -> Result
@@ -97,3 +119,5 @@
 runTest (Predicate n c)
   | c = Passed n
   | otherwise = Failed n
+runTest (Group n ts) =
+  Multiple n (map runTest ts)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -28,17 +28,23 @@
 
 exampleTests :: [Test]
 exampleTests =
-  [ expectExample01,
-    expectExample02,
-    predicateExample01,
-    predicateExample02,
-    predicateExample03
+  [ group
+      "Expect"
+      [ expectExample01
+      , expectExample02
+      ]
+  , group
+      "Predicate"
+      [ predicateExample01
+      , predicateExample02
+      , predicateExample03
+      ]
   ]
 
 step :: Result -> (ShowS, Bool) -> (ShowS, Bool)
 step result (f, allPassed) =
-  ( showString (resultToString result) . showChar '\n' . f,
-    resultIsPassed result && allPassed
+  ( showString (resultToString result) . showChar '\n' . f
+  , resultIsPassed result && allPassed
   )
 
 results :: (String, Bool)
