<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rust Testing Masterclass on Atharva Pandey</title><link>https://atharva.page/series/rust-testing-masterclass/</link><description>Recent content in Rust Testing Masterclass on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Sun, 08 Sep 2024 11:10:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/rust-testing-masterclass/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 12: Test Architecture — When to unit, integration, or e2e</title><link>https://atharva.page/post/rust/rust-test-architecture/</link><pubDate>Sun, 08 Sep 2024 11:10:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-architecture/</guid><description>&lt;p&gt;I inherited a Rust project with 800 tests. Running them took 45 minutes. I dug in and found: 600 unit tests that mocked every dependency (most tested nothing meaningful), 180 integration tests that duplicated what the unit tests already covered, and 20 end-to-end tests that were flaky because they hit a staging server. The project had &lt;em&gt;more tests&lt;/em&gt; than any codebase I&amp;rsquo;d seen — and also &lt;em&gt;more bugs&lt;/em&gt;. The volume was high, but the strategy was garbage.&lt;/p&gt;</description></item><item><title>Lesson 11: Testing in CI — GitHub Actions for Rust</title><link>https://atharva.page/post/rust/rust-test-ci/</link><pubDate>Thu, 05 Sep 2024 16:45:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-ci/</guid><description>&lt;p&gt;I merged a PR last year that passed all tests on my M2 MacBook and broke on the Linux CI runner. The issue? I&amp;rsquo;d used &lt;code&gt;std::path::PathBuf&lt;/code&gt; with hardcoded forward slashes, which worked fine on macOS but blew up on Linux because the test setup expected a specific path format. CI exists to catch exactly this kind of &amp;ldquo;it works on my machine&amp;rdquo; problem. Here&amp;rsquo;s how to set it up properly for Rust.&lt;/p&gt;</description></item><item><title>Lesson 10: Benchmarking with criterion — Measure, don't guess</title><link>https://atharva.page/post/rust/rust-test-benchmarks/</link><pubDate>Mon, 02 Sep 2024 13:20:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-benchmarks/</guid><description>&lt;p&gt;I once optimized a hot loop by replacing a &lt;code&gt;HashMap&lt;/code&gt; lookup with a &lt;code&gt;Vec&lt;/code&gt; indexed by a precomputed key. Felt clever. Ran the benchmarks. The &amp;ldquo;optimized&amp;rdquo; version was 15% &lt;em&gt;slower&lt;/em&gt; because the &lt;code&gt;Vec&lt;/code&gt; was huge, cache-cold, and the access pattern was random. Without the benchmark, I would&amp;rsquo;ve shipped that &amp;ldquo;improvement&amp;rdquo; and bragged about it. Performance intuition is unreliable. Measurement isn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Rust is fast by default, but that doesn&amp;rsquo;t mean your code is fast. You can still write O(n^2) algorithms, cause cache thrashing, allocate unnecessarily, or clone data you could borrow. Worse, optimizations that seem obvious can backfire in ways you don&amp;rsquo;t expect.&lt;/p&gt;</description></item><item><title>Lesson 9: Code Coverage — tarpaulin and llvm-cov</title><link>https://atharva.page/post/rust/rust-test-coverage/</link><pubDate>Fri, 30 Aug 2024 07:55:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-coverage/</guid><description>&lt;p&gt;I worked on a team that had a strict &amp;ldquo;90% code coverage&amp;rdquo; policy. Every PR had to hit that number or it got rejected. The result? People wrote tests like &lt;code&gt;assert!(true)&lt;/code&gt; just to touch lines. We had 92% coverage and bugs everywhere. Coverage is a useful signal. It&amp;rsquo;s a terrible goal.&lt;/p&gt;
&lt;p&gt;That said, knowing &lt;em&gt;which&lt;/em&gt; lines your tests don&amp;rsquo;t touch is genuinely valuable. It tells you where your blind spots are. Here&amp;rsquo;s how to measure it in Rust.&lt;/p&gt;</description></item><item><title>Lesson 8: Snapshot Testing with insta — Catch regressions instantly</title><link>https://atharva.page/post/rust/rust-test-snapshot/</link><pubDate>Tue, 27 Aug 2024 10:30:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-snapshot/</guid><description>&lt;p&gt;I refactored a code formatter once — changed how it handles indentation for nested blocks. I had assertions checking specific outputs for five test cases, and they all passed. But the formatter produced subtly different output for a sixth pattern I hadn&amp;rsquo;t tested, and a user filed a bug three days later. If I&amp;rsquo;d had snapshot tests, I would&amp;rsquo;ve seen every output change in a diff during the refactor. Five minutes of review instead of three days of embarrassment.&lt;/p&gt;</description></item><item><title>Lesson 7: Fuzz Testing with cargo-fuzz — Breaking your code automatically</title><link>https://atharva.page/post/rust/rust-test-fuzzing/</link><pubDate>Sat, 24 Aug 2024 20:05:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-fuzzing/</guid><description>&lt;p&gt;A friend of mine maintains a binary parser in Rust. It had hundreds of unit tests, property tests, the works. He ran a fuzzer against it on a Friday afternoon, left it running over the weekend. Monday morning: 23 unique crashes. Some were panics from unexpected inputs. Two were infinite loops. One was a stack overflow from deeply nested structures. All from inputs no human would ever think to construct.&lt;/p&gt;</description></item><item><title>Lesson 6: Property-Based Testing with proptest — Let the computer find your bugs</title><link>https://atharva.page/post/rust/rust-test-property/</link><pubDate>Thu, 22 Aug 2024 15:40:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-property/</guid><description>&lt;p&gt;I wrote a URL parser once. Had fifty hand-crafted test cases. All green. Pushed to production. Within a week, a user sent a URL with a percent-encoded space followed by a Unicode character, and the parser crashed. I never would have thought to write that test case. A property-based test would have found it in under a second.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;When you write tests by hand, you test the cases you think of. But bugs don&amp;rsquo;t live in the cases you think of — they live in the ones you don&amp;rsquo;t. Your test for &lt;code&gt;sort([3, 1, 2])&lt;/code&gt; passes, but does your sort handle a list with ten million duplicates? A list of all negative numbers? An empty list? A list with &lt;code&gt;i32::MAX&lt;/code&gt; and &lt;code&gt;i32::MIN&lt;/code&gt; adjacent?&lt;/p&gt;</description></item><item><title>Lesson 5: Mocking — mockall and faking dependencies</title><link>https://atharva.page/post/rust/rust-test-mocking/</link><pubDate>Mon, 19 Aug 2024 08:15:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-mocking/</guid><description>&lt;p&gt;I spent an embarrassing amount of time early in my Rust journey trying to mock a database connection by hand. I built a fake struct, implemented the trait, tracked method calls with &lt;code&gt;RefCell&amp;lt;Vec&amp;lt;...&amp;gt;&amp;gt;&lt;/code&gt; wrappers, wrote expectation-checking logic&amp;hellip; and ended up with 200 lines of mock code to test 15 lines of business logic. Then I found &lt;code&gt;mockall&lt;/code&gt; and felt like an idiot.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Real code has dependencies. Your payment processor calls Stripe. Your user service queries a database. Your notification system sends emails. You can&amp;rsquo;t — and shouldn&amp;rsquo;t — hit these real services in unit tests. They&amp;rsquo;re slow, flaky, expensive, and non-deterministic.&lt;/p&gt;</description></item><item><title>Lesson 4: Test Fixtures and Setup/Teardown — Reusable test infrastructure</title><link>https://atharva.page/post/rust/rust-test-fixtures/</link><pubDate>Sat, 17 Aug 2024 11:30:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-fixtures/</guid><description>&lt;p&gt;I had a test file last year where every single test started with the same twelve lines of setup — creating a database connection, inserting seed data, configuring a logger. Twelve lines, copy-pasted forty times. When I needed to change the seed data, I had to update forty tests. I missed three. Those three tests silently tested against stale data for two months.&lt;/p&gt;
&lt;p&gt;Fixtures exist to prevent this kind of insanity.&lt;/p&gt;</description></item><item><title>Lesson 3: Doc Tests — Tested documentation</title><link>https://atharva.page/post/rust/rust-test-doc-tests/</link><pubDate>Wed, 14 Aug 2024 18:10:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-doc-tests/</guid><description>&lt;p&gt;I once read the docs for a popular Rust crate, copied the example verbatim, and it didn&amp;rsquo;t compile. The API had changed two versions ago but nobody updated the docs. I spent twenty minutes debugging code that was supposed to be the &amp;ldquo;getting started&amp;rdquo; guide. Rust has a built-in solution to this exact problem, and it&amp;rsquo;s one of the most underappreciated features in the language.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Documentation lies. Not on purpose — it lies because code evolves and docs don&amp;rsquo;t. A function signature changes, a parameter gets renamed, a return type shifts from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt;, and the example in the docstring quietly becomes fiction. No compiler warning, no test failure, just a frustrated user copy-pasting broken code.&lt;/p&gt;</description></item><item><title>Lesson 2: Integration Tests — The tests/ directory</title><link>https://atharva.page/post/rust/rust-test-integration/</link><pubDate>Mon, 12 Aug 2024 09:45:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-integration/</guid><description>&lt;p&gt;A colleague once reviewed one of my Rust libraries and said, &amp;ldquo;Your unit tests pass, but I can&amp;rsquo;t actually use the crate — the public API doesn&amp;rsquo;t compose the way anyone would expect.&amp;rdquo; He was right. I&amp;rsquo;d tested every internal function meticulously and completely ignored the experience of someone actually calling my library from the outside. That&amp;rsquo;s the gap integration tests fill.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Unit tests live inside your source files and can access private functions. That&amp;rsquo;s great for verifying internal logic, but it creates a blind spot: you never validate that your &lt;em&gt;public API&lt;/em&gt; actually makes sense. You can have perfect internal functions that combine into a terrible user experience.&lt;/p&gt;</description></item><item><title>Lesson 1: Unit Tests — #[test] and assertions</title><link>https://atharva.page/post/rust/rust-test-unit-basics/</link><pubDate>Sat, 10 Aug 2024 14:22:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-test-unit-basics/</guid><description>&lt;p&gt;I shipped a Rust library last year that had zero tests. Not because I&amp;rsquo;m lazy — I was prototyping, moving fast, &amp;ldquo;I&amp;rsquo;ll add tests later.&amp;rdquo; You know how that story ends. A one-character typo in a boundary check sat in production for three weeks before someone&amp;rsquo;s data got silently corrupted. Three weeks. I&amp;rsquo;d have caught it with one &lt;code&gt;assert_eq!&lt;/code&gt; and thirty seconds of effort.&lt;/p&gt;
&lt;p&gt;Never again. Here&amp;rsquo;s how testing actually works in Rust, from the ground up.&lt;/p&gt;</description></item></channel></rss>