Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    AMD surges 10 percent in desktop PC share, again

    Google now lets you delete your personal info from search results

    Microsoft just forked Windows

    Facebook X (Twitter) Instagram
    • Artificial Intelligence
    • Business Technology
    • Cryptocurrency
    • Gadgets
    • Gaming
    • Health
    • Software and Apps
    • Technology
    Facebook X (Twitter) Instagram Pinterest Vimeo
    Tech AI Verse
    • Home
    • Artificial Intelligence

      Read the extended transcript: President Donald Trump interviewed by ‘NBC Nightly News’ anchor Tom Llamas

      February 6, 2026

      Stocks and bitcoin sink as investors dump software company shares

      February 4, 2026

      AI, crypto and Trump super PACs stash millions to spend on the midterms

      February 2, 2026

      To avoid accusations of AI cheating, college students are turning to AI

      January 29, 2026

      ChatGPT can embrace authoritarian ideas after just one prompt, researchers say

      January 24, 2026
    • Business

      The HDD brand that brought you the 1.8-inch, 2.5-inch, and 3.5-inch hard drives is now back with a $19 pocket-sized personal cloud for your smartphones

      February 12, 2026

      New VoidLink malware framework targets Linux cloud servers

      January 14, 2026

      Nvidia Rubin’s rack-scale encryption signals a turning point for enterprise AI security

      January 13, 2026

      How KPMG is redefining the future of SAP consulting on a global scale

      January 10, 2026

      Top 10 cloud computing stories of 2025

      December 22, 2025
    • Crypto

      Berachain Jumps 150% as Strategic Pivot Lifts BERA

      February 12, 2026

      Tom Lee’s BitMine (BMNR) Stock Faces Cost-Basis Risk — Price Breakdown at 10%?

      February 12, 2026

      Why the US Jobs Data Makes a Worrying Case for Bitcoin

      February 12, 2026

      MYX Falls Below $5 as Short Sellers Take Control — 42% Decline Risk Emerges

      February 12, 2026

      Solana Pins Its $75 Support on Short-Term Buyers — Can Price Survive This Risky Setup?

      February 12, 2026
    • Technology

      AMD surges 10 percent in desktop PC share, again

      February 12, 2026

      Google now lets you delete your personal info from search results

      February 12, 2026

      Microsoft just forked Windows

      February 12, 2026

      This Lenovo USB-C dock with 65 watts of power is just $76

      February 12, 2026

      Anker’s USB-C hub turns 1 laptop port into 8 ports (best ever price)

      February 12, 2026
    • Others
      • Gadgets
      • Gaming
      • Health
      • Software and Apps
    Check BMI
    Tech AI Verse
    You are at:Home»Technology»Golden Literal Testing in UTest
    Technology

    Golden Literal Testing in UTest

    TechAiVerseBy TechAiVerseAugust 1, 2025No Comments10 Mins Read2 Views
    Facebook Twitter Pinterest Telegram LinkedIn Tumblr Email Reddit
    Golden Literal Testing in UTest
    Share
    Facebook Twitter LinkedIn Pinterest WhatsApp Email

    Golden Literal Testing in UTest

    uTest is a small unit testing library I maintain that aims for simplicity and convenience. This blog post explores the Golden testing feature newly-added in uTest 0.9.0: why it is necessary, what it does, and how it works internally. This feature was inspired by the Jane Street blog post What if writing tests was a joyful experience.


    About the Author: Haoyi is a software engineer, and the author of many open-source Scala tools such as the Ammonite REPL and the Mill Build Tool. If you enjoyed the contents on this blog, you may also enjoy Haoyi’s book Hands-on Scala Programming


    The Motivation for Golden Tests

    Golden testing, also called Snapshot testing, compares the output of your code against some pre-defined “Golden” value. What differentiates golden tests from normal unit tests is that the golden values are often relatively large, and instead of being written and maintained by hand they are generated and updated automatically by the testing framework. For example, you may want to check that the logs of a particular workflow “look right”, and you would like to check that they do not change unexpectedly, but you don’t want to spend time typing out an entire log file by hand!

    For example, consider the FullRunLogsTests in the Mill build tool. These tests run some simple commands and assert against the shape of the output logs, with the dual goal of ensuring all the “expected” logging is present, and no unwanted debug messages appear in the output. Traditionally, they would be written something like this:

    val res = eval("run", "--text", "hello")
    
    val normalized = normalize(res)
    assert(
      normalized ==
      List(
        "============================== run  text hello ==============================",
        "[build.mill-/] compile",
        "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
        "[build.mill-] [info] done compiling",
        "[/] compile",
        "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
        "[] [info] done compiling",
        "[/] run",
        "[/] ============================== run  text hello ============================== s"
      )
    )
    

    In this case the value we’re asserting is a List of Strings, so putting the expected output in a file is possible. But asserting against other kids of literal data structure is also common: primitives, Tuples, Lists, Maps, case classes, or any combination of these nested within each other. These may be less convenient to move to a separate file, and keeping them in-line in your test code also helps avoid indirection forcing you to jump around from file-to-file just to figure out what your test is doing.

    While it is possible to manage these tests by hand, it can be quite tedious. When setting them up, typically a user would first use pprint.log from the PPrint library to print out the value:

    val res = eval("run", "--text", "hello")
    
    val normalized = normalize(res)
    pprint.log(normalized)
    

    Unlike normal Java .toString, PPrint is optimized for outputting well-formatted output that can be copy-pasted into your code: Lists are split across multiple lines and indented, Strings are indented, etc. The pprint.log call above would output:

    FullRunLogsTests.scala:19 normalized: List(
      "============================== run  text hello ==============================",
      "[build.mill-/] compile",
      "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
      "[build.mill-] [info] done compiling",
      "[/] compile",
      "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
      "[] [info] done compiling",
      "[/] run",
      "[/] ============================== run  text hello ============================== s"
    )
    

    And the user would then copy-paste it into the test code to use in the assert.

    However, it is not just setup that can be tedious: maintaining these tests as the behavior of your system evolves over time is tedious as well. For example, maybe we decide to replace the square [...]s with parentheses (...). That would cause the test to fail:

    utest.AssertionError: normalized == ...
    normalized: List[String] = List(
      "============================== run  text hello ==============================",
      "(build.mill-/) compile",
      "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
      "[build.mill-] [info] done compiling",
      "(/) compile",
      "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
      "[] [info] done compiling",
      "(/) run",
      "[/] ============================== run  text hello ============================== s"
    )
    normalized != ...:
      List(
        "============================== run  text hello ==============================",
    -   "(build.mill-/) compile",
    +   "[build.mill-/] compile",
        "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
        "[build.mill-] [info] done compiling",
    -   "(/) compile",
    +   "[/] compile",
        "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
        "[] [info] done compiling",
    -   "(/) run",
    +   "[/] run",
        "[/] ============================== run  text hello ============================== s"
      )
    

    And the user would have to copy-paste the new normalized value into their assertion to make the test pass. Again, this isn’t rocket science, but it can be very tedious: updating a large test suite with lots of tests to comply with updated output isn’t fun, and isn’t a good use of a 4 year university computer-science degree and decades of industry experience.

    Setting Up uTest Golden Literal Testing

    uTest 0.9.0 ships with a new assertGoldenLiteral method. To set this up the first time, you can call it with the runtime value on the left, and a dummy value () on the right:

    val res = eval("run", "--text", "hello")
    
    val normalized = normalize(res)
    assertGoldenLiteral(
      normalized,
      ()
    )
    

    Running this test produces the following assertion error:

    If you then run the test again with UTEST_UPDATE_GOLDEN_TESTS=1, you will see that uTest has recognized the mismatch, and gone and updated your FullRunLogsTests.scala source file on your behalf!

    + mill.integration.FullRunLogsTests.ticker 8970ms  
    UTEST_UPDATE_GOLDEN_TESTS detected, uTest applying 1 golden fixes to file /Users/lihaoyi/Github/mill/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    Updating line:column 46:8 to 46:10
    Tests: 1, Passed: 1, Failed: 0
    
    $ git diff
    diff --git a/integration/feature/full-run-logs/src/FullRunLogsTests.scala b/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    index 654ac4ac0f7..9c80dafd0e7 100644
    --- a/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    +++ b/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    @@ -44,7 +44,17 @@ object FullRunLogsTests extends UtestIntegrationTestSuite {
           val normalized = normalize(res)
           assertGoldenLiteral(
             normalized,
    -        ()
    +        List(
    +          "============================== run  text hello ==============================",
    +          "[build.mill-/] compile",
    +          "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
    +          "[build.mill-] [info] done compiling",
    +          "[/] compile",
    +          "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
    +          "[] [info] done compiling",
    +          "[/] run",
    +          "[/] ============================== run  text hello ============================== s"
    +        )
           )
         }
         test("keepGoingFailure") - integrationTest { tester =>
    

    If you run the test again now, you will find it passes given the new literal that has been spliced in by UTEST_UPDATE_GOLDEN_TESTS=1.

    In fact, you do not need to set up your asserts one at a time: you can write an entire test suite with multiple assertGoldenLiteral calls, leave them all stubbed out with (), and run the test with UTEST_UPDATE_GOLDEN_TESTS=1 to fill them in all at once!

    Maintaining Golden Literal Tests

    Earlier we mentioned that apart from setting up tests, keeping them up-to-date is also tedious. With uTest’s golden literal tests, if you then make a behavioral change like substituting the square brackets [...] with round parentheses (...), assertGoldenLiteral is able to recognize the difference and highlight the lines and characters that differ:

    And if run again with UTEST_UPDATE_GOLDEN_TESTS=1, uTest fix it on your behalf in the source code, updating the data structure in your test to the new value:

    + mill.integration.FullRunLogsTests.ticker 8527ms  
    UTEST_UPDATE_GOLDEN_TESTS detected, uTest applying 1 golden fixes to file /Users/lihaoyi/Github/mill/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    Updating line:column 46:8 to 56:9
    Tests: 1, Passed: 1, Failed: 0
    
    $ git diff
    diff --git a/integration/feature/full-run-logs/src/FullRunLogsTests.scala b/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    index 9c80dafd0e7..3698f8980a9 100644
    --- a/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    +++ b/integration/feature/full-run-logs/src/FullRunLogsTests.scala
    @@ -46,13 +46,13 @@ object FullRunLogsTests extends UtestIntegrationTestSuite {
             normalized,
             List(
               "============================== run  text hello ==============================",
    -          "[build.mill-/] compile",
    +          "(build.mill-/) compile",
               "[build.mill-] [info] compiling  Scala sources to .../out/mill-build/compile.dest/classes ...",
               "[build.mill-] [info] done compiling",
    -          "[/] compile",
    +          "(/) compile",
               "[] [info] compiling  Java source to .../out/compile.dest/classes ...",
               "[] [info] done compiling",
    -          "[/] run",
    +          "(/) run",
               "[/] ============================== run  text hello ============================== s"
             )
           )
    

    Effectively, for the subset of simple asserts that assertGoldenLiteral is suitable for, it greatly reduces the busy-work of writing and maintaining a test suite. Rather than tediously writing out the expected output yourself, and endlessly tweaking it as the behavior evolves, you can instead just ask uTest to update the expected output on your behalf and it will update your source code appropriately!

    Implementation Details

    How uTest’s assertGoldenLiteral works is itself interesting, and worth a mention. It has the following signature:

    def assertGoldenLiteral(actualValue: Any, goldenLiteral: GoldenFix.Span[Any])
                           (implicit reporter: GoldenFix.Reporter): Unit
    

    assertGoldenLiteral works with Anys; Scala (and it’s underlying Java or Javascript runtimes) relies on “Universal Equality” where any value can be compared to any other value, and uTest does not make any innovations in that regard. The actualValue: Any isn’t particularly interesting, but the right-hand value goldenLiteral: GoldenFix.Span[Any] is where the magic happens. GoldenFix.Span is similar to the sourcecode.Text type from the com-lihaoyi/sourcecode library, and is defined as:

    class Span[+T](value: T, sourceFile: String, startOffset: Int, endOffset: Int)
    

    Where sourcecode.Text captures just the textual contents of the expression, GoldenFix.Span captures the raw source file path and start/end offsets within it. Like sourcecode.Text, GoldenFix.Span is typically constructed from any value via an implicit macro conversion to turn the Any into a GoldenFix.Span[Any]. For example, when you write

    assertGoldenLiteral(
      normalized,
      ()
    )
    

    () is not a GoldenFix.Span, and so the implicit conversion GoldenFix.Span.generate expands that into:

    assertGoldenLiteral(
      normalized,
      GoldenFix.Span.generate(())
    )
    

    generate is a macro, which then expands into

    assertGoldenLiteral(
      normalized,
      GoldenFix.Span(
         (), 
         sourceFile = "integration/feature/full-run-logs/src/FullRunLogsTests.scala", 
         startOffset = 1546, 
         endOffset = 1548
      )
    )
    

    This way, assertGoldenLiteral is able to capture exactly where the goldenLiteral: GoldenFix.Span[Any] expression comes from in the source code on disk. We now have all the information we need to find the original source file, find where the goldenValue expression is inside of it, and replace it with our new value when necessary.

    To make use of the GoldenFix.Span, assertGoldenLiteral is defined as:

    def assertGoldenLiteral(actualValue: Any, goldenLiteral: GoldenFix.Span[Any])
                           (implicit reporter: GoldenFix.Reporter): Unit = {
       
      if (actualValue != goldenLiteral.value) {
        if (!sys.env.contains("UTEST_UPDATE_GOLDEN_TESTS") {
          throwAssertionError(goldenLiteral.sourceFile, goldenLiteral.value, actualValue)
        } else {
          reporter.apply(actualValue, goldenLiteral)
        }
      }
    }
    

    When the two values actualValue and goldenLiteral.value are not equal and UTEST_UPDATE_GOLDEN_TESTS is not given, we throw an assertion error as normal. But when UTEST_UPDATE_GOLDEN_TESTS is given, we do not throw and instead simply pass the actualValue and goldenLiteral to the implicit reporter: GoldenFix.Reporter that is passed automatically from the test suite. This reporter then knows:

    • Which assertGoldenLiteral calls failed
    • What file each one was in and exactly where in that file the goldenLiteral is from
    • What the actualValue was

    uTest can then pretty-print the actual value using the PPrint library, indent it appropriately based on the indentation of the original expression, and splice it into the source code where the original expression came from. Because PPrint is designed to provide source-equivalent and well-formatted output for the given expression, the updated code can now be compiled and run with that assertGoldenLiteral check passing!

    Conclusion

    assertGoldenLiteral only works for asserting equality with “literals”. These are values that can be pretty-printed using the PPrint library, typically primitives, collections, and case classes. It cannot totally replace all existing usages of assert, assertThrows, assertCompileError, etc.

    However, in the cases where assertGoldenLiteral (or its sister-method assertGoldenFile) can be applied, it works surprisingly well, cutting down on the busy-work involved in keeping your test suite up-to-date, allowing you to spend less time on busy-work and more time on the problem at hand.

    The reason assertGoldenLiteral works so well is that it is basically what users were doing by hand anyway:

    • Users were already running tests and seeing them fail
    • Users were already using pprint.log to print out a copy-paste-able version of the thing they want to assert against
    • Users were already splicing in the PPrint output into the source code where the original literal was, and fixing up the indentation as necessary.

    Lots of testing frameworks have some equivalent to uTest’s assertGoldenFile, which can automatically update a file on disk when it detects a mismatch. But uTest 0.9.0’s new assertGoldenLiteral takes that one step further, and is able to over-write data literals in your source code when it detects a mismatch. That is something that is pretty uncommon among testing frameworks across all languages, and I hope some of you will try it out in the new version of uTest!


    About the Author: Haoyi is a software engineer, and the author of many open-source Scala tools such as the Ammonite REPL and the Mill Build Tool. If you enjoyed the contents on this blog, you may also enjoy Haoyi’s book Hands-on Scala Programming


    Share. Facebook Twitter Pinterest LinkedIn Reddit WhatsApp Telegram Email
    Previous ArticleBritish man claims he’s unable to watch porn as tattoos confuse age check system
    Next Article Meta prepares for gigawatt datacentres to power ‘superintelligence’
    TechAiVerse
    • Website

    Jonathan is a tech enthusiast and the mind behind Tech AI Verse. With a passion for artificial intelligence, consumer tech, and emerging innovations, he deliver clear, insightful content to keep readers informed. From cutting-edge gadgets to AI advancements and cryptocurrency trends, Jonathan breaks down complex topics to make technology accessible to all.

    Related Posts

    AMD surges 10 percent in desktop PC share, again

    February 12, 2026

    Google now lets you delete your personal info from search results

    February 12, 2026

    Microsoft just forked Windows

    February 12, 2026
    Leave A Reply Cancel Reply

    Top Posts

    Ping, You’ve Got Whale: AI detection system alerts ships of whales in their path

    April 22, 2025667 Views

    Lumo vs. Duck AI: Which AI is Better for Your Privacy?

    July 31, 2025255 Views

    6.7 Cummins Lifter Failure: What Years Are Affected (And Possible Fixes)

    April 14, 2025152 Views

    6 Best MagSafe Phone Grips (2025), Tested and Reviewed

    April 6, 2025111 Views
    Don't Miss
    Technology February 12, 2026

    AMD surges 10 percent in desktop PC share, again

    AMD surges 10 percent in desktop PC share, again Image: Adam Patrick Murray / Foundry…

    Google now lets you delete your personal info from search results

    Microsoft just forked Windows

    This Lenovo USB-C dock with 65 watts of power is just $76

    Stay In Touch
    • Facebook
    • Twitter
    • Pinterest
    • Instagram
    • YouTube
    • Vimeo

    Subscribe to Updates

    Get the latest creative news from SmartMag about art & design.

    About Us
    About Us

    Welcome to Tech AI Verse, your go-to destination for everything technology! We bring you the latest news, trends, and insights from the ever-evolving world of tech. Our coverage spans across global technology industry updates, artificial intelligence advancements, machine learning ethics, and automation innovations. Stay connected with us as we explore the limitless possibilities of technology!

    Facebook X (Twitter) Pinterest YouTube WhatsApp
    Our Picks

    AMD surges 10 percent in desktop PC share, again

    February 12, 20263 Views

    Google now lets you delete your personal info from search results

    February 12, 20263 Views

    Microsoft just forked Windows

    February 12, 20263 Views
    Most Popular

    7 Best Kids Bikes (2025): Mountain, Balance, Pedal, Coaster

    March 13, 20250 Views

    VTOMAN FlashSpeed 1500: Plenty Of Power For All Your Gear

    March 13, 20250 Views

    This new Roomba finally solves the big problem I have with robot vacuums

    March 13, 20250 Views
    © 2026 TechAiVerse. Designed by Divya Tech.
    • Home
    • About Us
    • Contact Us
    • Privacy Policy
    • Terms & Conditions

    Type above and press Enter to search. Press Esc to cancel.