diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## v0.3.3
+
+Configuration:
+* Add `-optF=main:_` option for configuring the name of the main module/function ([#64](https://github.com/brandonchinn178/skeletest/issues/64))
+
+API changes:
+* Added `shouldReturn`
+
+Runtime changes:
+* Fix gray text output, using standard ANSI codes instead of a hardcoded RGB value
+
 ## v0.3.2
 
 API changes:
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -88,6 +88,8 @@
       build-tool-depends: skeletest:skeletest-preprocessor
     ```
 
+    Options may be specified as `-optF=<name>:<value>`. See [Options](#options).
+
 1. Add `Main.hs`:
 
     ```haskell
@@ -190,6 +192,7 @@
 * `shouldNotSatisfy` - equivalent to `shouldSatisfy` with `P.not`
 * `shouldBe` - equivalent to `shouldSatisfy` with `P.eq`
 * `shouldNotBe` - equivalent to `shouldNotSatisfy` with `P.eq`
+* `shouldReturn` - equivalent to `shouldSatisfy` with `P.returns . P.eq`
 
 `shouldSatisfy` is the most general function, but the others are provided for convenience. `shouldSatisfy` takes in the value being tested on the left, and a predicate on the right. Predicates should be imported from `Skeletest.Predicate`, qualified as `P`.
 
@@ -427,6 +430,12 @@
     ```haskell
     MyFlag flagVal <- getFlag
     ```
+
+### Options
+
+Skeletest accepts the following options, which may be added as `-optF=<name>:<value>`:
+
+* `main`: Specify the main module/function, as either `OtherMain.otherMainFunc`, `OtherMain` (equivalent to `OtherMain.main`), or `otherMainFunc` (equivalent to `Main.otherMainFunc`). Generally only useful with the `-main-is` GHC option.
 
 ### Hooks
 
diff --git a/skeletest.cabal b/skeletest.cabal
--- a/skeletest.cabal
+++ b/skeletest.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: skeletest
-version: 0.3.2
+version: 0.3.3
 synopsis: Batteries-included, opinionated test framework
 description: Batteries-included, opinionated test framework. See README.md for more details.
 homepage: https://github.com/brandonchinn178/skeletest#readme
@@ -81,7 +81,6 @@
     , aeson
     , aeson-pretty
     , ansi-terminal >= 0.7.0
-    , colour
     , containers
     , Diff >= 1.0
     , directory
diff --git a/src/Skeletest.hs b/src/Skeletest.hs
--- a/src/Skeletest.hs
+++ b/src/Skeletest.hs
@@ -20,6 +20,7 @@
   shouldNotBe,
   shouldSatisfy,
   shouldNotSatisfy,
+  shouldReturn,
   context,
   failTest,
   HasCallStack,
diff --git a/src/Skeletest/Assertions.hs b/src/Skeletest/Assertions.hs
--- a/src/Skeletest/Assertions.hs
+++ b/src/Skeletest/Assertions.hs
@@ -7,6 +7,7 @@
   shouldNotBe,
   shouldSatisfy,
   shouldNotSatisfy,
+  shouldReturn,
   context,
   failTest,
   AssertionFail (..),
@@ -42,7 +43,7 @@
   context = contextIO
   throwFailure = throwIO
 
-infix 1 `shouldBe`, `shouldNotBe`, `shouldSatisfy`, `shouldNotSatisfy`
+infix 1 `shouldBe`, `shouldNotBe`, `shouldSatisfy`, `shouldNotSatisfy`, `shouldReturn`
 
 -- | Assert that the given input should match the given value.
 -- Equivalent to @actual `shouldSatisfy` P.eq expected@
@@ -65,6 +66,12 @@
 -- | Assert that the given input should not satisfy the given predicate.
 shouldNotSatisfy :: (HasCallStack, Testable m) => a -> Predicate m a -> m ()
 actual `shouldNotSatisfy` p = GHC.withFrozenCallStack $ actual `shouldSatisfy` P.not p
+
+-- | Assert that the given input should return the given value.
+-- Equivalent to @actual `shouldSatisfy` (P.returns . P.eq) expected@
+-- @since 0.3.3
+shouldReturn :: (HasCallStack, Testable m, Eq a) => m a -> a -> m ()
+actual `shouldReturn` expected = GHC.withFrozenCallStack $ actual `shouldSatisfy` (P.returns . P.eq) expected
 
 contextIO :: String -> IO a -> IO a
 contextIO msg =
diff --git a/src/Skeletest/Internal/GHC.hs b/src/Skeletest/Internal/GHC.hs
--- a/src/Skeletest/Internal/GHC.hs
+++ b/src/Skeletest/Internal/GHC.hs
@@ -102,8 +102,8 @@
 -- | Our pure definition of PluginDef, agnostic of GHC version.
 data PluginDef = PluginDef
   { isPure :: Bool
-  , modifyParsed :: ModuleName -> ParsedModule -> ParsedModule
-  , onRename :: Ctx -> ModuleName -> HsExpr GhcRn -> HsExpr GhcRn
+  , modifyParsed :: [GHC.CommandLineOption] -> ModuleName -> ParsedModule -> ParsedModule
+  , onRename :: [GHC.CommandLineOption] -> Ctx -> ModuleName -> HsExpr GhcRn -> HsExpr GhcRn
   }
 
 data Ctx = Ctx
@@ -116,11 +116,11 @@
 mkPlugin PluginDef{..} =
   GHC.defaultPlugin
     { GHC.pluginRecompile = if isPure then GHC.purePlugin else GHC.impurePlugin
-    , GHC.parsedResultAction = \_ modInfo result -> do
+    , GHC.parsedResultAction = \opts modInfo result -> do
         let
           moduleName = getModuleName $ GHC.ms_mod modInfo
           parsedModule = initParsedModule . GHC.hpm_module . GHC.parsedResultModule $ result
-          ParsedModule{moduleFuncs} = modifyParsed moduleName parsedModule
+          ParsedModule{moduleFuncs} = modifyParsed opts moduleName parsedModule
         newDecls <-
           runCompilePs . fmap concat . sequence $
             [ compileFunDef funName funDef
@@ -129,7 +129,7 @@
         pure
           . (modifyParsedResultModule . modifyHpmModule . fmap . modifyModDecls) (newDecls <>)
           $ result
-    , GHC.renamedResultAction = \_ gblEnv group -> do
+    , GHC.renamedResultAction = \opts gblEnv group -> do
         nameCache <- GHC.hsc_NC . GHC.env_top <$> GHC.getEnv
         let
           moduleName = getModuleName $ GHC.tcg_mod gblEnv
@@ -137,7 +137,7 @@
             Ctx
               { matchesName = matchesNameImpl nameCache
               }
-        group' <- runCompileRn $ modifyModuleExprs (onRename ctx moduleName) group
+        group' <- runCompileRn $ modifyModuleExprs (onRename opts ctx moduleName) group
         pure (gblEnv, group')
     }
  where
diff --git a/src/Skeletest/Internal/Plugin.hs b/src/Skeletest/Internal/Plugin.hs
--- a/src/Skeletest/Internal/Plugin.hs
+++ b/src/Skeletest/Internal/Plugin.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskellQuotes #-}
 {-# LANGUAGE ViewPatterns #-}
@@ -15,6 +16,7 @@
 import Skeletest.Internal.Error (skeletestPluginError)
 import Skeletest.Internal.GHC
 import Skeletest.Internal.Predicate qualified as P
+import Skeletest.Internal.Preprocessor qualified as Preprocessor
 import Skeletest.Internal.Utils.HList (HList (..))
 import Skeletest.Main qualified as Main
 import Skeletest.Plugin qualified as Plugin
@@ -30,19 +32,28 @@
   mkPlugin
     PluginDef
       { isPure = True
-      , modifyParsed = \modName modl ->
-          if modName == "Main"
-            then transformMainModule modl
-            else modl
-      , onRename = \ctx modName expr ->
+      , modifyParsed = \opts modName modl ->
+          let options = decodeOptions opts
+           in if modName == options.mainModuleName
+                then transformMainModule options modl
+                else modl
+      , onRename = \_ ctx modName expr ->
           if "Spec" `Text.isSuffixOf` modName
             then transformTestModule ctx expr
             else expr
       }
+ where
+  decodeOptions =
+    either (error . Text.unpack) id . \case
+      [opts] -> Preprocessor.decodeOptions (Text.pack opts)
+      _ -> Left ""
 
 -- | Add 'main' function.
-transformMainModule :: ParsedModule -> ParsedModule
-transformMainModule modl = modl{moduleFuncs = (hsVarName "main", Just mainFun) : moduleFuncs modl}
+transformMainModule :: Preprocessor.Options -> ParsedModule -> ParsedModule
+transformMainModule options modl =
+  modl
+    { moduleFuncs = (hsVarName options.mainFuncName, Just mainFun) : moduleFuncs modl
+    }
  where
   findVar name =
     fmap hsExprVar . listToMaybe $
diff --git a/src/Skeletest/Internal/Preprocessor.hs b/src/Skeletest/Internal/Preprocessor.hs
--- a/src/Skeletest/Internal/Preprocessor.hs
+++ b/src/Skeletest/Internal/Preprocessor.hs
@@ -1,7 +1,12 @@
+{-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NoFieldSelectors #-}
 
 module Skeletest.Internal.Preprocessor (
   processFile,
+  Options (..),
+  defaultOptions,
+  decodeOptions,
 ) where
 
 import Control.Monad (guard)
@@ -14,12 +19,44 @@
 import Skeletest.Internal.Error (SkeletestError (..))
 import System.Directory (doesDirectoryExist, listDirectory)
 import System.FilePath (makeRelative, splitExtensions, takeDirectory, (</>))
+import Text.Read (readMaybe)
 import UnliftIO.Exception (throwIO)
 
+data Options = Options
+  { mainModuleName :: Text
+  , mainFuncName :: Text
+  }
+  deriving (Show, Read)
+
+defaultOptions :: Options
+defaultOptions =
+  Options
+    { mainModuleName = "Main"
+    , mainFuncName = "main"
+    }
+
+encodeOptions :: Options -> Text
+encodeOptions = Text.pack . show
+
+decodeOptions :: Text -> Either Text Options
+decodeOptions =
+  maybe (Left "Could not decode skeletest-preprocessor options") Right
+    . readMaybe
+    . Text.unpack
+    . unquote
+ where
+  unquote s =
+    case Text.stripPrefix "\"" s >>= Text.stripSuffix "\"" of
+      Just s' -> Text.replace "\\\"" "\"" s'
+      Nothing -> s
+
 -- | Preprocess the given Haskell file. See Main.hs
-processFile :: FilePath -> Text -> IO Text
-processFile path file = do
-  file' <- if isMain file then updateMainFile path file else pure file
+processFile :: Options -> FilePath -> Text -> IO Text
+processFile options path file = do
+  file' <-
+    if getModuleName file == options.mainModuleName
+      then updateMainFile path file
+      else pure file
   pure
     . addLine pluginPragma
     . addLine linePragma
@@ -28,22 +65,30 @@
   addLine line f = line <> "\n" <> f
   quoted s = "\"" <> s <> "\""
 
-  pluginPragma = "{-# OPTIONS_GHC -fplugin=Skeletest.Internal.Plugin #-}"
+  pluginMod = "Skeletest.Internal.Plugin"
+  quote s = "\"" <> Text.replace "\"" "\\\"" s <> "\""
+  pluginPragma =
+    Text.unwords
+      [ "{-# OPTIONS_GHC"
+      , "-fplugin=" <> pluginMod
+      , "-fplugin-opt=" <> pluginMod <> ":" <> (quote . encodeOptions) options
+      , "#-}"
+      ]
   linePragma =
     -- this is needed to tell GHC to use original path in error messages
     "{-# LINE 1 " <> quoted (Text.pack path) <> " #-}"
 
-isMain :: Text -> Bool
-isMain file =
-  case mapMaybe getModuleName $ Text.lines file of
+getModuleName :: Text -> Text
+getModuleName file =
+  case mapMaybe parseModuleLine $ Text.lines file of
     -- there was a module line
-    [name] -> name == "Main"
-    -- there were no module lines, it's the main module
-    [] -> True
+    [name] -> name
+    -- there were no module lines, it's the Main module
+    [] -> "Main"
     -- something else? just silently ignore it
-    _ -> False
+    _ -> ""
  where
-  getModuleName s =
+  parseModuleLine s =
     case Text.words s of
       "module" : name : _ -> Just name
       _ -> Nothing
diff --git a/src/Skeletest/Internal/Utils/Color.hs b/src/Skeletest/Internal/Utils/Color.hs
--- a/src/Skeletest/Internal/Utils/Color.hs
+++ b/src/Skeletest/Internal/Utils/Color.hs
@@ -5,7 +5,6 @@
   gray,
 ) where
 
-import Data.Colour.Names qualified as Color
 import Data.Text (Text)
 import Data.Text qualified as Text
 import System.Console.ANSI qualified as ANSI
@@ -23,4 +22,4 @@
 yellow = withANSI [ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Yellow]
 
 gray :: Text -> Text
-gray = withANSI [ANSI.SetRGBColor ANSI.Foreground Color.gray]
+gray = withANSI [ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Black]
diff --git a/src/Skeletest/Plugin.hs b/src/Skeletest/Plugin.hs
--- a/src/Skeletest/Plugin.hs
+++ b/src/Skeletest/Plugin.hs
@@ -68,6 +68,7 @@
 data Hooks = Hooks
   { hookModifyFileSpecs :: [SpecTree] -> IO [SpecTree]
   -- ^ Modify the specs in a file
+  -- @since 0.3.2
   , hookRunTest :: TestInfo -> IO TestResult -> IO TestResult
   -- ^ Modify how a test is executed
   }
diff --git a/src/bin/skeletest-preprocessor.hs b/src/bin/skeletest-preprocessor.hs
--- a/src/bin/skeletest-preprocessor.hs
+++ b/src/bin/skeletest-preprocessor.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 {-| A preprocessor that registers skeletest in a test suite.
 
@@ -19,6 +20,8 @@
 -}
 module Main where
 
+import Data.Char (isUpper)
+import Data.Foldable (foldlM)
 import Data.List (dropWhileEnd)
 import Data.Text.IO qualified as Text
 import GHC.IO.Encoding (setLocaleEncoding, utf8)
@@ -27,8 +30,10 @@
 import System.IO (hPutStrLn, stderr)
 import UnliftIO.Exception (displayException, handle)
 
+import Data.Text qualified as Text
 import Skeletest.Internal.Error (SkeletestError)
 import Skeletest.Internal.Preprocessor (processFile)
+import Skeletest.Internal.Preprocessor qualified as Preprocessor
 
 main :: IO ()
 main = handleErrors $ do
@@ -37,8 +42,10 @@
 
   getArgs >>= \case
     -- https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/phases.html#options-affecting-a-haskell-pre-processor
-    [fp, input, output] -> Text.readFile input >>= processFile fp >>= Text.writeFile output
-    _ -> error "The skeletest preprocessor does not accept any additional arguments."
+    fp : input : output : args -> do
+      options <- either error pure $ foldlM parseOpts Preprocessor.defaultOptions args
+      Text.readFile input >>= processFile options fp >>= Text.writeFile output
+    _ -> error "The skeletest preprocessor expects at least three arguments."
 
 -- | Output SkeletestError
 handleErrors :: IO a -> IO a
@@ -49,3 +56,24 @@
   normalizeLines
     | __GLASGOW_HASKELL__ == (908 :: Int) = dropWhileEnd (== '\n')
     | otherwise = id
+
+parseOpts :: Preprocessor.Options -> String -> Either String Preprocessor.Options
+parseOpts opts argStr
+  | Just (mMainMod, mMainFunc) <- Text.stripPrefix "main:" arg >>= parseMain =
+      Right
+        . maybe id (\s o -> o{Preprocessor.mainModuleName = s}) mMainMod
+        . maybe id (\s o -> o{Preprocessor.mainFuncName = s}) mMainFunc
+        $ opts
+  | otherwise = Left $ "Unknown option: " <> argStr
+ where
+  arg = Text.pack argStr
+  parseMain s =
+    case Text.splitOn "." s of
+      [modName, modFunc] -> Just (Just modName, Just modFunc)
+      [_] -> do
+        (c, _) <- Text.uncons s
+        Just $
+          if isUpper c
+            then (Just s, Nothing)
+            else (Nothing, Just s)
+      _ -> Nothing
diff --git a/test/Skeletest/AssertionsSpec.hs b/test/Skeletest/AssertionsSpec.hs
--- a/test/Skeletest/AssertionsSpec.hs
+++ b/test/Skeletest/AssertionsSpec.hs
@@ -86,6 +86,24 @@
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
+  describe "shouldReturn" $ do
+    it "should pass" $
+      pure 1 `shouldReturn` (1 :: Int)
+
+    integration . it "should show helpful failure" $ do
+      runner <- getFixture
+      addTestFile runner "ExampleSpec.hs" $
+        [ "module ExampleSpec (spec) where"
+        , ""
+        , "import Skeletest"
+        , ""
+        , "spec = it \"should fail\" $ pure 1 `shouldReturn` (2 :: Int)"
+        ]
+
+      (stdout, stderr) <- expectFailure $ runTests runner []
+      stderr `shouldBe` ""
+      stdout `shouldSatisfy` P.matchesSnapshot
+
   describe "context" $ do
     integration . it "should show failure context" $ do
       runner <- getFixture
diff --git a/test/Skeletest/__snapshots__/AssertionsSpec.snap.md b/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
--- a/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
+++ b/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
@@ -85,6 +85,20 @@
 ╰───────────────────────────────────────────────────────────────────────────────
 ```
 
+## shouldReturn / should show helpful failure
+
+```
+./ExampleSpec.hs
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:5:
+│ │
+│ │ spec = it "should fail" $ pure 1 `shouldReturn` (2 :: Int)
+│ │                                  ^^^^^^^^^^^^^^
+│ 
+│ 1 ≠ 2
+╰───────────────────────────────────────────────────────────────────────────────
+```
+
 ## shouldSatisfy / should show helpful failure
 
 ```
