diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
-## [_Unreleased_](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.1...main)
+## [_Unreleased_](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.2...main)
 
 None
+
+## [v1.0.2.2](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.1...v1.0.2.2)
+
+- Add `file` and `line` attributes in `testcase` nodes
 
 ## [v1.0.2.1](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.0...v1.0.2.1)
 
diff --git a/hspec-junit-formatter.cabal b/hspec-junit-formatter.cabal
--- a/hspec-junit-formatter.cabal
+++ b/hspec-junit-formatter.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               hspec-junit-formatter
-version:            1.0.2.1
+version:            1.0.2.2
 license:            MIT
 license-file:       LICENSE
 copyright:          2021 Renaissance Learning Inc
diff --git a/library/Test/Hspec/JUnit.hs b/library/Test/Hspec/JUnit.hs
--- a/library/Test/Hspec/JUnit.hs
+++ b/library/Test/Hspec/JUnit.hs
@@ -8,6 +8,7 @@
 
 import Prelude
 
+import Control.Applicative ((<|>))
 import Data.Conduit (runConduitRes, (.|))
 import Data.Conduit.Combinators (sinkFile)
 import Data.Conduit.List (sourceList)
@@ -79,7 +80,9 @@
 itemToTestCase
   :: (FilePath -> FilePath) -> Text -> Text -> Item -> Schema.TestCase
 itemToTestCase applyPrefix group name item = Schema.TestCase
