Close Menu

    Subscribe to Updates

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

    What's Hot

    Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display

    Gigabyte Gaming A16 GA63H

    Metroid Prime 4: Beyond release date leaked and it’s sooner than expected

    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

      Blue-collar jobs are gaining popularity as AI threatens office work

      August 17, 2025

      Man who asked ChatGPT about cutting out salt from his diet was hospitalized with hallucinations

      August 15, 2025

      What happens when chatbots shape your reality? Concerns are growing online

      August 14, 2025

      Scientists want to prevent AI from going rogue by teaching it to be bad first

      August 8, 2025

      AI models may be accidentally (and secretly) learning each other’s bad behaviors

      July 30, 2025
    • Business

      Why Certified VMware Pros Are Driving the Future of IT

      August 24, 2025

      Murky Panda hackers exploit cloud trust to hack downstream customers

      August 23, 2025

      The rise of sovereign clouds: no data portability, no party

      August 20, 2025

      Israel is reportedly storing millions of Palestinian phone calls on Microsoft servers

      August 6, 2025

      AI site Perplexity uses “stealth tactics” to flout no-crawl edicts, Cloudflare says

      August 5, 2025
    • Crypto

      Former Indian Politician Convicted in Bitcoin Extortion Case

      August 30, 2025

      Top 3 Real World Asset (RWA) Altcoins to Watch in September

      August 30, 2025

      Ethereum Dip May Be Temporary with $1 Billion Whale Buys and Slower Profit Taking

      August 30, 2025

      Everything We Know So Far About the Bitcoin Thriller “Killing Satoshi”

      August 30, 2025

      Why HBAR’s Bearish Sentiment Might Be Its Trigger for a Price Rebound

      August 30, 2025
    • Technology

      Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display

      August 30, 2025

      Gigabyte Gaming A16 GA63H

      August 30, 2025

      Metroid Prime 4: Beyond release date leaked and it’s sooner than expected

      August 30, 2025

      New Casio Edifice EFRS108DE stainless-steel watches with textured dials now purchasable in the US with limited stock

      August 30, 2025

      Seven new IKEA smart home products with Matter on the way

      August 30, 2025
    • 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

    BMI Calculator – Check your Body Mass Index for free!

    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


    BMI Calculator – Check your Body Mass Index for free!

    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

    Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display

    August 30, 2025

    Gigabyte Gaming A16 GA63H

    August 30, 2025

    Metroid Prime 4: Beyond release date leaked and it’s sooner than expected

    August 30, 2025
    Leave A Reply Cancel Reply

    Top Posts

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

    April 22, 2025167 Views

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

    April 14, 202548 Views

    New Akira ransomware decryptor cracks encryptions keys using GPUs

    March 16, 202530 Views

    Is Libby Compatible With Kobo E-Readers?

    March 31, 202528 Views
    Don't Miss
    Technology August 30, 2025

    Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display

    Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display -…

    Gigabyte Gaming A16 GA63H

    Metroid Prime 4: Beyond release date leaked and it’s sooner than expected

    New Casio Edifice EFRS108DE stainless-steel watches with textured dials now purchasable in the US with limited stock

    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

    Nothing Phone (3) smartphone review: Top-class hardware combined with unrivaled design and secondary display

    August 30, 20250 Views

    Gigabyte Gaming A16 GA63H

    August 30, 20252 Views

    Metroid Prime 4: Beyond release date leaked and it’s sooner than expected

    August 30, 20252 Views
    Most Popular

    Xiaomi 15 Ultra Officially Launched in China, Malaysia launch to follow after global event

    March 12, 20250 Views

    Apple thinks people won’t use MagSafe on iPhone 16e

    March 12, 20250 Views

    French Apex Legends voice cast refuses contracts over “unacceptable” AI clause

    March 12, 20250 Views
    © 2025 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.