With no side effects, negating the need for mocks/spies, you'd think Elm would make unit testing easier. It does, but...
"Is number within range inclusive" is hard.
"Is discount rate formatted right" is harder. What does "right" mean? It looks right to me, but what do I know about annual percentage rate, and the constraints sales teams vs. product teams adjust them? Huge industry + our business specific knowledge there.
Even as a veteran Elm user, I'm still elated when the compiler says "We're good!" because we probably are. Same feelings 10 years ago with TypeScript, and years before that with ActionScript 2 and 3.
Keyword "probably", however, belies the HARD bugs.
The joy and being spoiled by the compiler still doesn't mean I don't have to put in the work. The true work of good Test Driven Development, or at the very least, Test After Dev of testing the logic as we know it right now.
We heavily rely on Cypress for "does the app work?"
However, if we find a bug saying "MDR is 0.001751" vs "MDR is 0.1751", fixing it so the test passes is good, no doubt, but not great. What's better is test the functions that are deriving this heavily important financial number on the UI.
Yes, Cypress confirms in true Behavior Driven Development fashion that the application is now behaving once we fix the broken test, or improve or add a new test to find & fix the hidden bug.
However, it doesn't improve our understanding nor prevent related bugs.
Most important of all, though, is we're skipping the design step. Design meaning "is this API good?". The exploration joy many get in dynamic languages quickly playing with code, you can get that feeling designing with types and functions in Elm.
That, however, can lead to "cool, the code works". It's made a worse bias because the tools are so much better, ensuring the code actually works. Despite your exploration tools being better, you're left with the same problems with skipping test first development: good design. Can you arrive at a good design? Absolutely! Intentional trumps happenstance, tho.
I've really struggled to do TDD in Elm because the compiler and designing with the type system is so good. I constantly have to remind myself when I get horrible bugs like this that the good tools do NOT replace the best practices, and I can't skimp on other important work.
People have all kinds of opinions about how organizations develop a distrust between engineering & product when engineering breaks things, or can't release confidently over time and the engineering culture rots from that point forward.
My opinion is because testing is hard, there are many facets to it, and you NEED to practice to get better.
When you spend 90% of your time writing tests for basic language things that "go away", then suddenly Elm is like "hey, bruh... you're free of that crap!" it's easy to rest on your laurels with such power.
Logic testing is hard. Yet easier now. Get to practicing!
Better agile practices, better management tools, better design/product approaches... sure. Those are good. From my experience, it's both developers learning to be better testers as well as incorporating that into a fast CICD process themselves to continually increase confidence and build trust.
In my experience developers just struggle to write good tests to know the code they release to prod is good because testing is hard, and defining "right" is harder still. There are tons of blogs, tutorials, and books out there on testing, yet it remains hard to glean basic tactics and strategies on how to go about it.
If I were to say what one thing has improved my entire career the most over time, yet I've had to work on my entire career beyond just the obvious of "continuous learning", it's testing.
Like all things non-programming in my life that I learn like skating, rollerblading, parkour, power lifting, bodybuilding, and lately riding motorcycles... you have to practice. A lot.
It may suck, but it's worth it.