-  { testCaseClassName = group
+  { testCaseLocation =
+    toSchemaLocation <$> (itemResultLocation item <|> itemLocation item)
+  , testCaseClassName = group
   , testCaseName = name
   , testCaseDuration = unSeconds $ itemDuration item
   , testCaseResult = case itemResult item of
@@ -123,6 +126,18 @@
   prefixInfo str
     | T.null $ T.strip $ pack $ itemInfo item = str
     | otherwise = pack (itemInfo item) <> "\n\n" <> str
+
+itemResultLocation :: Item -> Maybe Location
+itemResultLocation item = case itemResult item of
+  Success -> Nothing
+  Pending mLocation _ -> mLocation
+  Failure mLocation _ -> mLocation
+
+toSchemaLocation :: Location -> Schema.Location
+toSchemaLocation Location {..} = Schema.Location
+  { Schema.locationFile = locationFile
+  , Schema.locationLine = fromIntegral $ max 0 locationLine
+  }
 
 unSeconds :: Seconds -> Double
 unSeconds (Seconds x) = x
diff --git a/library/Test/Hspec/JUnit/Render.hs b/library/Test/Hspec/JUnit/Render.hs
--- a/library/Test/Hspec/JUnit/Render.hs
+++ b/library/Test/Hspec/JUnit/Render.hs
@@ -11,7 +11,8 @@
 import Data.Text (Text, pack)
 import Data.Time.Format.ISO8601 (iso8601Show)
 import Data.XML.Types (Event)
-import Test.Hspec.JUnit.Schema (Result(..), Suite(..), Suites(..), TestCase(..))
+import Test.Hspec.JUnit.Schema
+  (Location(..), Result(..), Suite(..), Suites(..), TestCase(..))
 import Text.Printf
 import Text.XML.Stream.Render (attr, content, tag)
 
@@ -55,15 +56,18 @@
 tshow = pack . show
 
 testCase :: MonadThrow m => ConduitT TestCase Event m ()
-testCase = awaitForever $ \(TestCase className name duration mResult) ->
-  tag "testcase" (attributes className name duration)
-    $ traverse_ yield mResult
-    .| result
+testCase =
+  awaitForever $ \(TestCase mLocation className name duration mResult) ->
+    tag "testcase" (attributes mLocation className name duration)
+      $ traverse_ yield mResult
+      .| result
  where
-  attributes className name duration =
-    attr "name" name <> attr "classname" className <> attr
-      "time"
-      (roundToStr duration)
+  attributes mLocation className name duration =
+    maybe mempty (attr "file" . pack . locationFile) mLocation
+      <> maybe mempty (attr "line" . pack . show . locationLine) mLocation
+      <> attr "name" name
+      <> attr "classname" className
+      <> attr "time" (roundToStr duration)
 
 result :: MonadThrow m => ConduitT Result Event m ()
 result = awaitForever go
diff --git a/library/Test/Hspec/JUnit/Schema.hs b/library/Test/Hspec/JUnit/Schema.hs
--- a/library/Test/Hspec/JUnit/Schema.hs
+++ b/library/Test/Hspec/JUnit/Schema.hs
@@ -2,6 +2,7 @@
   ( Suites(..)
   , Suite(..)
   , TestCase(..)
+  , Location(..)
   , Result(..)
   ) where
 
@@ -9,6 +10,7 @@
 
 import Data.Text (Text)
 import Data.Time (UTCTime)
+import Numeric.Natural
 
 data Suites = Suites
   { suitesName :: Text
@@ -24,10 +26,17 @@
   deriving stock Show
 
 data TestCase = TestCase
-  { testCaseClassName :: Text
+  { testCaseLocation :: Maybe Location
+  , testCaseClassName :: Text
   , testCaseName :: Text
   , testCaseDuration :: Double
   , testCaseResult :: Maybe Result
+  }
+  deriving stock Show
+
+data Location = Location
+  { locationFile :: FilePath
+  , locationLine :: Natural
   }
   deriving stock Show
 
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -6,6 +6,7 @@
 import Prelude
 
 import Control.Monad (void)
+import Data.Char (isSpace)
 import qualified Data.Map.Strict as Map
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -23,7 +24,7 @@
     it "matches golden file" $ do
       withJUnitReport ExampleSpec.spec $ \doc -> do
         golden <- XML.readFile XML.def "tests/golden.xml"
-        removeTimeAttributes doc `shouldBe` removeTimeAttributes golden
+        normalizeDoc doc `shouldBe` normalizeDoc golden
 
     it "can prefix source paths" $ do
       let modify = setJUnitConfigSourcePathPrefix "lol/monorepo"
@@ -52,6 +53,23 @@
       hspecConfig = configWithJUnit junitConfig defaultConfig
     void $ runSpec spec hspecConfig
     f =<< XML.readFile XML.def path
+
+normalizeDoc :: XML.Document -> XML.Document
+normalizeDoc = removeWhitespaceNodes . removeTimeAttributes
+
+removeWhitespaceNodes :: XML.Document -> XML.Document
+removeWhitespaceNodes doc = doc
+  { XML.documentRoot = go $ XML.documentRoot doc
+  }
+ where
+  go el =
+    el { XML.elementNodes = concatMap filterWhitespace $ XML.elementNodes el }
+
+  filterWhitespace :: XML.Node -> [XML.Node]
+  filterWhitespace = \case
+    XML.NodeElement el -> [XML.NodeElement $ go el]
+    XML.NodeContent c | T.all isSpace c -> []
+    n -> [n]
 
 -- | Remove volatile attributes so they don't invalidate comparison
 removeTimeAttributes :: XML.Document -> XML.Document
diff --git a/tests/golden.xml b/tests/golden.xml
--- a/tests/golden.xml
+++ b/tests/golden.xml
@@ -1,6 +1,61 @@
-<testsuites name="hspec-junit-format"><testsuite name="Some section" package="Some section" id="0" time="0.000032355" timestamp="2021-05-17T15:36:10.205608409Z" hostname="localhost" tests="2" failures="1" errors="0" skipped="0"><properties/><testcase name="has a failure" classname="Some section" time="0.000015537"><failure type="error">tests/ExampleSpec.hs:18:7
+<testsuites name="hspec-junit-format">
+  <testsuite
+    name="Some section"
+    package="Some section"
+    id="0"
+    hostname="localhost"
+    time="0.000032355"
+    timestamp="2021-05-17T15:36:10.205608409Z"
+    tests="2" failures="1" errors="0" skipped="0">
+    <properties/>
+    <testcase
+      file="tests/ExampleSpec.hs"
+      line="18"
+      name="has a failure"
+      classname="Some section"
+      time="0.000015537">
+      <failure type="error">tests/ExampleSpec.hs:18:7
 
 expected: &#34;False&#34;
  but got: &#34;True&#34;
-</failure></testcase><testcase name="has an expectation" classname="Some section" time="0.000016818"/></testsuite><testsuite name="Some section/A grouped context" package="Some section/A grouped context" id="1" time="0.000021009" timestamp="2021-05-17T15:36:10.205608409Z" hostname="localhost" tests="3" failures="0" errors="0" skipped="1"><properties/><testcase name="gets skipped" classname="Some section/A grouped context" time="0.000006765"><skipped>tests/ExampleSpec.hs:26:9
-some reason</skipped></testcase><testcase name="also happens in a group" classname="Some section/A grouped context" time="0.000007421"/><testcase name="happens in a group" classname="Some section/A grouped context" time="0.000006823"/></testsuite></testsuites>
+</failure>
+    </testcase>
+    <testcase
+      file="tests/ExampleSpec.hs"
+      line="15"
+      name="has an expectation"
+      classname="Some section"
+      time="0.000016818"/>
+  </testsuite>
+  <testsuite
+    name="Some section/A grouped context"
+    package="Some section/A grouped context"
+    id="1"
+    time="0.000021009"
+    timestamp="2021-05-17T15:36:10.205608409Z"
+    hostname="localhost"
+    tests="3" failures="0" errors="0" skipped="1">
+    <properties/>
+    <testcase
+      file="tests/ExampleSpec.hs"
+      line="26"
+      name="gets skipped"
+      classname="Some section/A grouped context"
+      time="0.000006765">
+      <skipped>tests/ExampleSpec.hs:26:9
+some reason</skipped>
+    </testcase>
+    <testcase
+      file="tests/ExampleSpec.hs"
+      line="23"
+      name="also happens in a group"
+      classname="Some section/A grouped context"
+      time="0.000007421"/>
+    <testcase
+      file="tests/ExampleSpec.hs"
+      line="21"
+      name="happens in a group"
+      classname="Some section/A grouped context"
+      time="0.000006823"/>
+  </testsuite>
+</testsuites>
